diff --git a/doc/scst_pg.sgml b/doc/scst_pg.sgml index ee19cd619..e3e899bd2 100644 --- a/doc/scst_pg.sgml +++ b/doc/scst_pg.sgml @@ -39,10 +39,8 @@ required. Undertakes most problems, related to execution contexts, thus practically eliminating one of the most complicated problem in the -kernel drivers development. For example, a target driver for QLogic -22xx/23xx cards, which has all necessary features, is about 2000 -lines of code long, that is at least in several times less, than the -initiator one. +kernel drivers development. For example, target drivers for Marvell SAS +adapters or for InfiniBand SRP are less 3000 lines of code long. Performs all required pre- and post- processing of incoming requests and all necessary error recovery functionality. diff --git a/qla2x00t/Kconfig b/qla2x00t/Kconfig index e4c991ff1..d48abfe71 100644 --- a/qla2x00t/Kconfig +++ b/qla2x00t/Kconfig @@ -27,11 +27,23 @@ config SCSI_QLA_FC ftp://ftp.qlogic.com/outgoing/linux/firmware/ config SCSI_QLA2XXX_TARGET - bool "QLogic 2XXX target mode support" + bool "QLogic 2xxx target mode support" depends on SCSI_QLA_FC - default SCST + default y ---help--- - This option enables target mode hooks used by the SCST qla2x00t driver. + This option enables target mode hooks used by the SCST QLA2x00tgt driver. Once the qla2x00tgt module is loaded, target mode can be enable via a sysfs interface under scsi_host, thus enabling target mode for specific cards. + You will also need the SCST middle level drivers from http://scst.sf.net/. + +config SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE + bool "Disable initiator mode for ports with enabled target mode" + depends on SCSI_QLA2XXX_TARGET + default y + ---help--- + This option disables initiator mode for hosts for which target mode was + enabled. In other words, it disables possibility to use both target and + initiator mode simultaneously on the same host. It is needed, because + some switches don't handle well ports with both target and initiator + modes enabled. diff --git a/qla2x00t/Makefile b/qla2x00t/Makefile index 9d13c6a61..2d9fefea5 100644 --- a/qla2x00t/Makefile +++ b/qla2x00t/Makefile @@ -1,32 +1,8 @@ -ifneq ($(PATCHLEVEL),) - qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o -else -ifeq ($(KVER),) - ifeq ($(KDIR),) - KVER = $(shell uname -r) - KDIR := /lib/modules/$(KVER)/build - endif -else - KDIR := /lib/modules/$(KVER)/build -endif - -all: - $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) - -install: all - $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \ - modules_install - -uninstall: - rm -f $(INSTALL_DIR)/qla2[2-3]00.ko $(INSTALL_DIR)/qla2xxx.ko - -/sbin/depmod -a $(KVER) -endif - clean: rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend *~ Modules.symvers \ Module.symvers Module.markers modules.order @@ -34,4 +10,4 @@ clean: extraclean: clean -.PHONY: all install uninstall clean extraclean +.PHONY: clean extraclean diff --git a/qla2x00t/qla2x00-target/ChangeLog b/qla2x00t/qla2x00-target/ChangeLog index f93a5a2fd..aae7fd4f5 100644 --- a/qla2x00t/qla2x00-target/ChangeLog +++ b/qla2x00t/qla2x00-target/ChangeLog @@ -1,3 +1,12 @@ +Summary of changes between versions 1.0.2 and 2.0.0 +--------------------------------------------------- + + - Support for 24xx/25xx added + + - Disable by default initiator mode if target mode enabled. It can be changed + using .config option CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE. + + Summary of changes between versions 1.0.1 and 1.0.2 --------------------------------------------------- diff --git a/qla2x00t/qla2x00-target/Kconfig b/qla2x00t/qla2x00-target/Kconfig index 76fceca53..856ea6063 100644 --- a/qla2x00t/qla2x00-target/Kconfig +++ b/qla2x00t/qla2x00-target/Kconfig @@ -17,3 +17,14 @@ config QLA_TGT_DEBUG_WORK_IN_THREAD performance loss. If unsure, say "N". + +config QLA_TGT_DEBUG_SRR + bool "SRR debugging" + depends on SCST_QLA_TGT_ADDON + help + Turns on restransmitting packets (SRR) + debugging. In this mode some CTIOs will be "broken" to force the + initiator to issue a retransmit request. Useful for debugging and lead to big + performance loss. + + If unsure, say "N". diff --git a/qla2x00t/qla2x00-target/Makefile b/qla2x00t/qla2x00-target/Makefile index 416649e0a..b5c351d21 100644 --- a/qla2x00t/qla2x00-target/Makefile +++ b/qla2x00t/qla2x00-target/Makefile @@ -2,7 +2,9 @@ # Qlogic 2x00 SCSI target driver makefile # # Copyright (C) 2004 - 2009 Vladislav Bolkhovitin +# Copyright (C) 2004 - 2009 Vladislav Bolkhovitin # Copyright (C) 2004 - 2005 Leonid Stoljar +# Copyright (C) 2006 - 2009 ID7 Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -41,6 +43,7 @@ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS #EXTRA_CFLAGS += -DCONFIG_SCST_TRACING EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g #EXTRA_CFLAGS += -DCONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD +#EXTRA_CFLAGS += -DCONFIG_QLA_TGT_DEBUG_SRR ifeq ($(KVER),) ifeq ($(KDIR),) diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 8cdfef755..a2fa70fa3 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -1,42 +1,43 @@ -Target driver for Qlogic 22xx/23xx Fibre Channel cards -====================================================== +Target driver for Qlogic 22xx/23xx/24xx/25xx Fibre Channel cards +================================================================ -Version 1.0.2, XX XXXXX 2009 +Version 2.0.0, XX XXXXX 2009 ---------------------------- -This driver has all required features and looks to be quite stable (for -beta) and useful. It consists from two parts: the target mode driver -itself and the changed initiator driver from Linux kernel, which is, -particularly, intended to perform all the initialization and shutdown -tasks. This driver was changed to provide the target mode support and -all necessary callbacks, but it's still capable to work as initiator -only. Mode, when a host acts as the initiator and the target -simultaneously, is supported as well. +This driver consists from two parts: the target mode driver itself and +the changed initiator driver from Linux kernel, which is, particularly, +intended to perform all the initialization and shutdown tasks. The +initiator driver was changed to provide the target mode support and all +necessary callbacks, but it's still capable to work as initiator only. +Mode, when a host acts as the initiator and the target simultaneously, +is supported as well. This version is compatible with SCST core version 1.0.2 and higher and Linux kernel 2.6.26 and higher. If you need to use this driver on kernels prior 2.6.26, you should apply qla_ini_pre-2.6.26.patch. -The original initiator driver was taken from the kernel 2.6.27. +The original initiator driver was taken from the kernel 2.6.26. See also "ToDo" file for list of known issues and unimplemented features. + Installation ------------ Only vanilla kernels from kernel.org and RHEL/CentOS 5.2 kernels are supported, but SCST should work on other (vendors') kernels, if you -manage to successfully compile on them. The main problem with vendors' -kernels is that they often contain patches, which will appear only in -the next version of the vanilla kernel, therefore it's quite hard to -track such changes. Thus, if during compilation for some vendor kernel -your compiler complains about redefinition of some symbol, you should -either switch to vanilla kernel, or add or change as necessary the -corresponding to that symbol "#if LINUX_VERSION_CODE" statement. +manage to successfully compile it on them. The main problem with +vendors' kernels is that they often contain patches, which will appear +only in the next version of the vanilla kernel, therefore it's quite +hard to track such changes. Thus, if during compilation for some vendor +kernel your compiler complains about redefinition of some symbol, you +should either switch to vanilla kernel, or add or change as necessary +the corresponding to that symbol "#if LINUX_VERSION_CODE" statement. -At first, make sure that the link "/lib/modules/`you_kernel_version`/build" -points to the source code for your currently running kernel. +Befer installation make sure that the link +"/lib/modules/`you_kernel_version`/build" points to the source code for +your currently running kernel. Then you should replace (or link) by the initiator driver from this package "qla2xxx" subdirectory in kernel_source/drivers/scsi/ of the @@ -80,6 +81,30 @@ You can find some installation and configuration HOWTOs in http://scst.sourceforge.net/qla2x00t-howto.html and https://forums.openfiler.com/viewtopic.php?id=3422. + +Explicit conformation +--------------------- + +This option should (actually, almost always must) be enabled by echoing +"1" in /sys/class/scsi_host/hostX/explicit_conform_enabled, if a target +card exports at least one stateful SCSI device, like tape, and class 2 +isn't used, otherwise link-level errors could lead to loss of the +target/initiator state synchronization. Also check if initiator supports +this feature, it is reported in the kernel logs ("confirmed completion +supported" or not). No major performance degradation was noticed, if it +is enabled. Supported only for 23xx+. Disabled by default. + + +Class 2 +------- + +Class 2 is the close equivalent of the TCP in the IP world. If you +enable it, all the Fibre Channel packets will be acknowledged. By +default, class 3 is used, which is UDP-like. Enable it by echoing +"1" in /sys/class/scsi_host/hostX/class2_enabled. This option needs +a special firmware with class 2 support. Disabled by default. + + Compilation options ------------------- @@ -98,11 +123,18 @@ in/out in Makefile: callbacks in internal SCST threads context instead of SIRQ context, where those commands were received. Useful for debugging and lead to some performance loss. + + - CONFIG_QLA_TGT_DEBUG_SRR - turns on restransmitting packets (SRR) + debugging. In this mode some CTIOs will be "broken" to force the + initiator to issue a retransmit request. + Credits ------- -Thanks to +Thanks to: + + * QLogic support for their invaluable help. * Nathaniel Clark for porting to new 2.6 kernel initiator driver. diff --git a/qla2x00t/qla2x00-target/ToDo b/qla2x00t/qla2x00-target/ToDo index db19eb0c9..97d152cc6 100644 --- a/qla2x00t/qla2x00-target/ToDo +++ b/qla2x00t/qla2x00-target/ToDo @@ -1,8 +1,10 @@ Known issues and unimplemented features --------------------------------------- + - NPIV support + - If a Linux initiator asks for devices using INQUIRY command too soon - before the controller on the target is fully initialized in the + before the controller on the 23xx target is fully initialized in the target mode, the initiator could receive garbage devices and the messages "scsi: unknown device type 31" will be printed in the kernel log on the initiator. After rescan on the initiator (eg by @@ -15,9 +17,9 @@ Known issues and unimplemented features - Minor "ToDo"'s spread in the code. - - On 2300 if on a tape with block size 0 we write block with size X + - On 23xx if on a tape with block size 0 we write block with size X and then read it with bs * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark + * Copyright (C) 2006 - 2009 ID7 Ltd. * - * QLogic 2x00 SCSI target driver. - * + * QLogic 22xx/23xx/24xx/25xx FC target driver. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, version 2 * of the License. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -35,215 +36,225 @@ #include "qla2x00t.h" -#if !defined(CONFIG_SCSI_QLA2XXX_TARGET) +#ifndef CONFIG_SCSI_QLA2XXX_TARGET #error "CONFIG_SCSI_QLA2XXX_TARGET is NOT DEFINED" #endif #ifdef CONFIG_SCST_DEBUG -#define Q2T_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \ +#define Q2T_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_LINE| TRACE_PID | \ TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_MINOR | \ TRACE_MGMT_DEBUG | TRACE_MINOR | TRACE_SPECIAL) #else # ifdef CONFIG_SCST_TRACING #define Q2T_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MINOR | \ - TRACE_SPECIAL) + TRACE_SPECIAL) # endif #endif -static int q2t_target_detect(struct scst_tgt_template *templ); +static int q2x_target_detect(struct scst_tgt_template *templ); +static int q24_target_detect(struct scst_tgt_template *templ); static int q2t_target_release(struct scst_tgt *scst_tgt); -static int q2t_xmit_response(struct scst_cmd *scst_cmd); +static int q2x_xmit_response(struct scst_cmd *scst_cmd); +static int q24_xmit_response(struct scst_cmd *scst_cmd); static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd); static void q2t_on_free_cmd(struct scst_cmd *scst_cmd); static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *mcmd); /* Predefs for callbacks handed to qla2xxx(target) */ -static void q2t_response_pkt(scsi_qla_host_t *ha, sts_entry_t *pkt); +static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *pkt); +static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt); static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha, uint16_t *mailbox); -static void q2t_ctio_completion(scsi_qla_host_t *ha, uint32_t handle); -static void q2t_host_action(scsi_qla_host_t *ha, - enum qla2x_tgt_host_action action); -static void q2t_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, - struct atio_entry *atio, int ha_locked); +static void q2x_ctio_completion(scsi_qla_host_t *ha, uint32_t handle); +static int q2t_host_action(scsi_qla_host_t *ha, + qla2x_tgt_host_action_t action); +static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport); +static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport); +static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, + int lun_size, int fn, void *iocb, int flags); +static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, + atio_entry_t *atio, int ha_locked); +static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, + atio7_entry_t *atio, int ha_locked); +static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, + int ha_lock); +static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset); +static void q2t_clear_tgt_db(struct q2t_tgt *tgt, bool local_only); static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd); +static int q2t_unreg_sess(struct q2t_sess *sess); /* * Global Variables */ +static struct scst_tgt_template tgt2x_template = { + .name = "qla2x00tgt", + .sg_tablesize = 0, + .use_clustering = 1, +#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD + .xmit_response_atomic = 0, + .rdy_to_xfer_atomic = 0, +#else + .xmit_response_atomic = 1, + .rdy_to_xfer_atomic = 1, +#endif +#if SCST_VERSION_CODE >= SCST_VERSION(1, 0, 2, 0) + .max_hw_pending_time = Q2T_MAX_HW_PENDING_TIME, +#endif + .detect = q2x_target_detect, + .release = q2t_target_release, + .xmit_response = q2x_xmit_response, + .rdy_to_xfer = q2t_rdy_to_xfer, + .on_free_cmd = q2t_on_free_cmd, + .task_mgmt_fn_done = q2t_task_mgmt_fn_done, +#if SCST_VERSION_CODE >= SCST_VERSION(1, 0, 2, 0) + .on_hw_pending_cmd_timeout = q2t_on_hw_pending_cmd_timeout, +#endif +}; + +static struct scst_tgt_template tgt24_template = { + .name = "qla24xx-tgt", + .sg_tablesize = 0, + .use_clustering = 1, + .no_proc_entry = 1, +#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD + .xmit_response_atomic = 0, + .rdy_to_xfer_atomic = 0, +#else + .xmit_response_atomic = 1, + .rdy_to_xfer_atomic = 1, +#endif +#if SCST_VERSION_CODE >= SCST_VERSION(1, 0, 2, 0) + .max_hw_pending_time = Q2T_MAX_HW_PENDING_TIME, +#endif + .detect = q24_target_detect, + .release = q2t_target_release, + .xmit_response = q24_xmit_response, + .rdy_to_xfer = q2t_rdy_to_xfer, + .on_free_cmd = q2t_on_free_cmd, + .task_mgmt_fn_done = q2t_task_mgmt_fn_done, +#if SCST_VERSION_CODE >= SCST_VERSION(1, 0, 2, 0) + .on_hw_pending_cmd_timeout = q2t_on_hw_pending_cmd_timeout, +#endif +}; + #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) #define trace_flag q2t_trace_flag static unsigned long q2t_trace_flag = Q2T_DEFAULT_LOG_FLAGS; #endif -static struct scst_tgt_template tgt_template = { - .name = "qla2x00tgt", - .sg_tablesize = 0, - .use_clustering = 1, -#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD - .xmit_response_atomic = 0, - .rdy_to_xfer_atomic = 0, -#else - .xmit_response_atomic = 1, - .rdy_to_xfer_atomic = 1, -#endif - .max_hw_pending_time = Q2T_MAX_HW_PENDING_TIME, - .detect = q2t_target_detect, - .release = q2t_target_release, - .xmit_response = q2t_xmit_response, - .rdy_to_xfer = q2t_rdy_to_xfer, - .on_free_cmd = q2t_on_free_cmd, - .task_mgmt_fn_done = q2t_task_mgmt_fn_done, - .on_hw_pending_cmd_timeout = q2t_on_hw_pending_cmd_timeout, -}; - static struct kmem_cache *q2t_cmd_cachep; -static struct qla2x_tgt_target tgt_data; -static DEFINE_MUTEX(qla_mgmt_mutex); +static struct qla_target tgt_data; +static struct kmem_cache *q2t_mgmt_cmd_cachep; +static mempool_t *q2t_mgmt_cmd_mempool; -/* - * Functions - */ +static DECLARE_RWSEM(q2t_unreg_rwsem); -static inline int test_tgt_sess_count(struct q2t_tgt *tgt, scsi_qla_host_t *ha) +/* It's not yet supported */ +static inline int scst_cmd_get_ppl_offset(struct scst_cmd *scst_cmd) { - unsigned long flags; - int res; - - /* - * We need to protect against race, when tgt is freed before or - * inside wake_up() - */ - spin_lock_irqsave(&tgt->ha->hardware_lock, flags); - TRACE_DBG("tgt %p, empty(sess_list)=%d sess_count=%d", - tgt, list_empty(&tgt->sess_list), tgt->sess_count); - res = (tgt->sess_count == 0); - spin_unlock_irqrestore(&tgt->ha->hardware_lock, flags); - - return res; + return 0; } +/* ha->hardware_lock supposed to be held on entry */ +static inline void q2t_sess_get(struct q2t_sess *sess) +{ + sess->sess_ref++; + TRACE_DBG("sess %p, new sess_ref %d", sess, sess->sess_ref); +} + +/* ha->hardware_lock supposed to be held on entry */ +static inline void q2t_sess_put(struct q2t_sess *sess) +{ + TRACE_DBG("sess %p, new sess_ref %d", sess, sess->sess_ref-1); + sBUG_ON(sess->sess_ref == 0); + + sess->sess_ref--; + if (sess->sess_ref == 0) + q2t_unreg_sess(sess); +} + +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt, + uint16_t lid) +{ + struct q2t_sess *sess; + sBUG_ON(tgt == NULL); + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + if (lid == (sess->loop_id)) + return sess; + } + return NULL; +} + +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt, + const uint8_t *s_id) +{ + struct q2t_sess *sess; + sBUG_ON(tgt == NULL); + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + if ((sess->s_id.b.al_pa == s_id[2]) && + (sess->s_id.b.area == s_id[1]) && + (sess->s_id.b.domain == s_id[0])) + return sess; + } + return NULL; +} + +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt, + const uint8_t *s_id) +{ + struct q2t_sess *sess; + sBUG_ON(tgt == NULL); + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + if ((sess->s_id.b.al_pa == s_id[0]) && + (sess->s_id.b.area == s_id[1]) && + (sess->s_id.b.domain == s_id[2])) + return sess; + } + return NULL; +} + + /* ha->hardware_lock supposed to be held on entry */ static inline void q2t_exec_queue(scsi_qla_host_t *ha) { tgt_data.isp_cmd(ha); } -/* ha->hardware_lock supposed to be held on entry */ -static void q2t_modify_command_count(scsi_qla_host_t *ha, int cmd_count, - int imm_count) +/* Might release hw lock, then reaquire!! */ +static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked) { - struct modify_lun_entry *pkt; - - TRACE_ENTRY(); - - TRACE_DBG("Sending MODIFY_LUN ha %p, cmd %d, imm %d", - ha, cmd_count, imm_count); - - pkt = (struct modify_lun_entry *)tgt_data.req_pkt(ha); - ha->tgt->modify_lun_expected++; - - pkt->entry_type = MODIFY_LUN_TYPE; - pkt->entry_count = 1; - if (cmd_count < 0) { - /* Subtract from command count */ - pkt->operators = MODIFY_LUN_CMD_SUB; - pkt->command_count = -cmd_count; - } else if (cmd_count > 0) { - /* Add to command count */ - pkt->operators = MODIFY_LUN_CMD_ADD; - pkt->command_count = cmd_count; - } - - if (imm_count < 0) { - pkt->operators |= MODIFY_LUN_IMM_SUB; - pkt->immed_notify_count = -imm_count; - } else if (imm_count > 0) { - pkt->operators |= MODIFY_LUN_IMM_ADD; - pkt->immed_notify_count = imm_count; - } - - pkt->timeout = 0; /* Use default */ - q2t_exec_queue(ha); - - TRACE_EXIT(); - return; -} - -/* ha->hardware_lock supposed to be held on entry */ -static void __q2t_send_notify_ack(scsi_qla_host_t *ha, - uint16_t target_id, uint16_t status, uint16_t task_flags, - uint16_t seq_id, uint32_t add_flags, uint16_t resp_code, - int resp_code_valid, uint16_t ox_id) -{ - struct nack_entry *ntfy; - - TRACE_ENTRY(); - /* Send marker if required */ - if (tgt_data.issue_marker(ha) != QLA_SUCCESS) { - PRINT_ERROR("qla2x00tgt(%ld): __QLA2X00_MARKER() " - "failed", ha->host_no); - goto out; + if (unlikely(ha->marker_needed != 0)) { + int rc = tgt_data.issue_marker(ha, ha_locked); + if (rc != QLA_SUCCESS) { + PRINT_ERROR("qla2x00tgt(%ld): issue_marker() " + "failed", ha->instance); + } + return rc; } - - ntfy = (struct nack_entry *)tgt_data.req_pkt(ha); - - if (ha->tgt != NULL) - ha->tgt->notify_ack_expected++; - - memset(ntfy, 0, sizeof(*ntfy)); - ntfy->entry_type = NOTIFY_ACK_TYPE; - ntfy->entry_count = 1; - SET_TARGET_ID(ha, ntfy->target, target_id); - ntfy->status = status; - ntfy->task_flags = task_flags; - ntfy->seq_id = seq_id; - /* Do not increment here, the chip isn't decrementing */ - /* ntfy->flags = __constant_cpu_to_le16(NOTIFY_ACK_RES_COUNT); */ - ntfy->flags |= cpu_to_le16(add_flags); - ntfy->ox_id = ox_id; - - if (resp_code_valid) { - ntfy->resp_code = cpu_to_le16(resp_code); - ntfy->flags |= - __constant_cpu_to_le16(NOTIFY_ACK_TM_RESP_CODE_VALID); - } - - TRACE(TRACE_SCSI, "Sending Notify Ack Seq %#x -> I %#x St %#x RC %#x", - le16_to_cpu(seq_id), target_id, le16_to_cpu(status), - le16_to_cpu(ntfy->resp_code)); - - q2t_exec_queue(ha); - -out: - TRACE_EXIT(); - return; -} -/* ha->hardware_lock supposed to be held on entry */ -static inline void q2t_send_notify_ack(scsi_qla_host_t *ha, - struct notify_entry *iocb, uint32_t add_flags, uint16_t resp_code, - int resp_code_valid) -{ - __q2t_send_notify_ack(ha, GET_TARGET_ID(ha, iocb), iocb->status, - iocb->task_flags, iocb->seq_id, add_flags, resp_code, - resp_code_valid, iocb->ox_id); + return (QLA_SUCCESS); } -/* - * register with initiator driver (but target mode isn't enabled till +/* + * Registers with initiator driver (but target mode isn't enabled till * it's turned on via sysfs) */ -static int q2t_target_detect(struct scst_tgt_template *templ) +static int q2x_target_detect(struct scst_tgt_template *templ) { int res; - struct qla2x_tgt_initiator itd = { - .magic = QLA2X_TARGET_MAGIC, - .tgt_response_pkt = q2t_response_pkt, - .tgt_ctio_completion = q2t_ctio_completion, - .tgt_async_event = q2t_async_event, - .tgt_host_action = q2t_host_action, + struct qla_tgt_initiator itd = { + magic: QLA2X_TARGET_MAGIC, + tgt24_atio_pkt: q24_atio_pkt, + tgt_response_pkt: q2t_response_pkt, + tgt2x_ctio_completion: q2x_ctio_completion, + tgt_async_event: q2t_async_event, + tgt_host_action: q2t_host_action, + tgt_fc_port_added: q2t_fc_port_added, + tgt_fc_port_deleted: q2t_fc_port_deleted, }; TRACE_ENTRY(); @@ -254,18 +265,26 @@ static int q2t_target_detect(struct scst_tgt_template *templ) goto out; } - if (tgt_data.magic != QLA2X_INITIATOR_MAGIC) { - PRINT_ERROR("Wrong version of the initiator part: %d", - tgt_data.magic); - res = -EINVAL; - } + if (tgt_data.magic != QLA2X_INITIATOR_MAGIC) { + PRINT_ERROR("Wrong version of the initiator part: %d", + tgt_data.magic); + res = -EINVAL; + } + + PRINT_INFO("%s", "Target mode driver for QLogic 2x00 controller " + "registered successfully"); out: TRACE_EXIT(); return res; } -/* no lock held */ +static int q24_target_detect(struct scst_tgt_template *templ) +{ + /* Nothing to do */ + return 0; +} + static void q2t_free_session_done(struct scst_session *scst_sess) { struct q2t_sess *sess; @@ -280,13 +299,15 @@ static void q2t_free_session_done(struct scst_session *scst_sess) sBUG_ON(sess == NULL); tgt = sess->tgt; + TRACE_MGMT_DBG("Unregistration of sess %p finished", sess); + kfree(sess); if (tgt == NULL) goto out; - TRACE_MGMT_DBG("tgt %p, empty(sess_list) %d, sess_count %d", - tgt, list_empty(&tgt->sess_list), tgt->sess_count); + TRACE_DBG("empty(sess_list) %d sess_count %d", + list_empty(&tgt->sess_list), tgt->sess_count); ha = tgt->ha; @@ -306,101 +327,511 @@ out: } /* ha->hardware_lock supposed to be held on entry */ -static void q2t_unreg_sess(struct q2t_sess *sess) +static int q2t_unreg_sess(struct q2t_sess *sess) { + int res = 1; + TRACE_ENTRY(); - if (sess == NULL) - goto out; + sBUG_ON(sess == NULL); + sBUG_ON(sess->sess_ref != 0); - list_del(&sess->list); + TRACE_MGMT_DBG("Deleting sess %p from tgt %p", sess, sess->tgt); + list_del(&sess->sess_list_entry); - PRINT_INFO("qla2x00tgt(%ld): session for loop_id %d deleted", - sess->tgt->ha->host_no, sess->loop_id); + if (sess->deleted) + list_del(&sess->del_list_entry); - /* - * Any commands for this session will be finished regularly, - * because we must not drop SCSI commands on transport level, - * at least without any response to the initiator. - */ + PRINT_INFO("qla2x00tgt(%ld): %ssession for loop_id %d deleted", + sess->tgt->ha->instance, sess->local ? "local " : "", + sess->loop_id); scst_unregister_session(sess->scst_sess, 0, q2t_free_session_done); -out: - TRACE_EXIT(); - return; + TRACE_EXIT_RES(res); + return res; } /* ha->hardware_lock supposed to be held on entry */ -static void q2t_port_logout(scsi_qla_host_t *ha, int loop_id) +static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd) { - struct q2t_sess *sess = q2t_find_sess_by_lid(ha->tgt, loop_id); + struct q2t_sess *sess; + int loop_id; + uint16_t lun = 0; + int res = 0; - TRACE_MGMT_DBG("scsi(%ld) Unregistering session %p loop_id=%d", - ha->host_no, sess, loop_id); + TRACE_ENTRY(); - q2t_unreg_sess(sess); + if (IS_FWI2_CAPABLE(ha)) { + notify24xx_entry_t *n = (notify24xx_entry_t *)iocb; + loop_id = le16_to_cpu(n->nport_handle); + } else + loop_id = GET_TARGET_ID(ha, (notify_entry_t *)iocb); + + if (loop_id == 0xFFFF) { + /* Global event */ + q2t_clear_tgt_db(ha->tgt, 1); + if (!list_empty(&ha->tgt->sess_list)) { + sess = list_entry(ha->tgt->sess_list.next, + typeof(*sess), sess_list_entry); + switch(mcmd) { + case Q2T_NEXUS_LOSS_SESS: + mcmd = Q2T_NEXUS_LOSS; + break; + + case Q2T_ABORT_ALL_SESS: + mcmd = Q2T_ABORT_ALL; + break; + + case Q2T_NEXUS_LOSS: + case Q2T_ABORT_ALL: + break; + + default: + PRINT_ERROR("qla2x00tgt(%ld): Not allowed " + "command %x in %s", ha->instance, + mcmd, __func__); + sess = NULL; + break; + } + } else + sess = NULL; + } else + sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); + + if (sess == NULL) { + res = -ESRCH; + ha->tgt->tm_to_unknown = 1; + goto out; + } + + TRACE_MGMT_DBG("scsi(%ld): resetting (session %p, " + "mcmd %x, loop_id %d)", ha->host_no, sess, mcmd, loop_id); + + res = q2t_issue_task_mgmt(sess, (uint8_t *)&lun, sizeof(lun), + mcmd, iocb, Q24_MGMT_SEND_NACK); + +out: + TRACE_EXIT_RES(res); + return res; } /* ha->hardware_lock supposed to be held on entry */ -static void q2t_clear_tgt_db(struct q2t_tgt *tgt) +static void q2t_clear_tgt_db(struct q2t_tgt *tgt, bool local_only) { struct q2t_sess *sess, *sess_tmp; TRACE_ENTRY(); - TRACE_MGMT_DBG("Clearing targets DB %p", tgt); + TRACE(TRACE_MGMT, "Clearing targets DB %p", tgt); - list_for_each_entry_safe(sess, sess_tmp, &tgt->sess_list, list) { - q2t_unreg_sess(sess); + list_for_each_entry_safe(sess, sess_tmp, &tgt->sess_list, + sess_list_entry) { + if (local_only && !sess->local) + continue; + if (local_only && sess->local) + TRACE_MGMT_DBG("Putting local session %p", sess); + q2t_sess_put(sess); } /* At this point tgt could be already dead */ - TRACE_MGMT_DBG("Finished clearing Target DB %p", tgt); + TRACE_MGMT_DBG("Finished clearing tgt %p DB", tgt); TRACE_EXIT(); return; } -/* should be called w/out hardware_lock, but tgt should be - * unfindable at this point */ +/* Called in a thread context */ +static void q2t_alloc_session_done(struct scst_session *scst_sess, + void *data, int result) +{ + TRACE_ENTRY(); + + if (result != 0) { + struct q2t_sess *sess = (struct q2t_sess *)data; + struct q2t_tgt *tgt = sess->tgt; + scsi_qla_host_t *ha = tgt->ha; + unsigned long flags; + + PRINT_INFO("qla2x00tgt(%ld): Session initialization failed", + ha->instance); + + spin_lock_irqsave(&ha->hardware_lock, flags); + q2t_sess_put(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } + + TRACE_EXIT(); + return; +} + +static void q2t_del_sess_timer_fn(unsigned long arg) +{ + struct q2t_tgt *tgt = (struct q2t_tgt *)arg; + scsi_qla_host_t *ha = tgt->ha; + struct q2t_sess *sess; + unsigned long flags; + + TRACE_ENTRY(); + + spin_lock_irqsave(&ha->hardware_lock, flags); + while(!list_empty(&tgt->del_sess_list)) { + sess = list_entry(tgt->del_sess_list.next, typeof(*sess), + del_list_entry); + if (time_after_eq(jiffies, sess->expires)) { + /* + * sess will be deleted from del_sess_list in + * q2t_unreg_sess() + */ + TRACE_MGMT_DBG("Timeout: sess %p about to be deleted", + sess); + q2t_sess_put(sess); + } else { + tgt->sess_del_timer.expires = sess->expires; + add_timer(&tgt->sess_del_timer); + break; + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + TRACE_EXIT(); + return; +} + +/* + * Must be called under tgt_mutex. + * + * Adds an extra ref to allow to drop hw lock after adding sess to the list. + * Caller must put it. + */ +static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, + bool local) +{ + char *wwn_str; + const int wwn_str_len = 3*WWN_SIZE+2; + struct q2t_tgt *tgt = ha->tgt; + struct q2t_sess *sess; + + TRACE_ENTRY(); + + /* Check to avoid double sessions */ + spin_lock_irq(&ha->hardware_lock); + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + if ((sess->port_name[0] == fcport->port_name[0]) && + (sess->port_name[1] == fcport->port_name[1]) && + (sess->port_name[2] == fcport->port_name[2]) && + (sess->port_name[3] == fcport->port_name[3]) && + (sess->port_name[4] == fcport->port_name[4]) && + (sess->port_name[5] == fcport->port_name[5]) && + (sess->port_name[6] == fcport->port_name[6]) && + (sess->port_name[7] == fcport->port_name[7])) { + TRACE_MGMT_DBG("Double sess %p found (s_id %x:%x:%x, " + "loop_id %d), updating to d_id %x:%x:%x, " + "loop_id %d", sess, sess->s_id.b.al_pa, + sess->s_id.b.area, sess->s_id.b.domain, + sess->loop_id, fcport->d_id.b.al_pa, + fcport->d_id.b.area, fcport->d_id.b.domain, + fcport->loop_id); + + if (sess->deleted) { + list_del(&sess->del_list_entry); + sess->deleted = 0; + } + + sess->s_id = fcport->d_id; + sess->loop_id = fcport->loop_id; + sess->conf_compl_supported = fcport->conf_compl_supported; + if (sess->local && !local) + sess->local = false; + spin_unlock_irq(&ha->hardware_lock); + goto out; + } + } + spin_unlock_irq(&ha->hardware_lock); + + /* We are under tgt_mutex, so a new sess can't be added behind us */ + + sess = kzalloc(sizeof(*sess), GFP_KERNEL); + if (sess == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): session allocation failed, " + "all commands from port %02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x will be refused", ha->instance, + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7]); + goto out; + } + + sess->sess_ref = 2; /* plus 1 extra ref, see above */ + sess->tgt = ha->tgt; + sess->s_id = fcport->d_id; + sess->loop_id = fcport->loop_id; + sess->conf_compl_supported = fcport->conf_compl_supported; + sess->local = local; + BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name)); + memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name)); + + wwn_str = kmalloc(wwn_str_len, GFP_KERNEL); + if (wwn_str == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): Allocation of wwn_str failed. " + "All commands from port %02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x will be refused", ha->instance, + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7]); + goto out_free_sess; + } + + sprintf(wwn_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7]); + + /* Let's do the session creation async'ly */ + sess->scst_sess = scst_register_session(tgt->scst_tgt, 1, wwn_str, + sess, q2t_alloc_session_done); + + if (sess->scst_sess == NULL) { + PRINT_CRIT_ERROR("qla2x00tgt(%ld): scst_register_session() " + "failed for host %ld (wwn %s, loop_id %d), all " + "commands from it will be refused", ha->instance, + ha->host_no, wwn_str, fcport->loop_id); + goto out_free_sess_wwn; + } + scst_sess_set_tgt_priv(sess->scst_sess, sess); + + spin_lock_irq(&ha->hardware_lock); + TRACE_MGMT_DBG("Adding sess %p to tgt %p", sess, tgt); + list_add_tail(&sess->sess_list_entry, &tgt->sess_list); + tgt->sess_count++; + spin_unlock_irq(&ha->hardware_lock); + + PRINT_INFO("qla2x00tgt(%ld): %ssession for wwn %s (loop_id %d, " + "s_id %x:%x:%x, confirmed completion %ssupported) added", + ha->instance, local ? "local " : "", wwn_str, fcport->loop_id, + sess->s_id.b.al_pa, sess->s_id.b.area, sess->s_id.b.domain, + sess->conf_compl_supported ? "" : "not "); + + kfree(wwn_str); + +out: + TRACE_EXIT_HRES(sess); + return sess; + +out_free_sess_wwn: + kfree(wwn_str); + /* go through */ + +out_free_sess: + kfree(sess); + sess = NULL; + goto out; +} + +static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) +{ + struct q2t_tgt *tgt; + struct q2t_sess *sess; + + TRACE_ENTRY(); + + mutex_lock(&ha->tgt_mutex); + + tgt = ha->tgt; + + if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR)) + goto out_unlock; + + if (tgt->tgt_shutdown) + goto out_unlock; + + spin_lock_irq(&ha->hardware_lock); + + sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id); + if (sess == NULL) { + spin_unlock_irq(&ha->hardware_lock); + sess = q2t_create_sess(ha, fcport, false); + spin_lock_irq(&ha->hardware_lock); + if (sess != NULL) + q2t_sess_put(sess); /* put the extra creation ref */ + } else { + if (sess->deleted) { + list_del(&sess->del_list_entry); + sess->deleted = 0; + + PRINT_INFO("qla2x00tgt(%ld): session for port %02x:" + "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) " + "reappeared", ha->instance, fcport->port_name[0], + fcport->port_name[1], fcport->port_name[2], + fcport->port_name[3], fcport->port_name[4], + fcport->port_name[5], fcport->port_name[6], + fcport->port_name[7], sess->loop_id); + TRACE_MGMT_DBG("Appeared sess %p", sess); + } else if (sess->local) { + TRACE(TRACE_MGMT, "qla2x00tgt(%ld): local session for " + "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x " + "(loop ID %d) became global", ha->instance, + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], + sess->loop_id); + } + sess->local = 0; + } + + spin_unlock_irq(&ha->hardware_lock); + +out_unlock: + mutex_unlock(&ha->tgt_mutex); + + TRACE_EXIT(); + return; +} + +static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) +{ + struct q2t_tgt *tgt; + struct q2t_sess *sess; + uint32_t dev_loss_tmo; + + TRACE_ENTRY(); + + mutex_lock(&ha->tgt_mutex); + + tgt = ha->tgt; + + if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR)) + goto out_unlock; + + dev_loss_tmo = ha->port_down_retry_count + 5; + + if (tgt->tgt_shutdown) + goto out_unlock; + + spin_lock_irq(&ha->hardware_lock); + + sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id); + if (sess == NULL) + goto out_unlock_ha; + + if (!sess->deleted) { + int add_tmr; + + add_tmr = list_empty(&tgt->del_sess_list); + + TRACE_MGMT_DBG("Scheduling sess %p to deletion", sess); + list_add_tail(&sess->del_list_entry, &tgt->del_sess_list); + sess->deleted = 1; + + PRINT_INFO("qla2x00tgt(%ld): %ssession for port %02x:%02x:%02x:" + "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for " + "deletion in %d secs", ha->instance, + sess->local ? "local " : "", + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], + sess->loop_id, dev_loss_tmo); + + sess->expires = jiffies + dev_loss_tmo * HZ; + if (add_tmr) + mod_timer(&tgt->sess_del_timer, sess->expires); + } + +out_unlock_ha: + spin_unlock_irq(&ha->hardware_lock); + +out_unlock: + mutex_unlock(&ha->tgt_mutex); + + TRACE_EXIT(); + return; +} + +static inline int test_tgt_sess_count(struct q2t_tgt *tgt) +{ + unsigned long flags; + int res; + + /* + * We need to protect against race, when tgt is freed before or + * inside wake_up() + */ + spin_lock_irqsave(&tgt->ha->hardware_lock, flags); + TRACE_DBG("tgt %p, empty(sess_list)=%d sess_count=%d", + tgt, list_empty(&tgt->sess_list), tgt->sess_count); + res = (tgt->sess_count == 0); + spin_unlock_irqrestore(&tgt->ha->hardware_lock, flags); + + return res; +} + +/* Must be called under read locked q2t_unreg_rwsem */ static int q2t_target_release(struct scst_tgt *scst_tgt) { int res = 0; struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); scsi_qla_host_t *ha = tgt->ha; - unsigned long flags = 0; TRACE_ENTRY(); - spin_lock_irqsave(&ha->hardware_lock, flags); + /* + * Mutex needed to sync with q2t_fc_port_[added,deleted]. + * Lock is needed, because we still can get an incoming packet. + */ + + mutex_lock(&ha->tgt_mutex); + spin_lock_irq(&ha->hardware_lock); tgt->tgt_shutdown = 1; - q2t_clear_tgt_db(tgt); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + q2t_clear_tgt_db(tgt, false); + spin_unlock_irq(&ha->hardware_lock); + mutex_unlock(&ha->tgt_mutex); - wait_event(tgt->waitQ, test_tgt_sess_count(tgt, ha)); + del_timer_sync(&tgt->sess_del_timer); - mutex_lock(&qla_mgmt_mutex); + TRACE_MGMT_DBG("Waiting for sess works (tgt %p)", tgt); + spin_lock_irq(&tgt->sess_work_lock); + while(!list_empty(&tgt->sess_works_list)) { + spin_unlock_irq(&tgt->sess_work_lock); + flush_scheduled_work(); + spin_lock_irq(&tgt->sess_work_lock); + } + spin_unlock_irq(&tgt->sess_work_lock); - /* big hammer */ - if (!ha->flags.host_shutting_down) - tgt_data.disable_lun(ha); - - /* wait for sessions to clear out (just in case) */ - wait_event(tgt->waitQ, test_tgt_sess_count(tgt, ha)); - - TRACE_MGMT_DBG("Finished waiting for tgt %p: empty(sess_list)=%d " + TRACE_MGMT_DBG("Waiting for tgt %p: list_empty(sess_list)=%d " "sess_count=%d", tgt, list_empty(&tgt->sess_list), tgt->sess_count); - /* The lock is needed, because we still can get an incoming packet */ - spin_lock_irqsave(&ha->hardware_lock, flags); + wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); + + /* Big hammer */ + if (!ha->host_shutting_down) + tgt_data.disable_tgt_mode(ha); + + /* Wait for sessions to clear out (just in case) */ + wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); + + TRACE_MGMT_DBG("Waiting for %d IRQ commands to complete (tgt %p)", + tgt->irq_cmd_count, tgt); + + mutex_lock(&ha->tgt_mutex); + spin_lock_irq(&ha->hardware_lock); + while (tgt->irq_cmd_count != 0) { + spin_unlock_irq(&ha->hardware_lock); + udelay(2); + spin_lock_irq(&ha->hardware_lock); + } scst_tgt_set_tgt_priv(scst_tgt, NULL); ha->tgt = NULL; - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irq(&ha->hardware_lock); + mutex_unlock(&ha->tgt_mutex); - mutex_unlock(&qla_mgmt_mutex); + TRACE_MGMT_DBG("Release of tgt %p finished", tgt); kfree(tgt); @@ -408,15 +839,513 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) return res; } +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q2x_modify_command_count(scsi_qla_host_t *ha, int cmd_count, + int imm_count) +{ + modify_lun_entry_t *pkt; + + TRACE_ENTRY(); + + TRACE_DBG("Sending MODIFY_LUN (ha=%p, cmd=%d, imm=%d)", + ha, cmd_count, imm_count); + + /* Sending marker isn't necessary, since we called from ISR */ + + pkt = (modify_lun_entry_t *)tgt_data.req_pkt(ha); + if (pkt == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + ha->tgt->modify_lun_expected++; + + pkt->entry_type = MODIFY_LUN_TYPE; + pkt->entry_count = 1; + if (cmd_count < 0) { + pkt->operators = MODIFY_LUN_CMD_SUB; /* Subtract from command count */ + pkt->command_count = -cmd_count; + } else if (cmd_count > 0){ + pkt->operators = MODIFY_LUN_CMD_ADD; /* Add to command count */ + pkt->command_count = cmd_count; + } + + if (imm_count < 0) { + pkt->operators |= MODIFY_LUN_IMM_SUB; + pkt->immed_notify_count = -imm_count; + } else if (imm_count > 0) { + pkt->operators |= MODIFY_LUN_IMM_ADD; + pkt->immed_notify_count = imm_count; + } + + pkt->timeout = 0; /* Use default */ + + TRACE_BUFFER("MODIFY LUN packet data", pkt, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); + +out: + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb, + uint32_t add_flags, uint16_t resp_code, int resp_code_valid, + uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan) +{ + nack_entry_t *ntfy; + + TRACE_ENTRY(); + + TRACE_DBG("Sending NOTIFY_ACK (ha=%p)", ha); + + /* Send marker if required */ + if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + goto out; + + ntfy = (nack_entry_t *)tgt_data.req_pkt(ha); + if (ntfy == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + if (ha->tgt != NULL) + ha->tgt->notify_ack_expected++; + + ntfy->entry_type = NOTIFY_ACK_TYPE; + ntfy->entry_count = 1; + SET_TARGET_ID(ha, ntfy->target, GET_TARGET_ID(ha, iocb)); + ntfy->status = iocb->status; + ntfy->task_flags = iocb->task_flags; + ntfy->seq_id = iocb->seq_id; + /* Do not increment here, the chip isn't decrementing */ + /* ntfy->flags = __constant_cpu_to_le16(NOTIFY_ACK_RES_COUNT); */ + ntfy->flags |= cpu_to_le16(add_flags); + ntfy->srr_rx_id = iocb->srr_rx_id; + ntfy->srr_rel_offs = iocb->srr_rel_offs; + ntfy->srr_ui = iocb->srr_ui; + ntfy->srr_flags = cpu_to_le16(srr_flags); + ntfy->srr_reject_code = cpu_to_le16(srr_reject_code); + ntfy->srr_reject_code_expl = srr_explan; + ntfy->ox_id = iocb->ox_id; + + if (resp_code_valid) { + ntfy->resp_code = cpu_to_le16(resp_code); + ntfy->flags |= __constant_cpu_to_le16( + NOTIFY_ACK_TM_RESP_CODE_VALID); + } + + TRACE(TRACE_SCSI, "Sending Notify Ack Seq %#x -> I %#x St %#x RC %#x", + le16_to_cpu(iocb->seq_id), GET_TARGET_ID(ha, iocb), + le16_to_cpu(iocb->status), le16_to_cpu(ntfy->resp_code)); + TRACE_BUFFER("Notify Ack packet data", ntfy, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); + +out: + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q24_send_abts_resp(scsi_qla_host_t *ha, + const abts24_recv_entry_t *abts, uint32_t status, bool ids_reversed) +{ + abts24_resp_entry_t *resp; + uint32_t f_ctl; + uint8_t *p; + + TRACE_ENTRY(); + + TRACE_DBG("Sending task mgmt ABTS response (ha=%p, atio=%p, " + "status=%x", ha, abts, status); + + /* Send marker if required */ + if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + goto out; + + resp = (abts24_resp_entry_t *)tgt_data.req_pkt(ha); + if (resp == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + resp->entry_type = ABTS_RESP_24XX; + resp->entry_count = 1; + resp->nport_handle = abts->nport_handle; + resp->sof_type = abts->sof_type; + resp->exchange_address = abts->exchange_address; + resp->fcp_hdr_le = abts->fcp_hdr_le; + f_ctl = __constant_cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP | + F_CTL_LAST_SEQ | F_CTL_END_SEQ | + F_CTL_SEQ_INITIATIVE); + p = (uint8_t *)&f_ctl; + resp->fcp_hdr_le.f_ctl[0] = *p++; + resp->fcp_hdr_le.f_ctl[1] = *p++; + resp->fcp_hdr_le.f_ctl[2] = *p; + if (ids_reversed) { + resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.d_id[0]; + resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.d_id[1]; + resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.d_id[2]; + resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.s_id[0]; + resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.s_id[1]; + resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.s_id[2]; + } else { + resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.s_id[0]; + resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.s_id[1]; + resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.s_id[2]; + resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.d_id[0]; + resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.d_id[1]; + resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.d_id[2]; + } + resp->exchange_addr_to_abort = abts->exchange_addr_to_abort; + if (status == SCST_MGMT_STATUS_SUCCESS) { + resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC; + resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID; + resp->payload.ba_acct.low_seq_cnt = 0x0000; + resp->payload.ba_acct.high_seq_cnt = 0xFFFF; + resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id; + resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id; + } else { + resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT; + resp->payload.ba_rjt.reason_code = + BA_RJT_REASON_CODE_UNABLE_TO_PERFORM; + /* Other bytes are zero */ + } + + TRACE_BUFFER("ABTS RESP packet data", resp, REQUEST_ENTRY_SIZE); + + ha->tgt->abts_resp_expected++; + + q2t_exec_queue(ha); + +out: + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q24_retry_term_exchange(scsi_qla_host_t *ha, + abts24_resp_fw_entry_t *entry) +{ + ctio7_status1_entry_t *ctio; + + TRACE_ENTRY(); + + TRACE_DBG("Sending retry TERM EXCH CTIO7 (ha=%p)", ha); + + /* Send marker if required */ + if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + goto out; + + ctio = (ctio7_status1_entry_t *)tgt_data.req_pkt(ha); + if (ctio == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + /* + * We've got on entrance firmware's response on by us generated + * ABTS response. So, in it ID fields are reversed. + */ + + ctio->common.entry_type = CTIO_TYPE7; + ctio->common.entry_count = 1; + ctio->common.nport_handle = entry->nport_handle; + ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.initiator_id[0] = entry->fcp_hdr_le.d_id[0]; + ctio->common.initiator_id[1] = entry->fcp_hdr_le.d_id[1]; + ctio->common.initiator_id[2] = entry->fcp_hdr_le.d_id[2]; + ctio->common.exchange_addr = entry->exchange_addr_to_abort; + ctio->flags = __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); + ctio->ox_id = entry->fcp_hdr_le.ox_id; + + TRACE_BUFFER("CTIO7 retry TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); + + q24_send_abts_resp(ha, (abts24_recv_entry_t *)entry, + SCST_MGMT_STATUS_SUCCESS, true); + +out: + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts) +{ + uint32_t tag; + int rc; + struct q2t_mgmt_cmd *mcmd; + struct q2t_sess *sess; + + TRACE_ENTRY(); + + if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) { + PRINT_ERROR("qla2x00tgt(%ld): ABTS: Abort Sequence not " + "supported", ha->instance); + goto out_err; + } + + tag = abts->exchange_addr_to_abort; + + if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) { + TRACE_MGMT_DBG("qla2x00tgt(%ld): ABTS: Unknown Exchange " + "Address received", ha->instance); + goto out_err; + } + + TRACE(TRACE_MGMT_MINOR, "qla2x00tgt(%ld): task abort (s_id=%x:%x:%x, " + "tag=%d, param=%x)", ha->instance, abts->fcp_hdr_le.s_id[0], + abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[2], tag, + le32_to_cpu(abts->fcp_hdr_le.parameter)); + + sess = q2t_find_sess_by_s_id_le(ha->tgt, abts->fcp_hdr_le.s_id); + if (sess == NULL) { + TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task abort for unexisting " + "session", ha->instance); + ha->tgt->tm_to_unknown = 1; + goto out_err; + } + + mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); + if (mcmd == NULL) { + PRINT_ERROR("%s: Allocation of ABORT cmd failed", __func__); + goto out_err; + } + memset(mcmd, 0, sizeof(*mcmd)); + + mcmd->sess = sess; + memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts)); + + rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag, + SCST_ATOMIC, mcmd); + if (rc != 0) { + PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_tag() failed: %d", + ha->instance, rc); + goto out_err_free; + } + +out: + TRACE_EXIT(); + return; + +out_err_free: + mempool_free(mcmd, q2t_mgmt_cmd_mempool); + +out_err: + q24_send_abts_resp(ha, abts, SCST_MGMT_STATUS_REJECTED, false); + goto out; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha, + struct q2t_mgmt_cmd *mcmd, uint32_t resp_code) +{ + const atio7_entry_t *atio = &mcmd->orig_iocb.atio7; + ctio7_status1_entry_t *ctio; + + TRACE_ENTRY(); + + TRACE_DBG("Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x", + ha, atio, resp_code); + + /* Send marker if required */ + if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + goto out; + + ctio = (ctio7_status1_entry_t *)tgt_data.req_pkt(ha); + if (ctio == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + ctio->common.entry_type = CTIO_TYPE7; + ctio->common.entry_count = 1; + ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->common.nport_handle = mcmd->sess->loop_id; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; + ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; + ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; + ctio->common.exchange_addr = atio->exchange_addr; + ctio->flags = (atio->attr << 9) | __constant_cpu_to_le16( + CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS); + ctio->ox_id = swab16(atio->fcp_hdr.ox_id); + ctio->scsi_status = cpu_to_le16(resp_code); + + TRACE_BUFFER("CTIO7 TASK MGMT packet data", ctio, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); + +out: + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q24_send_notify_ack(scsi_qla_host_t *ha, + notify24xx_entry_t *iocb, uint16_t srr_flags, + uint8_t srr_reject_code, uint8_t srr_explan) +{ + nack24xx_entry_t *nack; + + TRACE_ENTRY(); + + TRACE_DBG("Sending NOTIFY_ACK24 (ha=%p)", ha); + + /* Send marker if required */ + if (q2t_issue_marker(ha, 1) != QLA_SUCCESS) + goto out; + + if (ha->tgt != NULL) + ha->tgt->notify_ack_expected++; + + nack = (nack24xx_entry_t *)tgt_data.req_pkt(ha); + if (nack == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + nack->entry_type = NOTIFY_ACK_TYPE; + nack->entry_count = 1; + nack->nport_handle = iocb->nport_handle; + if (le16_to_cpu(iocb->status) == IMM_NTFY_ELS) { + nack->flags = iocb->flags & + __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); + } + nack->srr_rx_id = iocb->srr_rx_id; + nack->status = iocb->status; + nack->status_subcode = iocb->status_subcode; + nack->exchange_address = iocb->exchange_address; + nack->srr_rel_offs = iocb->srr_rel_offs; + nack->srr_ui = iocb->srr_ui; + nack->srr_flags = cpu_to_le16(srr_flags); + nack->srr_reject_code = srr_reject_code; + nack->srr_reject_code_expl = srr_explan; + nack->ox_id = iocb->ox_id; + + TRACE(TRACE_SCSI, "Sending 24xx Notify Ack %d", nack->status); + TRACE_BUFFER("24xx Notify Ack packet data", nack, sizeof(*nack)); + + q2t_exec_queue(ha); + +out: + TRACE_EXIT(); + return; +} + +int q2t_convert_to_fc_tm_status(int scst_mstatus) +{ + int res; + + switch (scst_mstatus) { + case SCST_MGMT_STATUS_SUCCESS: + res = FC_TM_SUCCESS; + break; + case SCST_MGMT_STATUS_TASK_NOT_EXIST: + res = FC_TM_BAD_CMD; + break; + case SCST_MGMT_STATUS_FN_NOT_SUPPORTED: + case SCST_MGMT_STATUS_REJECTED: + res = FC_TM_REJECT; + break; + case SCST_MGMT_STATUS_LUN_NOT_EXIST: + case SCST_MGMT_STATUS_FAILED: + default: + res = FC_TM_FAILED; + break; + } + + TRACE_EXIT_RES(res); + return res; +} + +/* SCST Callback */ +static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) +{ + struct q2t_mgmt_cmd *mcmd; + unsigned long flags; + scsi_qla_host_t *ha; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("scst_mcmd (%p) status %#x state %#x", scst_mcmd, + scst_mcmd->status, scst_mcmd->state); + + mcmd = scst_mgmt_cmd_get_tgt_priv(scst_mcmd); + if (unlikely(mcmd == NULL)) { + PRINT_ERROR("scst_mcmd %p tgt_spec is NULL", mcmd); + goto out; + } + + ha = mcmd->sess->tgt->ha; + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (IS_FWI2_CAPABLE(ha)) { + if (mcmd->flags == Q24_MGMT_SEND_NACK) { + q24_send_notify_ack(ha, + &mcmd->orig_iocb.notify_entry24, 0, 0, 0); + } else { + if (scst_mcmd->fn == SCST_ABORT_TASK) + q24_send_abts_resp(ha, &mcmd->orig_iocb.abts, + scst_mgmt_cmd_get_status(scst_mcmd), + false); + else + q24_send_task_mgmt_ctio(ha, mcmd, + q2t_convert_to_fc_tm_status( + scst_mgmt_cmd_get_status(scst_mcmd))); + } + } else { + int resp_code = q2t_convert_to_fc_tm_status( + scst_mgmt_cmd_get_status(scst_mcmd)); + q2x_send_notify_ack(ha, &mcmd->orig_iocb.notify_entry, 0, + resp_code, 1, 0, 0, 0); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); + mempool_free(mcmd, q2t_mgmt_cmd_mempool); + +out: + TRACE_EXIT(); + return; +} + +/* No locks */ static int q2t_pci_map_calc_cnt(struct q2t_prm *prm) { int res = 0; - sBUG_ON(prm->sg_cnt == 0); + sBUG_ON(prm->cmd->sg_cnt == 0); - /* 32 bit S/G Data Transfer */ - prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, prm->sg, prm->sg_cnt, - scst_to_tgt_dma_dir(prm->data_direction)); + prm->sg = (struct scatterlist *)prm->cmd->sg; + prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, prm->cmd->sg, + prm->cmd->sg_cnt, + scst_to_tgt_dma_dir(prm->cmd->data_direction)); if (unlikely(prm->seg_cnt == 0)) goto out_err; /* @@ -428,156 +1357,214 @@ static int q2t_pci_map_calc_cnt(struct q2t_prm *prm) prm->tgt->datasegs_per_cmd) / prm->tgt->datasegs_per_cont; if (((uint16_t)(prm->seg_cnt - prm->tgt->datasegs_per_cmd)) % - prm->tgt->datasegs_per_cont) { + prm->tgt->datasegs_per_cont) + { prm->req_cnt++; } } out: - TRACE_DBG("seg_cnt=%d, req_cnt=%d, res=%d", prm->seg_cnt, + TRACE_DBG("seg_cnt=%d, req_cnt=%d, res=%d", prm->seg_cnt, prm->req_cnt, res); return res; out_err: - PRINT_ERROR("qla2x00tgt(%ld): PCI mapping failed: sg_cnt=%d", - prm->tgt->ha->host_no, prm->sg_cnt); + PRINT_ERROR("qla2x00tgt(%ld): PCI mapping failed: sg_cnt=%d", + prm->tgt->ha->instance, prm->cmd->sg_cnt); res = -1; goto out; } +static int q2t_check_reserve_free_req(scsi_qla_host_t *ha, uint32_t req_cnt) +{ + int res = SCST_TGT_RES_SUCCESS; + device_reg_t __iomem *reg = ha->iobase; + uint32_t cnt; + + TRACE_ENTRY(); + + if (ha->req_q_cnt < (req_cnt + 2)) { + if (IS_FWI2_CAPABLE(ha)) + cnt = (uint16_t)RD_REG_DWORD( + ®->isp24.req_q_out); + else + cnt = qla2x00_debounce_register( + ISP_REQ_Q_OUT(ha, ®->isp)); + TRACE_DBG("Request ring circled: cnt=%d, " + "ha->req_ring_index=%d, ha->req_q_cnt=%d, req_cnt=%d", + cnt, ha->req_ring_index, ha->req_q_cnt, req_cnt); + if (ha->req_ring_index < cnt) + ha->req_q_cnt = cnt - ha->req_ring_index; + else + ha->req_q_cnt = ha->request_q_length - + (ha->req_ring_index - cnt); + } + + if (unlikely(ha->req_q_cnt < (req_cnt + 2))) { + TRACE(TRACE_OUT_OF_MEM, "There is no room in the request ring: " + "ha->req_ring_index=%d, ha->req_q_cnt=%d, req_cnt=%d", + ha->req_ring_index, ha->req_q_cnt, req_cnt); + res = SCST_TGT_RES_QUEUE_FULL; + goto out; + } + + ha->req_q_cnt -= req_cnt; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static inline void *q2t_get_req_pkt(scsi_qla_host_t *ha) +{ + /* Adjust ring index. */ + ha->req_ring_index++; + if (ha->req_ring_index == ha->request_q_length) { + ha->req_ring_index = 0; + ha->request_ring_ptr = ha->request_ring; + } else { + ha->request_ring_ptr++; + } + return (cont_entry_t *)ha->request_ring_ptr; +} + /* ha->hardware_lock supposed to be held on entry */ static inline uint32_t q2t_make_handle(scsi_qla_host_t *ha) { uint32_t h; - h = ha->current_cmd; + h = ha->current_handle; /* always increment cmd handle */ do { ++h; - if (h > MAX_OUTSTANDING_COMMANDS) - h = 0; - - if (h == ha->current_cmd) { - TRACE(TRACE_OUT_OF_MEM, "Ran out of empty cmd slots " - "in ha %p", ha); + if (h > MAX_OUTSTANDING_COMMANDS) { + h = 1; /* 0 is Q2T_NULL_HANDLE */ + } + if (h == ha->current_handle) { + TRACE(TRACE_OUT_OF_MEM, + "Ran out of empty cmd slots in ha %p", ha); h = Q2T_NULL_HANDLE; break; } } while ((h == Q2T_NULL_HANDLE) || - (h == Q2T_BUSY_HANDLE) || - (h == Q2T_SKIP_HANDLE) || - (ha->cmds[h] != NULL)); + (h == Q2T_SKIP_HANDLE) || + (ha->cmds[h-1] != NULL)); if (h != Q2T_NULL_HANDLE) - ha->current_cmd = h; + ha->current_handle = h; return h; } /* ha->hardware_lock supposed to be held on entry */ -/* - * NOTE: About CTIO_COMPLETION_HANDLE - * This is checked for in qla2x00_process_response_queue() to see - * if a handle coming back in a multi-complete should come to the tgt driver - * or be handled there by qla2xxx - */ -static void q2t_build_ctio_pkt(struct q2t_prm *prm) +static void q2x_build_ctio_pkt(struct q2t_prm *prm) { - uint16_t timeout; uint32_t h; + ctio_entry_t *pkt; + scsi_qla_host_t *ha = prm->tgt->ha; - prm->pkt = (struct ctio_common_entry *)tgt_data.req_pkt(prm->tgt->ha); + pkt = (ctio_entry_t *)ha->request_ring_ptr; + prm->pkt = pkt; + memset(pkt, 0, sizeof(*pkt)); if (prm->tgt->tgt_enable_64bit_addr) - prm->pkt->entry_type = CTIO_A64_TYPE; + pkt->common.entry_type = CTIO_A64_TYPE; else - prm->pkt->entry_type = CONTINUE_TGT_IO_TYPE; + pkt->common.entry_type = CONTINUE_TGT_IO_TYPE; - prm->pkt->entry_count = (uint8_t) prm->req_cnt; + pkt->common.entry_count = (uint8_t)prm->req_cnt; - h = q2t_make_handle(prm->tgt->ha); + h = q2t_make_handle(ha); if (h != Q2T_NULL_HANDLE) - prm->tgt->ha->cmds[h] = prm->cmd; - prm->pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK; + ha->cmds[h-1] = prm->cmd; - timeout = Q2T_TIMEOUT; - prm->pkt->timeout = cpu_to_le16(timeout); + pkt->common.handle = h | CTIO_COMPLETION_HANDLE_MARK; + pkt->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); /* Set initiator ID */ - h = GET_TARGET_ID(prm->tgt->ha, &prm->cmd->atio); - SET_TARGET_ID(prm->tgt->ha, prm->pkt->target, h); + h = GET_TARGET_ID(ha, &prm->cmd->atio.atio2x); + SET_TARGET_ID(ha, pkt->common.target, h); + + pkt->common.rx_id = prm->cmd->atio.atio2x.rx_id; + pkt->common.relative_offset = cpu_to_le32(prm->cmd->offset); - prm->pkt->exchange_id = prm->cmd->atio.exchange_id; - - TRACE(TRACE_DEBUG|TRACE_SCSI, + TRACE(TRACE_DEBUG|TRACE_SCSI, "handle(scst_cmd) -> %08x, timeout %d L %#x -> I %#x E %#x", - prm->pkt->handle, timeout, le16_to_cpu(prm->cmd->atio.lun), - GET_TARGET_ID(prm->tgt->ha, prm->pkt), - le16_to_cpu(prm->pkt->exchange_id)); - + pkt->common.handle, Q2T_TIMEOUT, + le16_to_cpu(prm->cmd->atio.atio2x.lun), + GET_TARGET_ID(ha, &pkt->common), pkt->common.rx_id); } -static void q2t_load_data_segments(struct q2t_prm *prm) +/* ha->hardware_lock supposed to be held on entry */ +static int q24_build_ctio_pkt(struct q2t_prm *prm) { - uint32_t cnt; + uint32_t h; + ctio7_status0_entry_t *pkt; + scsi_qla_host_t *ha = prm->tgt->ha; + atio7_entry_t *atio = &prm->cmd->atio.atio7; + int res = SCST_TGT_RES_SUCCESS; + + TRACE_ENTRY(); + + pkt = (ctio7_status0_entry_t *)ha->request_ring_ptr; + prm->pkt = pkt; + memset(pkt, 0, sizeof(*pkt)); + + pkt->common.entry_type = CTIO_TYPE7; + pkt->common.entry_count = (uint8_t)prm->req_cnt; + + h = q2t_make_handle(ha); + if (unlikely(h == Q2T_NULL_HANDLE)) { + /* + * CTIO type 7 from the firmware doesn't provide a way to + * know the initiator's LOOP ID, hence we can't find + * the session and, so, the command. + */ + res = SCST_TGT_RES_QUEUE_FULL; + goto out; + } else + ha->cmds[h-1] = prm->cmd; + + pkt->common.handle = h | CTIO_COMPLETION_HANDLE_MARK; + pkt->common.nport_handle = prm->cmd->loop_id; + pkt->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + pkt->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; + pkt->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; + pkt->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; + pkt->common.exchange_addr = atio->exchange_addr; + pkt->flags |= (atio->attr << 9); + pkt->ox_id = swab16(atio->fcp_hdr.ox_id); + pkt->relative_offset = cpu_to_le32(prm->cmd->offset); + +out: + TRACE(TRACE_DEBUG|TRACE_SCSI, "handle(scst_cmd) -> %08x, timeout %d " + "ox_id %#x", pkt->common.handle, Q2T_TIMEOUT, + le16_to_cpu(pkt->ox_id)); + TRACE_EXIT_RES(res); + return res; +} + +/* + * ha->hardware_lock supposed to be held on entry. We have already made sure + * that there is sufficient amount of request entries to not drop it. + */ +static void q2t_load_cont_data_segments(struct q2t_prm *prm) +{ + int cnt; uint32_t *dword_ptr; int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; - TRACE_DBG("iocb->scsi_status=%x, iocb->flags=%x", - le16_to_cpu(prm->pkt->scsi_status), le16_to_cpu(prm->pkt->flags)); - - prm->pkt->transfer_length = cpu_to_le32(prm->bufflen); - - /* Setup packet address segment pointer */ - dword_ptr = prm->pkt->dseg_0_address; - - if (prm->seg_cnt == 0) { - /* No data transfer */ - *dword_ptr++ = 0; - *dword_ptr = 0; - - TRACE_BUFFER("No data, CTIO packet data", - prm->pkt, REQUEST_ENTRY_SIZE); - goto out; - } - - /* Set total data segment count */ - prm->pkt->dseg_count = cpu_to_le16(prm->seg_cnt); - - /* If scatter gather */ - TRACE_SG("%s", "Building S/G data segments..."); - /* Load command entry data segments */ - for (cnt = 0; - (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt; - cnt++, prm->seg_cnt--) { - *dword_ptr++ = - cpu_to_le32(pci_dma_lo32(sg_dma_address(prm->sg))); - if (enable_64bit_addressing) { - *dword_ptr++ = - cpu_to_le32(pci_dma_hi32 - (sg_dma_address(prm->sg))); - } - *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); - - TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d", - (long long unsigned int)pci_dma_hi32( - sg_dma_address(prm->sg)), - (long long unsigned int)pci_dma_lo32( - sg_dma_address(prm->sg)), - (int)sg_dma_len(prm->sg)); - - prm->sg++; - } - - TRACE_BUFFER("Scatter/gather, CTIO packet data", - prm->pkt, REQUEST_ENTRY_SIZE); + TRACE_ENTRY(); /* Build continuation packets */ while (prm->seg_cnt > 0) { cont_a64_entry_t *cont_pkt64 = - (cont_a64_entry_t *)tgt_data.req_cont_pkt(prm->tgt->ha); + (cont_a64_entry_t *)q2t_get_req_pkt(prm->tgt->ha); - /* + /* * Make sure that from cont_pkt64 none of * 64-bit specific fields used for 32-bit * addressing. Cast to (cont_entry_t *) for @@ -603,7 +1590,8 @@ static void q2t_load_data_segments(struct q2t_prm *prm) /* Load continuation entry data segments */ for (cnt = 0; cnt < prm->tgt->datasegs_per_cont && prm->seg_cnt; - cnt++, prm->seg_cnt--) { + cnt++, prm->seg_cnt--) + { *dword_ptr++ = cpu_to_le32(pci_dma_lo32 (sg_dma_address(prm->sg))); @@ -615,12 +1603,10 @@ static void q2t_load_data_segments(struct q2t_prm *prm) } *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); - TRACE_SG("S/G Cont. phys_addr=%llx:%llx, len=%d", - (long long unsigned int)pci_dma_hi32( - sg_dma_address(prm->sg)), - (long long unsigned int)pci_dma_lo32( - sg_dma_address(prm->sg)), - (int)sg_dma_len(prm->sg)); + TRACE_SG("S/G Segment Cont. phys_addr=%llx:%llx, len=%d", + (long long unsigned int)pci_dma_hi32(sg_dma_address(prm->sg)), + (long long unsigned int)pci_dma_lo32(sg_dma_address(prm->sg)), + (int)sg_dma_len(prm->sg)); prm->sg++; } @@ -629,26 +1615,317 @@ static void q2t_load_data_segments(struct q2t_prm *prm) cont_pkt64, REQUEST_ENTRY_SIZE); } + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. We have already made sure + * that there is sufficient amount of request entries to not drop it. + */ +static void q2x_load_data_segments(struct q2t_prm *prm) +{ + int cnt; + uint32_t *dword_ptr; + int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + ctio_common_entry_t *pkt = (ctio_common_entry_t *)prm->pkt; + + TRACE_DBG("iocb->scsi_status=%x, iocb->flags=%x", + le16_to_cpu(pkt->scsi_status), le16_to_cpu(pkt->flags)); + + pkt->transfer_length = cpu_to_le32(prm->cmd->bufflen); + + /* Setup packet address segment pointer */ + dword_ptr = pkt->dseg_0_address; + + if (prm->seg_cnt == 0) { + /* No data transfer */ + *dword_ptr++ = 0; + *dword_ptr = 0; + + TRACE_BUFFER("No data, CTIO packet data", pkt, + REQUEST_ENTRY_SIZE); + goto out; + } + + /* Set total data segment count */ + pkt->dseg_count = cpu_to_le16(prm->seg_cnt); + + /* If scatter gather */ + TRACE_SG("%s", "Building S/G data segments..."); + /* Load command entry data segments */ + for (cnt = 0; + (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt; + cnt++, prm->seg_cnt--) + { + *dword_ptr++ = + cpu_to_le32(pci_dma_lo32(sg_dma_address(prm->sg))); + if (enable_64bit_addressing) { + *dword_ptr++ = + cpu_to_le32(pci_dma_hi32 + (sg_dma_address(prm->sg))); + } + *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); + + TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d", + (long long unsigned int)pci_dma_hi32(sg_dma_address(prm->sg)), + (long long unsigned int)pci_dma_lo32(sg_dma_address(prm->sg)), + (int)sg_dma_len(prm->sg)); + + prm->sg++; + } + + TRACE_BUFFER("Scatter/gather, CTIO packet data", pkt, + REQUEST_ENTRY_SIZE); + + q2t_load_cont_data_segments(prm); + out: return; } -static void q2t_init_ctio_ret_entry(struct ctio_ret_entry *ctio_m1, +/* + * ha->hardware_lock supposed to be held on entry. We have already made sure + * that there is sufficient amount of request entries to not drop it. + */ +static void q24_load_data_segments(struct q2t_prm *prm) +{ + int cnt; + uint32_t *dword_ptr; + int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + ctio7_status0_entry_t *pkt = (ctio7_status0_entry_t *)prm->pkt; + + TRACE_DBG("iocb->scsi_status=%x, iocb->flags=%x", + le16_to_cpu(pkt->scsi_status), le16_to_cpu(pkt->flags)); + + pkt->transfer_length = cpu_to_le32(prm->cmd->bufflen); + + /* Setup packet address segment pointer */ + dword_ptr = pkt->dseg_0_address; + + if (prm->seg_cnt == 0) { + /* No data transfer */ + *dword_ptr++ = 0; + *dword_ptr = 0; + + TRACE_BUFFER("No data, CTIO7 packet data", pkt, + REQUEST_ENTRY_SIZE); + goto out; + } + + /* Set total data segment count */ + pkt->common.dseg_count = cpu_to_le16(prm->seg_cnt); + + /* If scatter gather */ + TRACE_SG("%s", "Building S/G data segments..."); + /* Load command entry data segments */ + for (cnt = 0; + (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt; + cnt++, prm->seg_cnt--) + { + *dword_ptr++ = + cpu_to_le32(pci_dma_lo32(sg_dma_address(prm->sg))); + if (enable_64bit_addressing) { + *dword_ptr++ = + cpu_to_le32(pci_dma_hi32( + sg_dma_address(prm->sg))); + } + *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); + + TRACE_SG("S/G Segment phys_addr=%llx:%llx, len=%d", + (long long unsigned int)pci_dma_hi32(sg_dma_address( + prm->sg)), + (long long unsigned int)pci_dma_lo32(sg_dma_address( + prm->sg)), + (int)sg_dma_len(prm->sg)); + + prm->sg++; + } + + q2t_load_cont_data_segments(prm); + +out: + return; +} + +static inline int q2t_has_data(struct q2t_cmd *cmd) +{ + return cmd->bufflen > 0; +} + +static int q2t_pre_xmit_response(struct q2t_cmd *cmd, + struct q2t_prm *prm, int xmit_type, unsigned long *flags) +{ + int res; + struct q2t_tgt *tgt = cmd->tgt; + scsi_qla_host_t *ha; + uint16_t full_req_cnt; + struct scst_cmd *scst_cmd = cmd->scst_cmd; + + TRACE_ENTRY(); + + if (unlikely(cmd->aborted)) { + scsi_qla_host_t *ha = tgt->ha; + + TRACE(TRACE_MGMT_MINOR, "qla2x00tgt(%ld): terminating exchange " + "for aborted cmd=%p (scst_cmd=%p, tag=%d)", + ha->instance, cmd, scst_cmd, cmd->tag); + + cmd->state = Q2T_STATE_ABORTED; + scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_ABORTED); + + if (IS_FWI2_CAPABLE(ha)) + q24_send_term_exchange(ha, cmd, &cmd->atio.atio7, 0); + else + q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 0); + /* !! At this point cmd could be already freed !! */ + res = Q2T_PRE_XMIT_RESP_CMD_ABORTED; + goto out; + } + + TRACE(TRACE_SCSI, "tag=%Ld", scst_cmd_get_tag(scst_cmd)); + + prm->cmd = cmd; + prm->tgt = tgt; + prm->rq_result = scst_cmd_get_status(scst_cmd); + prm->sense_buffer = scst_cmd_get_sense_buffer(scst_cmd); + prm->sense_buffer_len = scst_cmd_get_sense_buffer_len(scst_cmd); + prm->sg = NULL; + prm->seg_cnt = -1; + prm->req_cnt = 1; + prm->add_status_pkt = 0; + ha = tgt->ha; + + TRACE_DBG("rq_result=%x, xmit_type=%x", prm->rq_result, xmit_type); + if (prm->rq_result != 0) + TRACE_BUFFER("Sense", prm->sense_buffer, prm->sense_buffer_len); + + /* Send marker if required */ + if (q2t_issue_marker(ha, 0) != QLA_SUCCESS) { + res = SCST_TGT_RES_FATAL_ERROR; + goto out; + } + + TRACE_DBG("CTIO start: ha(%d)", (int)ha->instance); + + if ((xmit_type & Q2T_XMIT_DATA) && q2t_has_data(cmd)) { + if (q2t_pci_map_calc_cnt(prm) != 0) { + res = SCST_TGT_RES_QUEUE_FULL; + goto out; + } + } + + full_req_cnt = prm->req_cnt; + + if (xmit_type & Q2T_XMIT_STATUS) { + if (cmd->data_direction != SCST_DATA_WRITE) { + int expected; + if (IS_FWI2_CAPABLE(ha)) + expected = be32_to_cpu(cmd-> + atio.atio7.fcp_cmnd.data_length); + else + expected = le32_to_cpu(cmd-> + atio.atio2x.data_length); + prm->residual = expected - + scst_cmd_get_resp_data_len(scst_cmd); + if (prm->residual > 0) { + TRACE_DBG("Residual underflow: %d (tag %Ld, " + "op %x, expected %d, resp_data_len " + "%d, bufflen %d, rq_result %x)", + prm->residual, scst_cmd->tag, + scst_cmd->cdb[0], expected, + scst_cmd_get_resp_data_len(scst_cmd), + cmd->bufflen, prm->rq_result); + prm->rq_result |= SS_RESIDUAL_UNDER; + } else if (prm->residual < 0) { + TRACE_DBG("Residual overflow: %d (tag %Ld, " + "op %x, expected %d, resp_data_len " + "%d, bufflen %d, rq_result %x)", + prm->residual, scst_cmd->tag, + scst_cmd->cdb[0], expected, + scst_cmd_get_resp_data_len(scst_cmd), + cmd->bufflen, prm->rq_result); + prm->rq_result |= SS_RESIDUAL_OVER; + prm->residual = -prm->residual; + } + } + + /* + * If Q2T_XMIT_DATA is not set, add_status_pkt will be ignored + * in *xmit_response() below + */ + if (q2t_has_data(cmd)) { + if (SCST_SENSE_VALID(prm->sense_buffer) || + (IS_FWI2_CAPABLE(ha) && + (prm->rq_result != 0))) { + prm->add_status_pkt = 1; + full_req_cnt++; + } + } + } + + TRACE_DBG("req_cnt=%d, full_req_cnt=%d, add_status_pkt=%d", + prm->req_cnt, full_req_cnt, prm->add_status_pkt); + + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, *flags); + + /* Does F/W have an IOCBs for this request */ + res = q2t_check_reserve_free_req(ha, full_req_cnt); + if (unlikely(res != SCST_TGT_RES_SUCCESS) && + (xmit_type & Q2T_XMIT_DATA)) + goto out_unlock_free_unmap; + +out: + TRACE_EXIT_RES(res); + return res; + +out_unlock_free_unmap: + if (q2t_has_data(cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + } + /* Release ring specific lock */ + spin_unlock_irqrestore(&ha->hardware_lock, *flags); + goto out; +} + +static inline int q2t_need_explicit_conf(scsi_qla_host_t *ha, + struct q2t_cmd *cmd, int sending_sense) +{ + if (ha->enable_class_2) + return 0; + + if (sending_sense) + return cmd->conf_compl_supported; + else + return ha->enable_explicit_conf && cmd->conf_compl_supported; +} + +static void q2x_init_ctio_ret_entry(ctio_ret_entry_t *ctio_m1, struct q2t_prm *prm) { TRACE_ENTRY(); - prm->sense_buffer_len = min((uint32_t)prm->sense_buffer_len, + prm->sense_buffer_len = min((uint32_t)prm->sense_buffer_len, (uint32_t)sizeof(ctio_m1->sense_data)); ctio_m1->flags = __constant_cpu_to_le16(OF_SSTS | OF_FAST_POST | - OF_NO_DATA | OF_SS_MODE_1); + OF_NO_DATA | OF_SS_MODE_1); ctio_m1->flags |= __constant_cpu_to_le16(OF_INC_RC); + if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) { + ctio_m1->flags |= __constant_cpu_to_le16(OF_EXPL_CONF | + OF_CONF_REQ); + } ctio_m1->scsi_status = cpu_to_le16(prm->rq_result); ctio_m1->residual = cpu_to_le32(prm->residual); if (SCST_SENSE_VALID(prm->sense_buffer)) { - ctio_m1->scsi_status |= - __constant_cpu_to_le16(SS_SENSE_LEN_VALID); + if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) { + ctio_m1->flags |= __constant_cpu_to_le16(OF_EXPL_CONF | + OF_CONF_REQ); + } + ctio_m1->scsi_status |= __constant_cpu_to_le16( + SS_SENSE_LEN_VALID); ctio_m1->sense_length = cpu_to_le16(prm->sense_buffer_len); memcpy(ctio_m1->sense_data, prm->sense_buffer, prm->sense_buffer_len); @@ -657,296 +1934,508 @@ static void q2t_init_ctio_ret_entry(struct ctio_ret_entry *ctio_m1, ctio_m1->sense_length = 0; } - TRACE_BUFFER("CTIO returned packet data", ctio_m1, REQUEST_ENTRY_SIZE); - /* Sense with len > 26, is it possible ??? */ TRACE_EXIT(); return; } -static inline int q2t_has_data(struct scst_cmd *scst_cmd) +static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type) { - return scst_cmd_get_resp_data_len(scst_cmd) > 0; -} - -static int q2t_xmit_response(struct scst_cmd *scst_cmd) -{ - int res = SCST_TGT_RES_SUCCESS; - struct q2t_sess *sess; - int is_send_status; - unsigned long flags = 0; + int res; + unsigned long flags; + scsi_qla_host_t *ha; struct q2t_prm prm; - int data_sense_flag = 0; - uint16_t full_req_cnt; + ctio_common_entry_t *pkt; TRACE_ENTRY(); - TRACE(TRACE_SCSI, "tag=%lld", scst_cmd_get_tag(scst_cmd)); - -#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD - if (scst_cmd_atomic(scst_cmd)) - return SCST_TGT_RES_NEED_THREAD_CTX; -#endif memset(&prm, 0, sizeof(prm)); - prm.cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); - sess = (struct q2t_sess *) - scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd)); - - if (unlikely(scst_cmd_aborted(scst_cmd))) { - scsi_qla_host_t *ha = sess->tgt->ha; - - TRACE(TRACE_MGMT_MINOR, "qla2x00tgt(%ld): terminating exchange " - "for aborted scst_cmd=%p (tag=%lld)", - ha->host_no, scst_cmd, scst_cmd_get_tag(scst_cmd)); - - scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_ABORTED); - - prm.cmd->state = Q2T_STATE_ABORTED; - - q2t_send_term_exchange(ha, prm.cmd, &prm.cmd->atio, 0); - /* !! At this point cmd could be already freed !! */ + res = q2t_pre_xmit_response(cmd, &prm, xmit_type, &flags); + if (unlikely(res != SCST_TGT_RES_SUCCESS)) { + if (res == Q2T_PRE_XMIT_RESP_CMD_ABORTED) + res = SCST_TGT_RES_SUCCESS; goto out; } - prm.sg = scst_cmd_get_sg(scst_cmd); - prm.bufflen = scst_cmd_get_resp_data_len(scst_cmd); - prm.sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); - prm.data_direction = scst_cmd_get_data_direction(scst_cmd); - prm.rq_result = scst_cmd_get_status(scst_cmd); - prm.sense_buffer = scst_cmd_get_sense_buffer(scst_cmd); - prm.sense_buffer_len = scst_cmd_get_sense_buffer_len(scst_cmd); - prm.tgt = sess->tgt; - prm.seg_cnt = 0; - prm.req_cnt = 1; - is_send_status = scst_cmd_get_is_send_status(scst_cmd); + /* Here ha->hardware_lock already locked */ - TRACE_DBG("rq_result=%x, is_send_status=%x", prm.rq_result, - is_send_status); + ha = prm.tgt->ha; - if (prm.rq_result != 0) - TRACE_BUFFER("Sense", prm.sense_buffer, prm.sense_buffer_len); + q2x_build_ctio_pkt(&prm); + pkt = (ctio_common_entry_t *)prm.pkt; - if (!is_send_status) { - /* ToDo, after it's done in SCST */ - PRINT_ERROR("qla2x00tgt(%ld): is_send_status not set: " - "feature not implemented", prm.tgt->ha->host_no); - res = SCST_TGT_RES_FATAL_ERROR; - goto out; - } + if (q2t_has_data(cmd) && (xmit_type & Q2T_XMIT_DATA)) { + pkt->flags |= __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_IN); + pkt->flags |= __constant_cpu_to_le16(OF_INC_RC); - /* Acquire ring specific lock */ - spin_lock_irqsave(&prm.tgt->ha->hardware_lock, flags); + q2x_load_data_segments(&prm); - /* Send marker if required */ - if (tgt_data.issue_marker(prm.tgt->ha) != QLA_SUCCESS) { - PRINT_ERROR("qla2x00tgt(%ld): __QLA2X00_MARKER() " - "failed", prm.tgt->ha->host_no); - res = SCST_TGT_RES_FATAL_ERROR; - goto out_unlock; - } - - TRACE_DBG("CTIO start: ha(%d)", (int) prm.tgt->ha->host_no); - - if (q2t_has_data(scst_cmd)) { - if (q2t_pci_map_calc_cnt(&prm) != 0) { - res = SCST_TGT_RES_QUEUE_FULL; - goto out_unlock; - } - full_req_cnt = prm.req_cnt; - if (SCST_SENSE_VALID(prm.sense_buffer)) { - data_sense_flag = 1; - full_req_cnt++; - } - } else - full_req_cnt = prm.req_cnt; - - q2t_build_ctio_pkt(&prm); - - if (prm.data_direction != SCST_DATA_WRITE) { - prm.residual = - le32_to_cpu(prm.cmd->atio.data_length) - prm.bufflen; - if (prm.residual > 0) { - TRACE_DBG("Residual underflow: %d", prm.residual); - prm.rq_result |= SS_RESIDUAL_UNDER; - } else if (prm.residual < 0) { - TRACE_DBG("Residual overflow: %d", prm.residual); - prm.rq_result |= SS_RESIDUAL_OVER; - prm.residual = -prm.residual; - } - - if (q2t_has_data(scst_cmd)) { - prm.pkt->flags |= __constant_cpu_to_le16( - OF_FAST_POST | OF_INC_RC | OF_DATA_IN); - - q2t_load_data_segments(&prm); - - if (data_sense_flag == 0) { - prm.pkt->scsi_status = cpu_to_le16( - prm.rq_result); - prm.pkt->residual = cpu_to_le32(prm.residual); - prm.pkt->flags |= - __constant_cpu_to_le16(OF_SSTS); - } else { - struct ctio_ret_entry *ctio_m1 = - (struct ctio_ret_entry *) - tgt_data.req_cont_pkt(prm.tgt->ha); - - TRACE_DBG("%s", "Building additional status " - "packet"); - - memcpy(ctio_m1, prm.pkt, sizeof(*ctio_m1)); - ctio_m1->entry_count = 1; - - /* Real finish is ctio_m1's finish */ - prm.pkt->handle = Q2T_SKIP_HANDLE | - CTIO_COMPLETION_HANDLE_MARK; - - prm.pkt->flags &= ~__constant_cpu_to_le16(OF_INC_RC); - - q2t_init_ctio_ret_entry(ctio_m1, &prm); - TRACE_BUFFER("Status CTIO packet data", ctio_m1, - REQUEST_ENTRY_SIZE); + if (prm.add_status_pkt == 0) { + if (xmit_type & Q2T_XMIT_STATUS) { + pkt->scsi_status = cpu_to_le16(prm.rq_result); + pkt->residual = cpu_to_le32(prm.residual); + pkt->flags |= __constant_cpu_to_le16(OF_SSTS); + if (q2t_need_explicit_conf(ha, cmd, 0)) { + pkt->flags |= __constant_cpu_to_le16( + OF_EXPL_CONF | + OF_CONF_REQ); + } } - } else - q2t_init_ctio_ret_entry( - (struct ctio_ret_entry *)prm.pkt, &prm); + } else { + /* + * We have already made sure that there is sufficient + * amount of request entries to not drop HW lock in + * req_pkt(). + */ + ctio_ret_entry_t *ctio_m1 = + (ctio_ret_entry_t *)q2t_get_req_pkt(ha); + + TRACE_DBG("%s", "Building additional status packet"); + + memcpy(ctio_m1, pkt, sizeof(*ctio_m1)); + ctio_m1->entry_count = 1; + ctio_m1->dseg_count = 0; + + /* Real finish is ctio_m1's finish */ + pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK; + pkt->flags &= ~__constant_cpu_to_le16(OF_INC_RC); + + q2x_init_ctio_ret_entry(ctio_m1, &prm); + TRACE_BUFFER("Status CTIO packet data", ctio_m1, + REQUEST_ENTRY_SIZE); + } } else - q2t_init_ctio_ret_entry((struct ctio_ret_entry *)prm.pkt, - &prm); + q2x_init_ctio_ret_entry((ctio_ret_entry_t *)pkt, &prm); - /* Mid-level is done processing */ - prm.cmd->state = Q2T_STATE_PROCESSED; + cmd->state = Q2T_STATE_PROCESSED; /* Mid-level is done processing */ - TRACE_BUFFER("Xmitting", prm.pkt, REQUEST_ENTRY_SIZE); + TRACE_BUFFER("Xmitting", pkt, REQUEST_ENTRY_SIZE); - q2t_exec_queue(prm.tgt->ha); + q2t_exec_queue(ha); -out_unlock: /* Release ring specific lock */ - spin_unlock_irqrestore(&prm.tgt->ha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); out: TRACE_EXIT_RES(res); return res; } -static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd) +#ifdef CONFIG_QLA_TGT_DEBUG_SRR +static void q2t_check_srr_debug(struct q2t_cmd *cmd, int *xmit_type) { - int res = SCST_TGT_RES_SUCCESS; - struct q2t_sess *sess; - unsigned long flags = 0; - struct q2t_prm prm; +#if 0 /* This is not a real status packets lost, so it won't lead to SRR */ + if ((*xmit_type & Q2T_XMIT_STATUS) && (scst_random() % 200) == 50) { + *xmit_type &= ~Q2T_XMIT_STATUS; + TRACE_MGMT_DBG("Dropping cmd %p (tag %d) status", cmd, + cmd->tag); + } +#endif - TRACE_ENTRY(); - TRACE(TRACE_SCSI, "tag=%lld", scst_cmd_get_tag(scst_cmd)); + if (q2t_has_data(cmd) && (cmd->sg_cnt > 1) && + ((scst_random() % 100) == 20)) { + int i, leave = 0; + unsigned int tot_len = 0; + + while(leave == 0) + leave = scst_random() % cmd->sg_cnt; + + for(i = 0; i < leave; i++) + tot_len += cmd->sg[i].length; + + TRACE_MGMT_DBG("Cutting cmd %p (tag %d) buffer tail to len %d, " + "sg_cnt %d (cmd->bufflen %d, cmd->sg_cnt %d)", cmd, + cmd->tag, tot_len, leave, cmd->bufflen, cmd->sg_cnt); + + cmd->bufflen = tot_len; + cmd->sg_cnt = leave; + } + + if (q2t_has_data(cmd) && ((scst_random() % 100) == 70)) { + unsigned int offset = scst_random() % cmd->bufflen; + + TRACE_MGMT_DBG("Cutting cmd %p (tag %d) buffer head " + "to offset %d (cmd->bufflen %d)", cmd, cmd->tag, + offset, cmd->bufflen); + if (offset == 0) + *xmit_type &= ~Q2T_XMIT_DATA; + else if (q2t_cut_cmd_data_head(cmd, offset)) { + TRACE_MGMT_DBG("q2t_cut_cmd_data_head() failed (tag %d)", + cmd->tag); + } + } +} +#else +static inline void q2t_check_srr_debug(struct q2t_cmd *cmd, int *xmit_type) {} +#endif + +static int q2x_xmit_response(struct scst_cmd *scst_cmd) +{ + int xmit_type = Q2T_XMIT_DATA; + int is_send_status = scst_cmd_get_is_send_status(scst_cmd); + struct q2t_cmd *cmd = (struct q2t_cmd*)scst_cmd_get_tgt_priv(scst_cmd); + +#ifdef CONFIG_SCST_EXTRACHECKS + sBUG_ON(!q2t_has_data(cmd) && !is_send_status); +#endif #ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD if (scst_cmd_atomic(scst_cmd)) return SCST_TGT_RES_NEED_THREAD_CTX; #endif - memset(&prm, 0, sizeof(prm)); + if (is_send_status) + xmit_type |= Q2T_XMIT_STATUS; - prm.cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); - sess = (struct q2t_sess *) - scst_sess_get_tgt_priv(scst_cmd_get_session(scst_cmd)); + cmd->bufflen = scst_cmd_get_resp_data_len(scst_cmd); + cmd->sg = scst_cmd_get_sg(scst_cmd); + cmd->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); + cmd->data_direction = scst_cmd_get_data_direction(scst_cmd); + cmd->offset = scst_cmd_get_ppl_offset(scst_cmd); + cmd->aborted = scst_cmd_aborted(scst_cmd); - prm.sg = scst_cmd_get_sg(scst_cmd); - prm.bufflen = scst_cmd_get_bufflen(scst_cmd); - prm.sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); - prm.data_direction = scst_cmd_get_data_direction(scst_cmd); - prm.tgt = sess->tgt; - prm.req_cnt = 1; + q2t_check_srr_debug(cmd, &xmit_type); - /* Acquire ring specific lock */ - spin_lock_irqsave(&prm.tgt->ha->hardware_lock, flags); + TRACE_DBG("is_send_status=%x, cmd->bufflen=%d, cmd->sg_cnt=%d, " + "cmd->data_direction=%d", is_send_status, cmd->bufflen, + cmd->sg_cnt, cmd->data_direction); - /* Send marker if required */ - if (tgt_data.issue_marker(prm.tgt->ha) != QLA_SUCCESS) { - PRINT_ERROR("qla2x00tgt(%ld): __QLA2X00_MARKER() " - "failed", prm.tgt->ha->host_no); - res = SCST_TGT_RES_FATAL_ERROR; - goto out_unlock; + return __q2x_xmit_response(cmd, xmit_type); +} + +static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio, + struct q2t_prm *prm) +{ + ctio7_status1_entry_t *ctio1; + + TRACE_ENTRY(); + + prm->sense_buffer_len = min((uint32_t)prm->sense_buffer_len, + (uint32_t)sizeof(ctio1->sense_data)); + ctio->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_SEND_STATUS); + if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) { + ctio->flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_EXPLICIT_CONFORM | + CTIO7_FLAGS_CONFORM_REQ); + } + ctio->residual = cpu_to_le32(prm->residual); + ctio->scsi_status = cpu_to_le16(prm->rq_result); + if (SCST_SENSE_VALID(prm->sense_buffer)) { + int i; + ctio1 = (ctio7_status1_entry_t *)ctio; + if (q2t_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) { + ctio1->flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_EXPLICIT_CONFORM | + CTIO7_FLAGS_CONFORM_REQ); + } + ctio1->flags &= ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ctio1->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + ctio1->scsi_status |= __constant_cpu_to_le16(SS_SENSE_LEN_VALID); + ctio1->sense_length = cpu_to_le16(prm->sense_buffer_len); + for(i = 0; i < prm->sense_buffer_len/4; i++) + ((uint32_t *)ctio1->sense_data)[i] = + cpu_to_be32(((uint32_t *)prm->sense_buffer)[i]); +#if 0 + if (unlikely((prm->sense_buffer_len % 4) != 0)) { + static int q; + if (q < 10) { + PRINT_INFO("qla2x00tgt(%ld): %d bytes of sense " + "lost", prm->tgt->ha->instance, + prm->sense_buffer_len % 4); + q++; + } + } +#endif + } else { + ctio1 = (ctio7_status1_entry_t *)ctio; + ctio1->flags &= ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ctio1->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + ctio1->sense_length = 0; + memset(ctio1->sense_data, 0, sizeof(ctio1->sense_data)); } - TRACE_DBG("CTIO_start: ha(%d)", (int) prm.tgt->ha->host_no); + /* Sense with len > 24, is it possible ??? */ + + TRACE_EXIT(); + return; +} + +static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type) +{ + int res; + unsigned long flags; + scsi_qla_host_t *ha; + struct q2t_prm prm; + ctio7_status0_entry_t *pkt; + + TRACE_ENTRY(); + + memset(&prm, 0, sizeof(prm)); + + res = q2t_pre_xmit_response(cmd, &prm, xmit_type, &flags); + if (unlikely(res != SCST_TGT_RES_SUCCESS)) { + if (res == Q2T_PRE_XMIT_RESP_CMD_ABORTED) + res = SCST_TGT_RES_SUCCESS; + goto out; + } + + /* Here ha->hardware_lock already locked */ + + ha = prm.tgt->ha; + + res = q24_build_ctio_pkt(&prm); + if (unlikely(res != SCST_TGT_RES_SUCCESS)) + goto out_unmap_unlock; + + pkt = (ctio7_status0_entry_t *)prm.pkt; + + if (q2t_has_data(cmd) && (xmit_type & Q2T_XMIT_DATA)) { + pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN | + CTIO7_FLAGS_STATUS_MODE_0); + + q24_load_data_segments(&prm); + + if (prm.add_status_pkt == 0) { + if (xmit_type & Q2T_XMIT_STATUS) { + pkt->scsi_status = cpu_to_le16(prm.rq_result); + pkt->residual = cpu_to_le32(prm.residual); + pkt->flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_SEND_STATUS); + if (q2t_need_explicit_conf(ha, cmd, 0)) { + pkt->flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_EXPLICIT_CONFORM | + CTIO7_FLAGS_CONFORM_REQ); + } + } + } else { + /* + * We have already made sure that there is sufficient + * amount of request entries to not drop HW lock in + * req_pkt(). + */ + ctio7_status1_entry_t *ctio = + (ctio7_status1_entry_t *)q2t_get_req_pkt(ha); + + TRACE_DBG("%s", "Building additional status packet"); + + memcpy(ctio, pkt, sizeof(*ctio)); + ctio->common.entry_count = 1; + ctio->common.dseg_count = 0; + ctio->flags &= ~__constant_cpu_to_le16( + CTIO7_FLAGS_DATA_IN); + + /* Real finish is ctio_m1's finish */ + pkt->common.handle |= CTIO_INTERMEDIATE_HANDLE_MARK; + pkt->flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_DONT_RET_CTIO); + q24_init_ctio_ret_entry((ctio7_status0_entry_t *)ctio, + &prm); + TRACE_BUFFER("Status CTIO7", ctio, REQUEST_ENTRY_SIZE); + } + } else + q24_init_ctio_ret_entry(pkt, &prm); + + cmd->state = Q2T_STATE_PROCESSED; /* Mid-level is done processing */ + + TRACE_BUFFER("Xmitting CTIO7", pkt, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); + +out_unlock: + /* Release ring specific lock */ + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +out: + TRACE_EXIT_RES(res); + return res; + +out_unmap_unlock: + if (q2t_has_data(cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + } + goto out_unlock; +} + +static int q24_xmit_response(struct scst_cmd *scst_cmd) +{ + int xmit_type = Q2T_XMIT_DATA; + int is_send_status = scst_cmd_get_is_send_status(scst_cmd); + struct q2t_cmd *cmd = (struct q2t_cmd*)scst_cmd_get_tgt_priv(scst_cmd); + +#ifdef CONFIG_SCST_EXTRACHECKS + sBUG_ON(!q2t_has_data(cmd) && !is_send_status); +#endif + +#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD + if (scst_cmd_atomic(scst_cmd)) + return SCST_TGT_RES_NEED_THREAD_CTX; +#endif + + if (is_send_status) + xmit_type |= Q2T_XMIT_STATUS; + + cmd->bufflen = scst_cmd_get_resp_data_len(scst_cmd); + cmd->sg = scst_cmd_get_sg(scst_cmd); + cmd->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); + cmd->data_direction = scst_cmd_get_data_direction(scst_cmd); + cmd->offset = scst_cmd_get_ppl_offset(scst_cmd); + cmd->aborted = scst_cmd_aborted(scst_cmd); + + q2t_check_srr_debug(cmd, &xmit_type); + + TRACE_DBG("is_send_status=%x, bufflen=%d, sg_cnt=%d, " + "data_direction=%d, offset=%d", is_send_status, cmd->bufflen, + cmd->sg_cnt, cmd->data_direction, cmd->offset); + + return __q24_xmit_response(cmd, xmit_type); +} + +static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd) +{ + int res = SCST_TGT_RES_SUCCESS; + unsigned long flags; + scsi_qla_host_t *ha; + struct q2t_tgt *tgt = cmd->tgt; + struct q2t_prm prm; + void *p; + + TRACE_ENTRY(); + + memset(&prm, 0, sizeof(prm)); + prm.cmd = cmd; + prm.tgt = tgt; + prm.sg = NULL; + prm.req_cnt = 1; + ha = tgt->ha; + + /* Send marker if required */ + if (q2t_issue_marker(ha, 0) != QLA_SUCCESS) { + res = SCST_TGT_RES_FATAL_ERROR; + goto out; + } + + TRACE_DBG("CTIO_start: ha(%d)", (int)ha->instance); /* Calculate number of entries and segments required */ if (q2t_pci_map_calc_cnt(&prm) != 0) { res = SCST_TGT_RES_QUEUE_FULL; - goto out_unlock; + goto out; } - prm.cmd->iocb_cnt = prm.req_cnt; + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); - q2t_build_ctio_pkt(&prm); + /* Does F/W have an IOCBs for this request */ + res = q2t_check_reserve_free_req(ha, prm.req_cnt); + if (res != SCST_TGT_RES_SUCCESS) + goto out_unlock_free_unmap; - prm.pkt->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_OUT); + if (IS_FWI2_CAPABLE(ha)) { + ctio7_status0_entry_t *pkt; + res = q24_build_ctio_pkt(&prm); + if (unlikely(res != SCST_TGT_RES_SUCCESS)) + goto out_unlock_free_unmap; + pkt = (ctio7_status0_entry_t *)prm.pkt; + pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT | + CTIO7_FLAGS_STATUS_MODE_0); + q24_load_data_segments(&prm); + p = pkt; + } else { + ctio_common_entry_t *pkt; + q2x_build_ctio_pkt(&prm); + pkt = (ctio_common_entry_t *)prm.pkt; + pkt->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_OUT); + q2x_load_data_segments(&prm); + p = pkt; + } - q2t_load_data_segments(&prm); + cmd->state = Q2T_STATE_NEED_DATA; - prm.cmd->state = Q2T_STATE_NEED_DATA; + TRACE_BUFFER("Xfering", p, REQUEST_ENTRY_SIZE); - TRACE_BUFFER("Xfering", prm.pkt, REQUEST_ENTRY_SIZE); - - q2t_exec_queue(prm.tgt->ha); + q2t_exec_queue(ha); out_unlock: /* Release ring specific lock */ - spin_unlock_irqrestore(&prm.tgt->ha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +out: TRACE_EXIT_RES(res); return res; + +out_unlock_free_unmap: + if (q2t_has_data(cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + } + goto out_unlock; +} + +static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd) +{ + int res; + struct q2t_cmd *cmd; + + TRACE_ENTRY(); + + TRACE(TRACE_SCSI, "tag=%Ld", scst_cmd_get_tag(scst_cmd)); + + cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); + cmd->bufflen = scst_cmd_get_bufflen(scst_cmd); + cmd->sg = scst_cmd_get_sg(scst_cmd); + cmd->sg_cnt = scst_cmd_get_sg_cnt(scst_cmd); + cmd->data_direction = scst_cmd_get_data_direction(scst_cmd); + + res = __q2t_rdy_to_xfer(cmd); + + TRACE_EXIT(); + return res; } -static void q2t_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, - struct atio_entry *atio, int ha_locked) +/* If hardware_lock held on entry, might drop it, then reaquire */ +static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, + atio_entry_t *atio, int ha_locked) { - struct ctio_ret_entry *ctio; - unsigned long flags = 0; + ctio_ret_entry_t *ctio; + unsigned long flags = 0; /* to stop compiler's warning */ int do_tgt_cmd_done = 0; TRACE_ENTRY(); TRACE_DBG("Sending TERM EXCH CTIO (ha=%p)", ha); + /* Send marker if required */ + if (q2t_issue_marker(ha, ha_locked) != QLA_SUCCESS) + goto out; + if (!ha_locked) spin_lock_irqsave(&ha->hardware_lock, flags); - /* Send marker if required */ - if (tgt_data.issue_marker(ha) != QLA_SUCCESS) { - PRINT_ERROR("qla2x00tgt(%ld): __QLA2X00_MARKER() " - "failed", ha->host_no); - goto out_unlock; - } - - ctio = (struct ctio_ret_entry *)tgt_data.req_pkt(ha); + ctio = (ctio_ret_entry_t *)tgt_data.req_pkt(ha); if (ctio == NULL) { PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " - "request packet", ha->host_no, __func__); + "request packet", ha->instance, __func__); goto out_unlock; } ctio->entry_type = CTIO_RET_TYPE; ctio->entry_count = 1; - if (cmd != NULL) { - ctio->handle = q2t_make_handle(ha); - if (ctio->handle != Q2T_NULL_HANDLE) { - ha->cmds[ctio->handle] = cmd; - } else { - ctio->handle = Q2T_SKIP_HANDLE; + if (cmd->state < Q2T_STATE_PROCESSED) { + PRINT_ERROR("qla2x00tgt(%ld): Terminating cmd %p with " + "incorrect state %d", ha->instance, cmd, + cmd->state); + } else do_tgt_cmd_done = 1; - } - } else - ctio->handle = Q2T_SKIP_HANDLE; - - ctio->handle |= CTIO_COMPLETION_HANDLE_MARK; + } + ctio->handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + /* Set IDs */ SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio)); - ctio->exchange_id = atio->exchange_id; + ctio->rx_id = atio->rx_id; /* Most likely, it isn't needed */ ctio->residual = atio->data_length; @@ -966,30 +2455,111 @@ out_unlock: spin_unlock_irqrestore(&ha->hardware_lock, flags); if (do_tgt_cmd_done) { - if (!in_interrupt()) { - msleep(250); + if (!ha_locked && !in_interrupt()) { + msleep(250); /* just in case */ scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_DIRECT); } else scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_TASKLET); /* !! At this point cmd could be already freed !! */ } +out: + TRACE_EXIT(); + return; +} + +/* If hardware_lock held on entry, might drop it, then reaquire */ +static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd, + atio7_entry_t *atio, int ha_locked) +{ + ctio7_status1_entry_t *ctio; + unsigned long flags = 0; /* to stop compiler's warning */ + int do_tgt_cmd_done = 0; + + TRACE_ENTRY(); + + TRACE_DBG("Sending TERM EXCH CTIO7 (ha=%p)", ha); + + /* Send marker if required */ + if (q2t_issue_marker(ha, ha_locked) != QLA_SUCCESS) + goto out; + + if (!ha_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + + ctio = (ctio7_status1_entry_t *)tgt_data.req_pkt(ha); + if (ctio == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out_unlock; + } + + ctio->common.entry_type = CTIO_TYPE7; + ctio->common.entry_count = 1; + if (cmd != NULL) { + ctio->common.nport_handle = cmd->loop_id; + if (cmd->state < Q2T_STATE_PROCESSED) { + PRINT_ERROR("qla2x00tgt(%ld): Terminating cmd %p with " + "incorrect state %d", ha->instance, cmd, + cmd->state); + } else + do_tgt_cmd_done = 1; + } else + ctio->common.nport_handle = CTIO7_NHANDLE_UNRECOGNIZED; + ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; + ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; + ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; + ctio->common.exchange_addr = atio->exchange_addr; + ctio->flags = (atio->attr << 9) | __constant_cpu_to_le16( + CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); + ctio->ox_id = swab16(atio->fcp_hdr.ox_id); + + /* Most likely, it isn't needed */ + ctio->residual = atio->fcp_cmnd.data_length; + if (ctio->residual != 0) + ctio->scsi_status |= SS_RESIDUAL_UNDER; + + TRACE_BUFFER("CTIO7 TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); + +out_unlock: + if (!ha_locked) + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (do_tgt_cmd_done) { + if (!ha_locked && !in_interrupt()) { + msleep(250); /* just in case */ + scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_DIRECT); + } else + scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_TASKLET); + /* !! At this point cmd could be already freed !! */ + } + +out: TRACE_EXIT(); return; } static inline void q2t_free_cmd(struct q2t_cmd *cmd) { + if (unlikely(cmd->free_sg)) + kfree(cmd->sg); kmem_cache_free(q2t_cmd_cachep, cmd); } static void q2t_on_free_cmd(struct scst_cmd *scst_cmd) { - struct q2t_cmd *cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); + struct q2t_cmd *cmd; TRACE_ENTRY(); - TRACE(TRACE_SCSI, "END Command tag %lld", scst_cmd_get_tag(scst_cmd)); + TRACE(TRACE_SCSI, "Freeing command %p, tag %Ld", scst_cmd, + scst_cmd_get_tag(scst_cmd)); + + cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); scst_cmd_set_tgt_priv(scst_cmd, NULL); q2t_free_cmd(cmd); @@ -999,10 +2569,132 @@ static void q2t_on_free_cmd(struct scst_cmd *scst_cmd) } /* ha->hardware_lock supposed to be held on entry */ -static inline struct scst_cmd *q2t_get_cmd(scsi_qla_host_t *ha, uint32_t handle) +static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd, + void *ctio) { + struct srr_ctio *sc; + struct q2t_tgt *tgt = ha->tgt; + int res = 0; + struct srr_imm *imm; + + tgt->ctio_srr_id++; + + TRACE_MGMT_DBG("qla2x00tgt(%ld): CTIO with SRR " + "status received", ha->instance); + + if (ctio == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): SRR CTIO, " + "but ctio is NULL", ha->instance); + res = -EINVAL; + goto out; + } + + sc = kzalloc(sizeof(*sc), GFP_ATOMIC); + if (sc != NULL) { + sc->cmd = cmd; + /* IRQ is already OFF */ + spin_lock(&tgt->srr_lock); + sc->srr_id = tgt->ctio_srr_id; + list_add_tail(&sc->srr_list_entry, + &tgt->srr_ctio_list); + TRACE_MGMT_DBG("CTIO SRR %p added (id %d)", + sc, sc->srr_id); + if (tgt->imm_srr_id == tgt->ctio_srr_id) { + int found = 0; + list_for_each_entry(imm, &tgt->srr_imm_list, + srr_list_entry) { + if (imm->srr_id == sc->srr_id) { + found = 1; + break; + } + } + if (found) { + TRACE_MGMT_DBG("%s", "Scheduling srr work"); + schedule_work(&tgt->srr_work); + } else { + PRINT_ERROR("qla2x00tgt(%ld): imm_srr_id " + "== ctio_srr_id (%d), but there is no " + "corresponding SRR IMM, deleting CTIO " + "SRR %p", ha->instance, tgt->ctio_srr_id, + sc); + list_del(&sc->srr_list_entry); + spin_unlock(&tgt->srr_lock); + + kfree(sc); + res = -EINVAL; + goto out; + } + } + spin_unlock(&tgt->srr_lock); + } else { + struct srr_imm *ti; + PRINT_CRIT_ERROR("qla2x00tgt(%ld): Unable to " + "allocate SRR CTIO entry", ha->instance); + spin_lock(&tgt->srr_lock); + list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list, + srr_list_entry) { + if (imm->srr_id == tgt->ctio_srr_id) { + TRACE_MGMT_DBG("IMM SRR %p deleted " + "(id %d)", imm, imm->srr_id); + list_del(&imm->srr_list_entry); + q2t_reject_free_srr_imm(ha, imm, 1); + } + } + spin_unlock(&tgt->srr_lock); + res = -ENOMEM; + goto out; + } + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static int q2t_term_ctio_exchange(scsi_qla_host_t *ha, void *ctio, + struct q2t_cmd *cmd, uint32_t status) +{ + int term = 0; + + if (IS_FWI2_CAPABLE(ha)) { + if (ctio != NULL) { + ctio7_fw_entry_t *c = (ctio7_fw_entry_t *)ctio; + term = !(c->flags & + __constant_cpu_to_le16(OF_TERM_EXCH)); + } else + term = 1; + if (term) { + q24_send_term_exchange(ha, cmd, + &cmd->atio.atio7, 1); + } + } else { + if (status != CTIO_SUCCESS) + q2x_modify_command_count(ha, 1, 0); +#if 0 /* seems, it isn't needed */ + if (ctio != NULL) { + ctio_common_entry_t *c = (ctio_common_entry_t *)ctio; + term = !(c->flags & + __constant_cpu_to_le16( + CTIO7_FLAGS_TERMINATE)); + } else + term = 1; + if (term) { + q2x_send_term_exchange(ha, cmd, + &cmd->atio.atio2x, 1); + } +#endif + } + return term; +} + +/* ha->hardware_lock supposed to be held on entry */ +static inline struct q2t_cmd *q2t_get_cmd(scsi_qla_host_t *ha, uint32_t handle) +{ + handle--; if (ha->cmds[handle] != NULL) { - struct scst_cmd *cmd = ha->cmds[handle]->scst_cmd; + struct q2t_cmd *cmd = ha->cmds[handle]; ha->cmds[handle] = NULL; return cmd; } else @@ -1010,16 +2702,84 @@ static inline struct scst_cmd *q2t_get_cmd(scsi_qla_host_t *ha, uint32_t handle) } /* ha->hardware_lock supposed to be held on entry */ -static void q2t_do_ctio_completion(scsi_qla_host_t *ha, - uint32_t handle, - uint16_t status, - struct ctio_common_entry *ctio) +static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle, + void *ctio) +{ + struct q2t_cmd *cmd = NULL; + + /* Clear out internal marks */ + handle &= ~(CTIO_COMPLETION_HANDLE_MARK | CTIO_INTERMEDIATE_HANDLE_MARK); + + if (handle != Q2T_NULL_HANDLE) { + if (unlikely(handle == Q2T_SKIP_HANDLE)) { + TRACE_DBG("%s", "SKIP_HANDLE CTIO"); + goto out; + } + /* handle-1 is actually used */ + if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) { + PRINT_ERROR("qla2x00tgt(%ld): Wrong handle %x " + "received", ha->instance, handle); + goto out; + } + cmd = q2t_get_cmd(ha, handle); + if (unlikely(cmd == NULL)) { + PRINT_WARNING("qla2x00tgt(%ld): Suspicious: unable to " + "find the command with handle %x", + ha->instance, handle); + goto out; + } + } else if (ctio != NULL) { + uint16_t loop_id; + int tag; + struct q2t_sess *sess; + struct scst_cmd *scst_cmd; + + if (IS_FWI2_CAPABLE(ha)) { + /* We can't get loop ID from CTIO7 */ + PRINT_ERROR("qla2x00tgt(%ld): Wrong CTIO received: " + "QLA24xx doesn't support NULL handles", + ha->instance); + goto out; + } else { + ctio_common_entry_t *c = (ctio_common_entry_t *)ctio; + loop_id = GET_TARGET_ID(ha, c); + tag = c->rx_id; + } + + sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); + if (sess == NULL) { + PRINT_WARNING("qla2x00tgt(%ld): Suspicious: " + "ctio_completion for non-existing session " + "(loop_id %d, tag %d)", + ha->instance, loop_id, tag); + goto out; + } + + scst_cmd = scst_find_cmd_by_tag(sess->scst_sess, tag); + if (scst_cmd == NULL) { + PRINT_WARNING("qla2x00tgt(%ld): Suspicious: unable to " + "find the command with tag %d (loop_id %d)", + ha->instance, tag, loop_id); + goto out; + } + + cmd = (struct q2t_cmd*)scst_cmd_get_tgt_priv(scst_cmd); + TRACE_DBG("Found q2t_cmd %p (tag %d)", cmd, tag); + } + +out: + return cmd; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, + uint32_t status, void *ctio) { struct scst_cmd *scst_cmd; struct q2t_cmd *cmd; - uint16_t loop_id = -1; enum scst_exec_context context; - int err = 0; TRACE_ENTRY(); @@ -1029,18 +2789,29 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, context = SCST_CONTEXT_TASKLET; #endif - if (ctio != NULL) - loop_id = GET_TARGET_ID(ha, ctio); + TRACE(TRACE_DEBUG|TRACE_SCSI, "handle(ctio %p status %#x) <- %08x", + ctio, status, handle); - TRACE(TRACE_DEBUG|TRACE_SCSI, "handle(ctio %p status %#x) <- %08x I %x", - ctio, status, handle, loop_id); + if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) { + /* That could happen only in case of an error/reset/abort */ + if (status != CTIO_SUCCESS) { + TRACE_MGMT_DBG("Intermediate CTIO received (status %x)", + status); + } + goto out; + } - /* Clear out CTIO_COMPLETION_HANDLE_MARK */ - handle &= ~CTIO_COMPLETION_HANDLE_MARK; + cmd = q2t_ctio_to_cmd(ha, handle, ctio); + if (cmd == NULL) { + if (status != CTIO_SUCCESS) + q2t_term_ctio_exchange(ha, ctio, NULL, status); + goto out; + } - if (status != CTIO_SUCCESS) { - err = 1; - switch (status) { + scst_cmd = cmd->scst_cmd; + + if (unlikely(status != CTIO_SUCCESS)) { + switch (status & 0xFFFF) { case CTIO_LIP_RESET: case CTIO_TARGET_RESET: case CTIO_ABORTED: @@ -1048,206 +2819,141 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, case CTIO_INVALID_RX_ID: /* they are OK */ TRACE(TRACE_MGMT_MINOR, "qla2x00tgt(%ld): CTIO with " - "status %#x received (LIP_RESET=e, ABORTED=2, " - "TARGET_RESET=17, TIMEOUT=b, " - "INVALID_RX_ID=8)", ha->host_no, status); + "status %#x received, state %x, scst_cmd %p, " + "op %x (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " + "TIMEOUT=b, INVALID_RX_ID=8)", ha->instance, + status, cmd->state, scst_cmd, scst_cmd->cdb[0]); break; case CTIO_PORT_LOGGED_OUT: case CTIO_PORT_UNAVAILABLE: PRINT_INFO("qla2x00tgt(%ld): CTIO with PORT LOGGED " "OUT (29) or PORT UNAVAILABLE (28) status %x " - "received", ha->host_no, status); + "received (state %x, scst_cmd %p, op %x)", + ha->instance, status, cmd->state, scst_cmd, + scst_cmd->cdb[0]); break; + case CTIO_SRR_RECEIVED: + if (q2t_prepare_srr_ctio(ha, cmd, ctio) != 0) + break; + else + goto out; + default: PRINT_ERROR("qla2x00tgt(%ld): CTIO with error status " - "0x%x received", ha->host_no, status); + "0x%x received (state %x, scst_cmd %p, op %x)", + ha->instance, status, cmd->state, scst_cmd, + scst_cmd->cdb[0]); break; } - q2t_modify_command_count(ha, 1, 0); + + if (cmd->state != Q2T_STATE_NEED_DATA) + if (q2t_term_ctio_exchange(ha, ctio, cmd, status)) + goto out; } - if (handle != Q2T_NULL_HANDLE) { - if (unlikely(handle == Q2T_SKIP_HANDLE)) - goto out; - if (unlikely(handle == Q2T_BUSY_HANDLE)) - goto out; - scst_cmd = q2t_get_cmd(ha, handle); - if (unlikely(scst_cmd == NULL)) { - PRINT_INFO("qla2x00tgt(%ld): Suspicious: unable to " - "find the command with handle %x", - ha->host_no, handle); - goto out; - } - if (unlikely(err)) - TRACE_MGMT_DBG("Found by handle failed CTIO scst_cmd " - "%p (op %x)", scst_cmd, scst_cmd->cdb[0]); - } else if (ctio != NULL) { - uint32_t tag = le16_to_cpu(ctio->exchange_id); - struct q2t_sess *sess = q2t_find_sess_by_lid(ha->tgt, loop_id); - - if (sess == NULL) { - PRINT_INFO("qla2x00tgt(%ld): Suspicious: " - "ctio_completion for non-existing session " - "(loop_id %d, tag %d)", - ha->host_no, loop_id, tag); - goto out; - } - - scst_cmd = scst_find_cmd_by_tag(sess->scst_sess, tag); - if (scst_cmd == NULL) { - PRINT_INFO("qla2x00tgt(%ld): Suspicious: unable to " - "find the command with tag %d (loop_id %d)", - ha->host_no, tag, loop_id); - goto out; - } - if (unlikely(err)) - TRACE_MGMT_DBG("Found by ctio failed CTIO scst_cmd %p " - "(op %x)", scst_cmd, scst_cmd->cdb[0]); - - TRACE_DBG("Found scst_cmd %p", scst_cmd); - } else - goto out; - - cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); - if (unlikely(err)) - TRACE(TRACE_MGMT_MINOR, "Failed CTIO state %d (err %x)", - cmd->state, status); - if (cmd->state == Q2T_STATE_PROCESSED) { TRACE_DBG("Command %p finished", cmd); - if (q2t_has_data(scst_cmd)) { - pci_unmap_sg(ha->pdev, scst_cmd_get_sg(scst_cmd), - scst_cmd_get_sg_cnt(scst_cmd), - scst_to_tgt_dma_dir( - scst_cmd_get_data_direction(scst_cmd))); + if (q2t_has_data(cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); } - goto out_free; } else if (cmd->state == Q2T_STATE_NEED_DATA) { int rx_status = SCST_RX_STATUS_SUCCESS; cmd->state = Q2T_STATE_DATA_IN; - if (status != CTIO_SUCCESS) + if (unlikely(status != CTIO_SUCCESS)) rx_status = SCST_RX_STATUS_ERROR; TRACE_DBG("Data received, context %x, rx_status %d", context, rx_status); - pci_unmap_sg(ha->pdev, scst_cmd_get_sg(scst_cmd), - scst_cmd_get_sg_cnt(scst_cmd), - scst_to_tgt_dma_dir( - scst_cmd_get_data_direction(scst_cmd))); + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); scst_rx_data(scst_cmd, rx_status, context); + goto out; } else if (cmd->state == Q2T_STATE_ABORTED) { - TRACE_MGMT_DBG("Aborted command %p finished", cmd); - goto out_free; + TRACE_MGMT_DBG("Aborted command %p (tag %d) finished", cmd, + cmd->tag); } else { PRINT_ERROR("qla2x00tgt(%ld): A command in state (%d) should " - "not return a CTIO complete", ha->host_no, cmd->state); - goto out_free; + "not return a CTIO complete", ha->instance, cmd->state); } + if (unlikely(status != CTIO_SUCCESS)) { + TRACE_MGMT_DBG("%s", "Finishing failed CTIO"); + scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_FAILED); + } + + scst_tgt_cmd_done(scst_cmd, context); + out: TRACE_EXIT(); return; - -out_free: - if (unlikely(err)) { - TRACE_MGMT_DBG("%s", "Finishing failed CTIO"); - scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_FAILED); - } - scst_tgt_cmd_done(scst_cmd, context); - goto out; } /* ha->hardware_lock supposed to be held on entry */ /* called via callback from qla2xxx */ -static void q2t_ctio_completion(scsi_qla_host_t *ha, uint32_t handle) +static void q2x_ctio_completion(scsi_qla_host_t *ha, uint32_t handle) { - TRACE_ENTRY(); - sBUG_ON(ha == NULL); + struct q2t_tgt *tgt = ha->tgt; - if (ha->tgt != NULL) { - q2t_do_ctio_completion(ha, handle, - CTIO_SUCCESS, NULL); + TRACE_ENTRY(); + + if (likely(tgt != NULL)) { + tgt->irq_cmd_count++; + q2t_do_ctio_completion(ha, handle, CTIO_SUCCESS, NULL); + tgt->irq_cmd_count--; } else { - TRACE_DBG("CTIO, but target mode not enabled. ha %p handle %#x", - ha, handle); + TRACE_DBG("CTIO, but target mode not enabled (ha %p handle " + "%#x)", ha, handle); } - TRACE_EXIT(); - return; -} - -/* ha->hardware_lock supposed to be held on entry */ -static void q2t_send_busy(scsi_qla_host_t *ha, struct atio_entry *atio) -{ - struct ctio_ret_entry *ctio; - - TRACE_ENTRY(); - - ctio = (struct ctio_ret_entry *)tgt_data.req_pkt(ha); - ctio->entry_type = CTIO_RET_TYPE; - ctio->entry_count = 1; - ctio->handle = Q2T_BUSY_HANDLE | CTIO_COMPLETION_HANDLE_MARK; - ctio->scsi_status = __constant_cpu_to_le16(BUSY << 1); - ctio->residual = atio->data_length; - if (ctio->residual != 0) - ctio->scsi_status |= SS_RESIDUAL_UNDER; - - /* Set IDs */ - SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio)); - ctio->exchange_id = atio->exchange_id; - - ctio->flags = __constant_cpu_to_le16(OF_SSTS | OF_FAST_POST | - OF_NO_DATA | OF_SS_MODE_1); - ctio->flags |= __constant_cpu_to_le16(OF_INC_RC); - - TRACE_BUFFER("CTIO BUSY packet data", ctio, REQUEST_ENTRY_SIZE); - - q2t_exec_queue(ha); TRACE_EXIT(); return; } /* ha->hardware_lock is supposed to be held on entry */ -static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, struct q2t_cmd *cmd) +static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd) { int res = 0; struct q2t_sess *sess = cmd->sess; uint16_t lun; - scst_data_direction dir = SCST_DATA_NONE; - enum scst_exec_context context; + atio_entry_t *atio = &cmd->atio.atio2x; + scst_data_direction dir; + int context; TRACE_ENTRY(); /* make it be in network byte order */ - lun = swab16(cmd->atio.lun); + lun = swab16(le16_to_cpu(atio->lun)); cmd->scst_cmd = scst_rx_cmd(sess->scst_sess, (uint8_t *)&lun, - sizeof(lun), cmd->atio.cdb, Q2T_MAX_CDB_LEN, + sizeof(lun), atio->cdb, Q2T_MAX_CDB_LEN, SCST_ATOMIC); if (cmd->scst_cmd == NULL) { - PRINT_ERROR("qla2x00tgt(%ld): scst_rx_cmd() failed for " - "host %ld(%p)", ha->host_no, ha->host_no, ha); + PRINT_ERROR("%s", "qla2x00tgt: scst_rx_cmd() failed"); res = -EFAULT; goto out; } - scst_cmd_set_tag(cmd->scst_cmd, le16_to_cpu(cmd->atio.exchange_id)); + cmd->tag = atio->rx_id; + scst_cmd_set_tag(cmd->scst_cmd, cmd->tag); scst_cmd_set_tgt_priv(cmd->scst_cmd, cmd); - if (cmd->atio.execution_codes & ATIO_EXEC_READ) + if (atio->execution_codes & ATIO_EXEC_READ) dir = SCST_DATA_READ; - else if (cmd->atio.execution_codes & ATIO_EXEC_WRITE) + else if (atio->execution_codes & ATIO_EXEC_WRITE) dir = SCST_DATA_WRITE; + else + dir = SCST_DATA_NONE; scst_cmd_set_expected(cmd->scst_cmd, dir, - le32_to_cpu(cmd->atio.data_length)); + le32_to_cpu(atio->data_length)); - switch (cmd->atio.task_codes) { + switch(atio->task_codes) { case ATIO_SIMPLE_QUEUE: cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_SIMPLE; break; @@ -1264,8 +2970,8 @@ static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, struct q2t_cmd *cmd) cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED; break; default: - PRINT_ERROR("qla2x00tgt(%ld): Unknown task code %x, use " - "ORDERED instead", ha->host_no, cmd->atio.task_codes); + PRINT_ERROR("qla2x00tgt: unknown task code %x, use " + "ORDERED instead", atio->task_codes); cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED; break; } @@ -1277,8 +2983,8 @@ static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, struct q2t_cmd *cmd) #endif TRACE_DBG("Context %x", context); - TRACE(TRACE_SCSI, "START Command (tag %lld)", - scst_cmd_get_tag(cmd->scst_cmd)); + TRACE(TRACE_SCSI, "START Command (tag %d, queue_type %d)", + cmd->tag, cmd->scst_cmd->queue_type); scst_cmd_init_done(cmd->scst_cmd, context); out: @@ -1286,281 +2992,285 @@ out: return res; } -/* Called in SCST's thread context */ -static void q2t_alloc_session_done(struct scst_session *scst_sess, - void *data, int result) +/* ha->hardware_lock is supposed to be held on entry */ +static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) { + int res = 0; + struct q2t_sess *sess = cmd->sess; + atio7_entry_t *atio = &cmd->atio.atio7; + scst_data_direction dir; + int context; + TRACE_ENTRY(); - if (result != 0) { - struct q2t_sess *sess = (struct q2t_sess *)data; - struct q2t_tgt *tgt = sess->tgt; - scsi_qla_host_t *ha = tgt->ha; - unsigned long flags; + cmd->scst_cmd = scst_rx_cmd(sess->scst_sess, + (uint8_t *)&atio->fcp_cmnd.lun, sizeof(atio->fcp_cmnd.lun), + atio->fcp_cmnd.cdb, Q2T_MAX_CDB_LEN, SCST_ATOMIC); - PRINT_INFO("qla2x00tgt(%ld): Session initialization failed", - ha->host_no); - - spin_lock_irqsave(&ha->hardware_lock, flags); - q2t_unreg_sess(sess); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - } - - TRACE_EXIT(); - return; -} - -static char *q2t_find_name(scsi_qla_host_t *ha, int loop_id) -{ - int wwn_found = 0; - char *wwn_str; - fc_port_t *fcl; - - wwn_str = kmalloc(3*WWN_SIZE, GFP_ATOMIC); - if (wwn_str == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of wwn_str failed"); + if (cmd->scst_cmd == NULL) { + PRINT_ERROR("%s", "qla2x00tgt: scst_rx_cmd() failed"); + res = -EFAULT; goto out; } - /* Find the WWN in the port db given the loop_id */ - list_for_each_entry_rcu(fcl, &ha->fcports, list) { - if (loop_id == (fcl->loop_id & 0xFF)) { - sprintf(wwn_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - fcl->port_name[0], fcl->port_name[1], - fcl->port_name[2], fcl->port_name[3], - fcl->port_name[4], fcl->port_name[5], - fcl->port_name[6], fcl->port_name[7]); - TRACE_DBG("found wwn: %s for loop_id: %d", wwn_str, loop_id); - wwn_found = 1; + cmd->tag = atio->exchange_addr; + scst_cmd_set_tag(cmd->scst_cmd, cmd->tag); + scst_cmd_set_tgt_priv(cmd->scst_cmd, cmd); + + if (atio->fcp_cmnd.rddata) + dir = SCST_DATA_READ; + else if (atio->fcp_cmnd.wrdata) + dir = SCST_DATA_WRITE; + else + dir = SCST_DATA_NONE; + scst_cmd_set_expected(cmd->scst_cmd, dir, + be32_to_cpu(atio->fcp_cmnd.data_length)); + + switch(atio->fcp_cmnd.task_attr) { + case ATIO_SIMPLE_QUEUE: + cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_SIMPLE; + break; + case ATIO_HEAD_OF_QUEUE: + cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_HEAD_OF_QUEUE; + break; + case ATIO_ORDERED_QUEUE: + cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED; + break; + case ATIO_ACA_QUEUE: + cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ACA; + break; + case ATIO_UNTAGGED: + cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED; + break; + default: + PRINT_ERROR("qla2x00tgt: unknown task code %x, use " + "ORDERED instead", atio->fcp_cmnd.task_attr); + cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED; break; - } } - if (wwn_found == 0) { - TRACE_MGMT_DBG("qla2x00tgt(%ld): Unable to find wwn login for " - "loop id %d", ha->host_no, loop_id); - kfree(wwn_str); - wwn_str = NULL; - } +#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD + context = SCST_CONTEXT_THREAD; +#else + context = SCST_CONTEXT_TASKLET; +#endif + + TRACE_DBG("Context %x", context); + TRACE(TRACE_SCSI, "START Command %p (tag %d, queue type %x)", cmd, + cmd->tag, cmd->scst_cmd->queue_type); + scst_cmd_init_done(cmd->scst_cmd, context); out: - return wwn_str; -} - -static char *q2t_make_name(scsi_qla_host_t *ha, const uint8_t *name) -{ - char *wwn_str; - - wwn_str = kmalloc(3*WWN_SIZE, GFP_ATOMIC); - if (wwn_str == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of wwn_str failed"); - goto out; - } - sprintf(wwn_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - name[1], name[0], name[3], name[2], name[5], name[4], - name[7], name[6]); - -out: - return wwn_str; + TRACE_EXIT_RES(res); + return res; } /* ha->hardware_lock supposed to be held on entry */ -static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, struct atio_entry *atio) +static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, + struct q2t_cmd *cmd, struct q2t_sess *sess) { - int res = 0; - struct q2t_tgt *tgt; - struct q2t_sess *sess; - struct q2t_cmd *cmd; - uint16_t *pn; - int loop_id; + int res; TRACE_ENTRY(); - tgt = ha->tgt; - loop_id = GET_TARGET_ID(ha, atio); + cmd->sess = sess; + cmd->loop_id = sess->loop_id; + cmd->conf_compl_supported = sess->conf_compl_supported; - pn = (uint16_t *)(((char *)atio)+0x2a); - TRACE_DBG("To SCST host_no=%ld l_id=%d tag=%d wwpn=%04x%04x%04x%04x", - ha->host_no, loop_id, le16_to_cpu(atio->exchange_id), - le16_to_cpu(pn[0]), - le16_to_cpu(pn[1]), - le16_to_cpu(pn[2]), - le16_to_cpu(pn[3])); - /* le64_to_cpu(*(uint64_t *)(((char *)atio)+0x2c))); */ - /*le32_to_cpu(*(uint32_t *)atio->initiator_port_name)); */ + if (IS_FWI2_CAPABLE(ha)) + res = q24_do_send_cmd_to_scst(cmd); + else + res = q2x_do_send_cmd_to_scst(cmd); - if (tgt->tgt_shutdown) { + TRACE_EXIT_RES(res); + return res; +} + +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) +{ + int res = 0; + struct q2t_tgt *tgt = ha->tgt; + struct q2t_sess *sess; + struct q2t_cmd *cmd; + + TRACE_ENTRY(); + + if (unlikely(tgt->tgt_shutdown)) { TRACE_MGMT_DBG("New command while device %p is shutting " "down", tgt); res = -EFAULT; goto out; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) - cmd = kmem_cache_alloc(q2t_cmd_cachep, GFP_ATOMIC); -#else - cmd = kmem_cache_zalloc(q2t_cmd_cachep, GFP_ATOMIC); -#endif + cmd = kmem_cache_zalloc(q2t_cmd_cachep, GFP_ATOMIC); if (cmd == NULL) { TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of cmd failed"); res = -ENOMEM; goto out; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) - memset(cmd, 0, sizeof(*cmd)); -#endif - TRACE_BUFFER("ATIO Coming Up", atio, sizeof(*atio)); - memcpy(&cmd->atio, atio, sizeof(*atio)); + memcpy(&cmd->atio.atio2x, atio, sizeof(*atio)); cmd->state = Q2T_STATE_NEW; + cmd->tgt = ha->tgt; - sess = q2t_find_sess_by_lid(tgt, loop_id); - if (unlikely(sess == NULL)) { - sess = kzalloc(sizeof(*sess), GFP_ATOMIC); - if (sess == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", - "Allocation of sess failed"); - res = -ENOMEM; - goto out_free_cmd; + if (IS_FWI2_CAPABLE(ha)) { + atio7_entry_t *a = (atio7_entry_t *)atio; + sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id); + if (unlikely(sess == NULL)) { + TRACE_MGMT_DBG("qla2x00tgt(%ld): Unable to find " + "wwn login (s_id %x:%x:%x), trying to create " + "it manually", ha->instance, + a->fcp_hdr.s_id[0], a->fcp_hdr.s_id[1], + a->fcp_hdr.s_id[2]); + goto out_sched; } - - sess->tgt = tgt; - sess->loop_id = loop_id; - INIT_LIST_HEAD(&sess->list); - - /* register session (remote initiator) */ - { - char *name; - if (IS_QLA2200(ha)) - name = q2t_find_name(ha, loop_id); - else { - name = q2t_make_name(ha, - atio->initiator_port_name); - } - if (name == NULL) { - res = -ESRCH; - goto out_free_sess; - } - - sess->scst_sess = scst_register_session( - tgt->scst_tgt, 1, name, sess, - q2t_alloc_session_done); - kfree(name); + } else { + sess = q2t_find_sess_by_loop_id(tgt, + GET_TARGET_ID(ha, (atio_entry_t *)atio)); + if (unlikely(sess == NULL)) { + TRACE_MGMT_DBG("qla2x00tgt(%ld): Unable to find " + "wwn login (loop_id=%d), trying to create it " + "manually", ha->instance, + GET_TARGET_ID(ha, (atio_entry_t *)atio)); + goto out_sched; } - - if (sess->scst_sess == NULL) { - PRINT_ERROR("qla2x00tgt(%ld): scst_register_session() " - "failed for host %ld(%p)", ha->host_no, - ha->host_no, ha); - res = -EFAULT; - goto out_free_sess; - } - scst_sess_set_tgt_priv(sess->scst_sess, sess); - - /* add session data to host data structure */ - list_add(&sess->list, &tgt->sess_list); - tgt->sess_count++; } - cmd->sess = sess; - res = q2t_do_send_cmd_to_scst(ha, cmd); - if (res != 0) + res = q2t_do_send_cmd_to_scst(ha, cmd, sess); + if (unlikely(res != 0)) goto out_free_cmd; out: TRACE_EXIT_RES(res); return res; -out_free_sess: - kfree(sess); - /* go through */ - out_free_cmd: q2t_free_cmd(cmd); goto out; + +out_sched: + { + struct q2t_sess_work_param *prm; + unsigned long flags; + + prm = kzalloc(sizeof(*prm), GFP_ATOMIC); + if (prm == NULL) { + PRINT_ERROR("%s", "Unable to create session work, " + "command will be refused"); + res = -1; + goto out_free_cmd; + } + + TRACE_MGMT_DBG("Scheduling work to find session for cmd %p", + cmd); + + prm->cmd = cmd; + + spin_lock_irqsave(&tgt->sess_work_lock, flags); + if (list_empty(&tgt->sess_works_list)) + tgt->tm_to_unknown = 0; + list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list); + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); + + schedule_work(&tgt->sess_work); + } + goto out; } /* ha->hardware_lock supposed to be held on entry */ -static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, struct notify_entry *iocb) +static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun, + int lun_size, int fn, void *iocb, int flags) { int res = 0, rc = -1; struct q2t_mgmt_cmd *mcmd; - struct q2t_tgt *tgt; - struct q2t_sess *sess; - int loop_id; - uint16_t lun; TRACE_ENTRY(); - tgt = ha->tgt; - loop_id = GET_TARGET_ID(ha, iocb); - - /* Make it be in network byte order */ - lun = swab16(iocb->lun); - - sess = q2t_find_sess_by_lid(tgt, loop_id); - if (sess == NULL) { - TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task mgmt fn 0x%x for " - "non-existant session", ha->host_no, iocb->task_flags); - res = -EFAULT; - goto out; - } - - mcmd = kzalloc(sizeof(*mcmd), GFP_ATOMIC); + mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); if (mcmd == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of mgmt cmd failed"); + PRINT_CRIT_ERROR("qla2x00tgt(%ld): Allocation of management " + "command failed, some commands and their data could " + "leak", sess->tgt->ha->instance); res = -ENOMEM; goto out; } + memset(mcmd, 0, sizeof(*mcmd)); mcmd->sess = sess; - mcmd->notify_entry = *iocb; + if (iocb) { + memcpy(&mcmd->orig_iocb.notify_entry, iocb, + sizeof(mcmd->orig_iocb.notify_entry)); + } + mcmd->flags = flags; - switch (iocb->task_flags) { - case IMM_NTFY_CLEAR_ACA: - TRACE(TRACE_MGMT, "%s", "IMM_NTFY_CLEAR_ACA received"); + switch (fn) { + case Q2T_CLEAR_ACA: + TRACE(TRACE_MGMT, "%s", "CLEAR_ACA received"); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_ACA, - (uint8_t *)&lun, sizeof(lun), - SCST_ATOMIC, mcmd); + lun, lun_size, SCST_ATOMIC, mcmd); break; - case IMM_NTFY_TARGET_RESET: - TRACE(TRACE_MGMT, "%s", "IMM_NTFY_TARGET_RESET received"); + case Q2T_TARGET_RESET: + TRACE(TRACE_MGMT, "%s", "TARGET_RESET received"); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_TARGET_RESET, - (uint8_t *)&lun, sizeof(lun), - SCST_ATOMIC, mcmd); + lun, lun_size, SCST_ATOMIC, mcmd); break; - case IMM_NTFY_LUN_RESET: - TRACE(TRACE_MGMT, "%s", "IMM_NTFY_LUN_RESET received"); + case Q2T_LUN_RESET: + TRACE(TRACE_MGMT, "%s", "LUN_RESET received"); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_LUN_RESET, - (uint8_t *)&lun, sizeof(lun), - SCST_ATOMIC, mcmd); + lun, lun_size, SCST_ATOMIC, mcmd); break; - case IMM_NTFY_CLEAR_TS: - TRACE(TRACE_MGMT, "%s", "IMM_NTFY_CLEAR_TS received"); + case Q2T_CLEAR_TS: + TRACE(TRACE_MGMT, "%s", "CLEAR_TS received"); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_TASK_SET, - (uint8_t *)&lun, sizeof(lun), - SCST_ATOMIC, mcmd); + lun, lun_size, SCST_ATOMIC, mcmd); break; - case IMM_NTFY_ABORT_TS: - TRACE(TRACE_MGMT, "%s", "IMM_NTFY_ABORT_TS received"); + case Q2T_ABORT_TS: + TRACE(TRACE_MGMT, "%s", "ABORT_TS received"); rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_TASK_SET, - (uint8_t *)&lun, sizeof(lun), - SCST_ATOMIC, mcmd); + lun, lun_size, SCST_ATOMIC, mcmd); + break; + + case Q2T_ABORT_ALL: + TRACE(TRACE_MGMT, "%s", "Doing ABORT_ALL_TASKS"); + rc = scst_rx_mgmt_fn_lun(sess->scst_sess, + SCST_ABORT_ALL_TASKS, + lun, lun_size, SCST_ATOMIC, mcmd); + break; + + case Q2T_ABORT_ALL_SESS: + TRACE(TRACE_MGMT, "%s", "Doing ABORT_ALL_TASKS_SESS"); + rc = scst_rx_mgmt_fn_lun(sess->scst_sess, + SCST_ABORT_ALL_TASKS_SESS, + lun, lun_size, SCST_ATOMIC, mcmd); + break; + + case Q2T_NEXUS_LOSS_SESS: + TRACE(TRACE_MGMT, "%s", "Doing NEXUS_LOSS_SESS"); + rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_NEXUS_LOSS_SESS, + lun, lun_size, SCST_ATOMIC, mcmd); + break; + + case Q2T_NEXUS_LOSS: + TRACE(TRACE_MGMT, "%s", "Doing NEXUS_LOSS"); + rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_NEXUS_LOSS, + lun, lun_size, SCST_ATOMIC, mcmd); break; default: PRINT_ERROR("qla2x00tgt(%ld): Unknown task mgmt fn 0x%x", - ha->host_no, iocb->task_flags); + sess->tgt->ha->instance, fn); + rc = -1; break; } if (rc != 0) { PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_lun() failed: %d", - ha->host_no, rc); + sess->tgt->ha->instance, rc); res = -EFAULT; goto out_free; } @@ -1570,12 +3280,62 @@ out: return res; out_free: - kfree(mcmd); + mempool_free(mcmd, q2t_mgmt_cmd_mempool); goto out; } /* ha->hardware_lock supposed to be held on entry */ -static int q2t_abort_task(scsi_qla_host_t *ha, struct notify_entry *iocb) +static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb) +{ + int res = 0; + struct q2t_tgt *tgt; + struct q2t_sess *sess; + uint8_t *lun; + uint16_t lun_data; + int lun_size; + int fn; + + TRACE_ENTRY(); + + tgt = ha->tgt; + if (IS_FWI2_CAPABLE(ha)) { + atio7_entry_t *a = (atio7_entry_t *)iocb; + lun = (uint8_t *)&a->fcp_cmnd.lun; + lun_size = sizeof(a->fcp_cmnd.lun); + fn = a->fcp_cmnd.task_mgmt_flags; + sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id); + if (sess != NULL) { + sess->s_id.b.al_pa = a->fcp_hdr.s_id[2]; + sess->s_id.b.area = a->fcp_hdr.s_id[1]; + sess->s_id.b.domain = a->fcp_hdr.s_id[0]; + } + } else { + notify_entry_t *n = (notify_entry_t *)iocb; + /* make it be in network byte order */ + lun_data = swab16(le16_to_cpu(n->lun)); + lun = (uint8_t *)&lun_data; + lun_size = sizeof(lun_data); + fn = n->task_flags >> IMM_NTFY_TASK_MGMT_SHIFT; + sess = q2t_find_sess_by_loop_id(tgt, GET_TARGET_ID(ha, n)); + } + + if (sess == NULL) { + TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task mgmt fn 0x%x for " + "non-existant session", ha->instance, fn); + tgt->tm_to_unknown = 1; + res = -ESRCH; + goto out; + } + + res = q2t_issue_task_mgmt(sess, lun, lun_size, fn, iocb, 0); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* ha->hardware_lock supposed to be held on entry */ +static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb) { int res = 0, rc; struct q2t_mgmt_cmd *mcmd; @@ -1588,29 +3348,32 @@ static int q2t_abort_task(scsi_qla_host_t *ha, struct notify_entry *iocb) loop_id = GET_TARGET_ID(ha, iocb); tag = le16_to_cpu(iocb->seq_id); - sess = q2t_find_sess_by_lid(ha->tgt, loop_id); + sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id); if (sess == NULL) { TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task abort for unexisting " - "session", ha->host_no); + "session", ha->instance); + ha->tgt->tm_to_unknown = 1; res = -EFAULT; goto out; } - - mcmd = kzalloc(sizeof(*mcmd), GFP_ATOMIC); + + mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC); if (mcmd == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of mgmt cmd failed"); + PRINT_ERROR("%s: Allocation of ABORT cmd failed", __func__); res = -ENOMEM; goto out; } + memset(mcmd, 0, sizeof(*mcmd)); mcmd->sess = sess; - mcmd->notify_entry = *iocb; + memcpy(&mcmd->orig_iocb.notify_entry, iocb, + sizeof(mcmd->orig_iocb.notify_entry)); - rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag, + rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag, SCST_ATOMIC, mcmd); if (rc != 0) { PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_tag() failed: %d", - ha->host_no, rc); + ha->instance, rc); res = -EFAULT; goto out_free; } @@ -1620,392 +3383,1505 @@ out: return res; out_free: - kfree(mcmd); + mempool_free(mcmd, q2t_mgmt_cmd_mempool); goto out; } -/* SCST Callback */ -static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb) { - struct q2t_mgmt_cmd *mcmd; - unsigned long flags; + int res = 0; TRACE_ENTRY(); - TRACE_MGMT_DBG("scst_mcmd (%p) status %#x state %#x", scst_mcmd, - scst_mcmd->status, scst_mcmd->state); + TRACE(TRACE_MGMT, "ELS opcode %x", iocb->status_subcode); - mcmd = scst_mgmt_cmd_get_tgt_priv(scst_mcmd); - if (unlikely(mcmd == NULL)) { - PRINT_ERROR("scst_mcmd %p tgt_spec is NULL", mcmd); + switch(iocb->status_subcode) { + case ELS_PLOGI: + case ELS_FLOGI: + case ELS_PRLI: + case ELS_LOGO: + case ELS_PRLO: + res = q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS); + break; + + case ELS_PDISC: + case ELS_ADISC: + { + struct q2t_tgt *tgt = ha->tgt; + if (tgt->link_reinit_iocb_pending) { + q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0); + tgt->link_reinit_iocb_pending = 0; + } + res = 1; /* send notify ack */ + break; + } + + default: + PRINT_ERROR("qla2x00tgt(%ld): Unsupported ELS command %x " + "received", ha->instance, iocb->status_subcode); + res = q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS); + break; + } + + TRACE_EXIT_RES(res); + return res; +} + +static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset) +{ + int res = 0; + int cnt, first_sg, first_page = 0, first_page_offs = 0, i; + unsigned int l; + int cur_dst, cur_src; + struct scatterlist *sg; + size_t bufflen = 0; + + TRACE_ENTRY(); + + first_sg = -1; + cnt = 0; + l = 0; + for(i = 0; i < cmd->sg_cnt; i++) { + l += cmd->sg[i].length; + if (l > offset) { + int sg_offs = l - cmd->sg[i].length; + first_sg = i; + if (cmd->sg[i].offset == 0) { + first_page_offs = offset % PAGE_SIZE; + first_page = (offset - sg_offs) >> PAGE_SHIFT; + } else { + TRACE_SG("i=%d, sg[i].offset=%d, " + "sg_offs=%d", i, cmd->sg[i].offset, sg_offs); + if ((cmd->sg[i].offset + sg_offs) > offset) { + first_page_offs = offset - sg_offs; + first_page = 0; + } else { + int sec_page_offs = sg_offs + + (PAGE_SIZE - cmd->sg[i].offset); + first_page_offs = sec_page_offs % PAGE_SIZE; + first_page = 1 + + ((offset - sec_page_offs) >> + PAGE_SHIFT); + } + } + cnt = cmd->sg_cnt - i + (first_page_offs != 0); + break; + } + } + if (first_sg == -1) { + PRINT_ERROR("qla2x00tgt(%ld): Wrong offset %d, buf length %d", + cmd->tgt->ha->instance, offset, cmd->bufflen); + res = -EINVAL; goto out; } - spin_lock_irqsave(&mcmd->sess->tgt->ha->hardware_lock, flags); - q2t_send_notify_ack(mcmd->sess->tgt->ha, &mcmd->notify_entry, 0, - (scst_mgmt_cmd_get_status(scst_mcmd) == SCST_MGMT_STATUS_SUCCESS) - ? 0 : FC_TM_FAILED, 1); - spin_unlock_irqrestore(&mcmd->sess->tgt->ha->hardware_lock, flags); + TRACE_SG("offset=%d, first_sg=%d, first_page=%d, " + "first_page_offs=%d, cmd->bufflen=%d, cmd->sg_cnt=%d", offset, + first_sg, first_page, first_page_offs, cmd->bufflen, + cmd->sg_cnt); - /* scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); */ - scst_mcmd->tgt_priv = NULL; - kfree(mcmd); + sg = kmalloc(cnt * sizeof(sg[0]), GFP_KERNEL); + if (sg == NULL) { + PRINT_CRIT_ERROR("qla2x00tgt(%ld): Unable to allocate cut " + "SG (len %zd)", cmd->tgt->ha->instance, + cnt * sizeof(sg[0])); + res = -ENOMEM; + goto out; + } + sg_init_table(sg, cnt); + + cur_dst = 0; + cur_src = first_sg; + if (first_page_offs != 0) { + int fpgs; + sg_set_page(&sg[cur_dst], &sg_page(&cmd->sg[cur_src])[first_page], + PAGE_SIZE - first_page_offs, first_page_offs); + bufflen += sg[cur_dst].length; + TRACE_SG("cur_dst=%d, cur_src=%d, sg[].page=%p, " + "sg[].offset=%d, sg[].length=%d, bufflen=%zu", + cur_dst, cur_src, sg_page(&sg[cur_dst]), sg[cur_dst].offset, + sg[cur_dst].length, bufflen); + cur_dst++; + + fpgs = (cmd->sg[cur_src].length >> PAGE_SHIFT) + + ((cmd->sg[cur_src].length & ~PAGE_MASK) != 0); + first_page++; + if (fpgs > first_page) { + sg_set_page(&sg[cur_dst], + &sg_page(&cmd->sg[cur_src])[first_page], + cmd->sg[cur_src].length - PAGE_SIZE*first_page, + 0); + TRACE_SG("fpgs=%d, cur_dst=%d, cur_src=%d, " + "sg[].page=%p, sg[].length=%d, bufflen=%zu", + fpgs, cur_dst, cur_src, sg_page(&sg[cur_dst]), + sg[cur_dst].length, bufflen); + bufflen += sg[cur_dst].length; + cur_dst++; + } + cur_src++; + } + + while(cur_src < cmd->sg_cnt) { + sg_set_page(&sg[cur_dst], sg_page(&cmd->sg[cur_src]), + cmd->sg[cur_src].length, cmd->sg[cur_src].offset); + TRACE_SG("cur_dst=%d, cur_src=%d, " + "sg[].page=%p, sg[].length=%d, sg[].offset=%d, " + "bufflen=%zu", cur_dst, cur_src, sg_page(&sg[cur_dst]), + sg[cur_dst].length, sg[cur_dst].offset, bufflen); + bufflen += sg[cur_dst].length; + cur_dst++; + cur_src++; + } + + if (cmd->free_sg) + kfree(cmd->sg); + + cmd->sg = sg; + cmd->free_sg = 1; + cmd->sg_cnt = cur_dst; + cmd->bufflen = bufflen; + cmd->offset += offset; + +out: + TRACE_EXIT_RES(res); + return res; +} + +static inline int q2t_srr_adjust_data(struct q2t_cmd *cmd, + uint32_t srr_rel_offs, int *xmit_type) +{ + int res = 0; + int rel_offs; + + rel_offs = srr_rel_offs - cmd->offset; + TRACE_MGMT_DBG("srr_rel_offs=%d, rel_offs=%d", srr_rel_offs, rel_offs); + + *xmit_type = Q2T_XMIT_ALL; + + if (rel_offs < 0) { + PRINT_ERROR("qla2x00tgt(%ld): SRR rel_offs (%d) " + "< 0", cmd->tgt->ha->instance, rel_offs); + res = -1; + } else if (rel_offs == cmd->bufflen) + *xmit_type = Q2T_XMIT_STATUS; + else if (rel_offs > 0) + res = q2t_cut_cmd_data_head(cmd, rel_offs); + + return res; +} + +/* No locks, thread context */ +static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, + struct srr_imm *imm) +{ + notify24xx_entry_t *ntfy = &imm->imm.notify_entry24; + struct q2t_cmd *cmd = sctio->cmd; + + TRACE_ENTRY(); + + switch(ntfy->srr_ui) { + case SRR_IU_STATUS: + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(ha, ntfy, + NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irq(&ha->hardware_lock); + __q24_xmit_response(cmd, Q2T_XMIT_STATUS); + break; + case SRR_IU_DATA_IN: + cmd->bufflen = scst_cmd_get_resp_data_len(cmd->scst_cmd); + if (q2t_has_data(cmd)) { + uint32_t offset; + int xmit_type; + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); + if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) + goto out_reject; + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(ha, ntfy, + NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irq(&ha->hardware_lock); + __q24_xmit_response(cmd, xmit_type); + } else { + PRINT_ERROR("qla2x00tgt(%ld): SRR for in data for cmd " + "without them (tag %d, SCSI status %d), " + "reject", ha->instance, cmd->tag, + scst_cmd_get_status(cmd->scst_cmd)); + goto out_reject; + } + break; + case SRR_IU_DATA_OUT: + cmd->bufflen = scst_cmd_get_bufflen(cmd->scst_cmd); + if (q2t_has_data(cmd)) { + uint32_t offset; + int xmit_type; + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); + if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) + goto out_reject; + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(ha, ntfy, + NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irq(&ha->hardware_lock); + __q2t_rdy_to_xfer(cmd); + } else { + PRINT_ERROR("qla2x00tgt(%ld): SRR for out data for cmd " + "without them (tag %d, SCSI status %d), " + "reject", ha->instance, cmd->tag, + scst_cmd_get_status(cmd->scst_cmd)); + goto out_reject; + } + break; + default: + PRINT_ERROR("qla2x00tgt(%ld): Unknown srr_ui value %x", + ha->instance, ntfy->srr_ui); + goto out_unmap_reject; + } + +out: + TRACE_EXIT(); + return; + +out_unmap_reject: + if (q2t_has_data(sctio->cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + } + +out_reject: + spin_lock_irq(&ha->hardware_lock); + q24_send_notify_ack(ha, ntfy, NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + if (cmd->state == Q2T_STATE_NEED_DATA) { + cmd->state = Q2T_STATE_DATA_IN; + + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + + scst_rx_data(cmd->scst_cmd, SCST_RX_STATUS_ERROR, + SCST_CONTEXT_THREAD); + } else + q24_send_term_exchange(ha, cmd, &cmd->atio.atio7, 1); + spin_unlock_irq(&ha->hardware_lock); + goto out; +} + +/* No locks, thread context */ +static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, + struct srr_imm *imm) +{ + notify_entry_t *ntfy = &imm->imm.notify_entry; + struct q2t_cmd *cmd = sctio->cmd; + + TRACE_ENTRY(); + + switch(ntfy->srr_ui) { + case SRR_IU_STATUS: + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(ha, ntfy, 0, 0, 0, + NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irq(&ha->hardware_lock); + __q2x_xmit_response(cmd, Q2T_XMIT_STATUS); + break; + case SRR_IU_DATA_IN: + cmd->bufflen = scst_cmd_get_resp_data_len(cmd->scst_cmd); + if (q2t_has_data(cmd)) { + uint32_t offset; + int xmit_type; + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs); + if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) + goto out_reject; + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(ha, ntfy, 0, 0, 0, + NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irq(&ha->hardware_lock); + __q2x_xmit_response(cmd, xmit_type); + } else { + PRINT_ERROR("qla2x00tgt(%ld): SRR for in data for cmd " + "without them (tag %d, SCSI status %d), " + "reject", ha->instance, cmd->tag, + scst_cmd_get_status(cmd->scst_cmd)); + goto out_reject; + } + break; + case SRR_IU_DATA_OUT: + cmd->bufflen = scst_cmd_get_bufflen(cmd->scst_cmd); + if (q2t_has_data(cmd)) { + uint32_t offset; + int xmit_type; + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs); + if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0) + goto out_reject; + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(ha, ntfy, 0, 0, 0, + NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irq(&ha->hardware_lock); + __q2t_rdy_to_xfer(cmd); + } else { + PRINT_ERROR("qla2x00tgt(%ld): SRR for out data for cmd " + "without them (tag %d, SCSI status %d), " + "reject", ha->instance, cmd->tag, + scst_cmd_get_status(cmd->scst_cmd)); + goto out_reject; + } + break; + default: + PRINT_ERROR("qla2x00tgt(%ld): Unknown srr_ui value %x", + ha->instance, ntfy->srr_ui); + goto out_unmap_reject; + } + +out: + TRACE_EXIT(); + return; + +out_unmap_reject: + if (q2t_has_data(sctio->cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + } + +out_reject: + spin_lock_irq(&ha->hardware_lock); + q2x_send_notify_ack(ha, ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + if (cmd->state == Q2T_STATE_NEED_DATA) { + cmd->state = Q2T_STATE_DATA_IN; + + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); + + scst_rx_data(cmd->scst_cmd, SCST_RX_STATUS_ERROR, + SCST_CONTEXT_THREAD); + } else + q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 1); + spin_unlock_irq(&ha->hardware_lock); + goto out; +} + +static void q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm, + int ha_locked) +{ + if (!ha_locked) + spin_lock_irq(&ha->hardware_lock); + + if (IS_FWI2_CAPABLE(ha)) { + q24_send_notify_ack(ha, &imm->imm.notify_entry24, + NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + } else { + q2x_send_notify_ack(ha, &imm->imm.notify_entry, + 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + } + + if (!ha_locked) + spin_unlock_irq(&ha->hardware_lock); + + kfree(imm); + return; +} + +static void q2t_handle_srr_work(struct work_struct *work) +{ + struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, srr_work); + scsi_qla_host_t *ha = tgt->ha; + struct srr_ctio *sctio; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("SRR work (tgt %p)", tgt); + +restart: + spin_lock_irq(&tgt->srr_lock); + list_for_each_entry(sctio, &tgt->srr_ctio_list, srr_list_entry) { + struct srr_imm *imm; + struct q2t_cmd *cmd; + struct srr_imm *i, *ti; + + imm = NULL; + list_for_each_entry_safe(i, ti, &tgt->srr_imm_list, + srr_list_entry) { + if (i->srr_id == sctio->srr_id) { + list_del(&i->srr_list_entry); + if (imm) { + PRINT_ERROR("qla2x00tgt(%ld): There must " + "be only one IMM SRR per CTIO SRR " + "(IMM SRR %p, id %d, CTIO %p", + ha->instance, i, i->srr_id, sctio); + q2t_reject_free_srr_imm(ha, i, 0); + } else + imm = i; + } + } + + TRACE_MGMT_DBG("IMM SRR %p, CTIO SRR %p (id %d)", imm, sctio, + sctio->srr_id); + + if (imm == NULL) { + TRACE_MGMT_DBG("Not found matching IMM for SRR CTIO " + "(id %d)", sctio->srr_id); + continue; + } else + list_del(&sctio->srr_list_entry); + + spin_unlock_irq(&tgt->srr_lock); + + cmd = sctio->cmd; + + /* Restore the originals, except bufflen */ + cmd->offset = scst_cmd_get_ppl_offset(cmd->scst_cmd); + if (cmd->free_sg) { + kfree(cmd->sg); + cmd->free_sg = 0; + } + cmd->sg = scst_cmd_get_sg(cmd->scst_cmd); + cmd->sg_cnt = scst_cmd_get_sg_cnt(cmd->scst_cmd); + + TRACE_MGMT_DBG("SRR cmd %p (scst_cmd %p, tag %d, op %x), " + "sg_cnt=%d, offset=%d", cmd, cmd->scst_cmd, + cmd->tag, cmd->scst_cmd->cdb[0], cmd->sg_cnt, + cmd->offset); + + if (IS_FWI2_CAPABLE(ha)) + q24_handle_srr(ha, sctio, imm); + else + q2x_handle_srr(ha, sctio, imm); + + kfree(imm); + kfree(sctio); + goto restart; + } + spin_unlock_irq(&tgt->srr_lock); + + TRACE_EXIT(); + return; +} + +/* ha->hardware_lock supposed to be held on entry */ +static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb) +{ + struct srr_imm *imm; + struct q2t_tgt *tgt = ha->tgt; + notify_entry_t *iocb2x = (notify_entry_t *)iocb; + notify24xx_entry_t *iocb24 = (notify24xx_entry_t *)iocb; + struct srr_ctio *sctio; + + tgt->imm_srr_id++; + + TRACE(TRACE_MGMT_MINOR, "qla2x00tgt(%ld): IMM NTFY SRR " + "received", ha->instance); + + imm = kzalloc(sizeof(*imm), GFP_ATOMIC); + if (imm != NULL) { + memcpy(&imm->imm.notify_entry, iocb, + sizeof(imm->imm.notify_entry)); + + /* IRQ is already OFF */ + spin_lock(&tgt->srr_lock); + imm->srr_id = tgt->imm_srr_id; + list_add_tail(&imm->srr_list_entry, + &tgt->srr_imm_list); + TRACE_MGMT_DBG("IMM NTFY SRR %p added (id %d, ui %x)", imm, + imm->srr_id, iocb24->srr_ui); + if (tgt->imm_srr_id == tgt->ctio_srr_id) { + int found = 0; + list_for_each_entry(sctio, &tgt->srr_ctio_list, + srr_list_entry) { + if (sctio->srr_id == imm->srr_id) { + found = 1; + break; + } + } + if (found) { + TRACE_MGMT_DBG("%s", "Scheduling srr work"); + schedule_work(&tgt->srr_work); + } else { + TRACE(TRACE_MGMT, "qla2x00tgt(%ld): imm_srr_id " + "== ctio_srr_id (%d), but there is no " + "corresponding SRR CTIO, deleting IMM " + "SRR %p", ha->instance, tgt->ctio_srr_id, + imm); + list_del(&imm->srr_list_entry); + + kfree(imm); + + spin_unlock(&tgt->srr_lock); + goto out_reject; + } + } + spin_unlock(&tgt->srr_lock); + } else { + struct srr_ctio *ts; + + PRINT_CRIT_ERROR("qla2x00tgt(%ld): Unable to allocate SRR IMM " + "entry, SRR request will be rejected", ha->instance); + + /* IRQ is already OFF */ + spin_lock(&tgt->srr_lock); + list_for_each_entry_safe(sctio, ts, &tgt->srr_ctio_list, + srr_list_entry) { + if (sctio->srr_id == tgt->imm_srr_id) { + TRACE_MGMT_DBG("CTIO SRR %p deleted " + "(id %d)", sctio, sctio->srr_id); + list_del(&sctio->srr_list_entry); + if (IS_FWI2_CAPABLE(ha)) { + q24_send_term_exchange(ha, sctio->cmd, + &sctio->cmd->atio.atio7, 1); + } else { + q2x_send_term_exchange(ha, sctio->cmd, + &sctio->cmd->atio.atio2x, 1); + } + kfree(sctio); + } + } + spin_unlock(&tgt->srr_lock); + goto out_reject; + } + +out: + return; + +out_reject: + if (IS_FWI2_CAPABLE(ha)) { + q24_send_notify_ack(ha, iocb24, + NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + } else { + q2x_send_notify_ack(ha, iocb2x, + 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + } + goto out; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb) +{ + uint16_t status; + uint32_t add_flags = 0; + int send_notify_ack = 1; + notify_entry_t *iocb2x = (notify_entry_t *)iocb; + notify24xx_entry_t *iocb24 = (notify24xx_entry_t *)iocb; + + TRACE_ENTRY(); + + status = le16_to_cpu(iocb2x->status); + + TRACE_BUFF_FLAG(TRACE_BUFF, "IMMED Notify Coming Up", + iocb, sizeof(*iocb2x)); + + switch (status) { + case IMM_NTFY_LIP_RESET: + { + if (IS_FWI2_CAPABLE(ha)) { + TRACE(TRACE_MGMT, "LIP reset (loop %#x), subcode %x", + le16_to_cpu(iocb24->nport_handle), + iocb24->status_subcode); + } else { + TRACE(TRACE_MGMT, "LIP reset (I %#x)", + GET_TARGET_ID(ha, iocb2x)); + /* set the Clear LIP reset event flag */ + add_flags |= NOTIFY_ACK_CLEAR_LIP_RESET; + } + tgt_data.mark_all_devices_lost(ha, 1); + if (q2t_reset(ha, iocb, Q2T_ABORT_ALL) == 0) + send_notify_ack = 0; + break; + } + + case IMM_NTFY_LIP_LINK_REINIT: + { + struct q2t_tgt *tgt = ha->tgt; + TRACE(TRACE_MGMT, "LINK REINIT (loop %#x, subcode %x)", + le16_to_cpu(iocb24->nport_handle), + iocb24->status_subcode); + if (tgt->link_reinit_iocb_pending) + q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0); + memcpy(&tgt->link_reinit_iocb, iocb24, sizeof(*iocb24)); + tgt->link_reinit_iocb_pending = 1; + /* + * QLogic requires to wait after LINK REINIT for possible + * PDISC or ADISC ELS commands + */ + send_notify_ack = 0; + break; + } + + case IMM_NTFY_PORT_LOGOUT: + TRACE(TRACE_MGMT, "Port logout (S %08x -> L %#x)", + le16_to_cpu(iocb2x->seq_id), le16_to_cpu(iocb2x->lun)); + tgt_data.mark_all_devices_lost(ha, 1); + if (q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS) == 0) + send_notify_ack = 0; + /* The sessions will be cleared in the callback, if needed */ + break; + + case IMM_NTFY_GLBL_TPRLO: + TRACE(TRACE_MGMT, "Global TPRLO (%x)" ,status); + tgt_data.mark_all_devices_lost(ha, 1); + if (q2t_reset(ha, iocb, Q2T_NEXUS_LOSS) == 0) + send_notify_ack = 0; + /* The sessions will be cleared in the callback, if needed */ + break; + + case IMM_NTFY_PORT_CONFIG: + TRACE(TRACE_MGMT, "Port config changed (%x)", status); + tgt_data.mark_all_devices_lost(ha, 1); + if (q2t_reset(ha, iocb, Q2T_ABORT_ALL) == 0) + send_notify_ack = 0; + /* The sessions will be cleared in the callback, if needed */ + break; + + case IMM_NTFY_GLBL_LOGO: + PRINT_ERROR("qla2x00tgt(%ld): Link failure detected", + ha->instance); + /* I_T nexus loss */ + tgt_data.mark_all_devices_lost(ha, 1); + if (q2t_reset(ha, iocb, Q2T_NEXUS_LOSS) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_IOCB_OVERFLOW: + PRINT_ERROR("qla2x00tgt(%ld): Cannot provide requested " + "capability (IOCB overflowed the immediate notify " + "resource count)", ha->instance); + break; + + case IMM_NTFY_ABORT_TASK: + TRACE(TRACE_MGMT_MINOR, "Abort Task (S %08x I %#x -> L %#x)", + le16_to_cpu(iocb2x->seq_id), GET_TARGET_ID(ha, iocb2x), + le16_to_cpu(iocb2x->lun)); + if (q2t_abort_task(ha, iocb2x) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_RESOURCE: + PRINT_ERROR("qla2x00tgt(%ld): Out of resources, host %ld", + ha->instance, ha->host_no); + break; + + case IMM_NTFY_MSG_RX: + TRACE(TRACE_MGMT, "Immediate notify task %x", iocb2x->task_flags); + if (q2t_handle_task_mgmt(ha, iocb2x) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_ELS: + if (q24_handle_els(ha, iocb24) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_SRR: + q2t_prepare_srr_imm(ha, iocb); + send_notify_ack = 0; + break; + + default: + PRINT_ERROR("qla2x00tgt(%ld): Received unknown immediate " + "notify status %x", ha->instance, status); + break; + } + + if (send_notify_ack) { + if (IS_FWI2_CAPABLE(ha)) + q24_send_notify_ack(ha, iocb24, 0, 0, 0); + else + q2x_send_notify_ack(ha, iocb2x, add_flags, 0, 0, 0, + 0, 0); + } + + TRACE_EXIT(); + return; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio) +{ + ctio_ret_entry_t *ctio; + + TRACE_ENTRY(); + + /* Sending marker isn't necessary, since we called from ISR */ + + ctio = (ctio_ret_entry_t *)tgt_data.req_pkt(ha); + if (ctio == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; + } + + ctio->entry_type = CTIO_RET_TYPE; + ctio->entry_count = 1; + ctio->handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->scsi_status = __constant_cpu_to_le16(SAM_STAT_BUSY); + ctio->residual = atio->data_length; + if (ctio->residual != 0) + ctio->scsi_status |= SS_RESIDUAL_UNDER; + + /* Set IDs */ + SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio)); + ctio->rx_id = atio->rx_id; + + ctio->flags = __constant_cpu_to_le16(OF_SSTS | OF_FAST_POST | + OF_NO_DATA | OF_SS_MODE_1); + ctio->flags |= __constant_cpu_to_le16(OF_INC_RC); + /* + * CTIO from fw w/o scst_cmd doesn't provide enough info to retry it, + * if the explicit conformation is used. + */ + + TRACE_BUFFER("CTIO BUSY packet data", ctio, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); out: TRACE_EXIT(); return; } -/* ha->hardware_lock supposed to be held on entry */ -static void q2t_handle_imm_notify(scsi_qla_host_t *ha, - struct notify_entry *iocb) +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio, + uint16_t status) { - uint16_t status; - int loop_id; - uint32_t add_flags = 0; - int send_notify_ack = 1; + ctio7_status1_entry_t *ctio; + struct q2t_sess *sess; TRACE_ENTRY(); - status = le16_to_cpu(iocb->status); - loop_id = GET_TARGET_ID(ha, iocb); - - if (!ha->flags.enable_target_mode || ha->tgt == NULL) { - TRACE(TRACE_MGMT_DEBUG|TRACE_SCSI|TRACE_DEBUG, - "Acking %04x S %04x I %#x -> L %#x", status, - le16_to_cpu(iocb->seq_id), loop_id, - le16_to_cpu(iocb->lun)); + sess = q2t_find_sess_by_s_id(ha->tgt, atio->fcp_hdr.s_id); + if (sess == NULL) { + q24_send_term_exchange(ha, NULL, atio, 1); goto out; } - TRACE_BUFFER("IMMED Notify Coming Up", iocb, sizeof(*iocb)); + /* Sending marker isn't necessary, since we called from ISR */ - switch (status) { - case IMM_NTFY_LIP_RESET: - TRACE(TRACE_MGMT, "LIP reset (I %#x)", loop_id); - /* - * ToDo: doing so we reset all holding RESERVE'ations, - * which could be unexpected, so be more carefull here - */ - q2t_clear_tgt_db(ha->tgt); - /* set the Clear LIP reset event flag */ - add_flags |= NOTIFY_ACK_CLEAR_LIP_RESET; - break; - - case IMM_NTFY_IOCB_OVERFLOW: - PRINT_ERROR("qla2x00tgt(%ld): Cannot provide requested " - "capability (IOCB overflow)", ha->host_no); - break; - - case IMM_NTFY_ABORT_TASK: - TRACE(TRACE_MGMT_MINOR, "Abort Task (S %04x I %#x -> L %#x)", - le16_to_cpu(iocb->seq_id), loop_id, - le16_to_cpu(iocb->lun)); - if (q2t_abort_task(ha, iocb) == 0) - send_notify_ack = 0; - break; - - case IMM_NTFY_PORT_LOGOUT: - TRACE(TRACE_MGMT, "Port logout (S %04x I %#x -> L %#x)", - le16_to_cpu(iocb->seq_id), loop_id, - le16_to_cpu(iocb->lun)); - /* - * ToDo: doing so we reset all holding RESERVE'ations, - * which could be unexpected, so be more carefull here - */ - q2t_port_logout(ha, loop_id); - break; - - case IMM_NTFY_PORT_CONFIG: - case IMM_NTFY_GLBL_TPRLO: - case IMM_NTFY_GLBL_LOGO: - /* ToDo: ports DB changes handling ?? */ - TRACE(TRACE_MGMT, "Port config changed, Global TPRLO or " - "Global LOGO (%d)", status); - /* - * ToDo: doing so we reset all holding RESERVE'ations, - * which could be unexpected, so be more carefull here - */ - q2t_clear_tgt_db(ha->tgt); - break; - - case IMM_NTFY_RESOURCE: - PRINT_ERROR("qla2x00tgt(%ld): Out of resources, host %ld", - ha->host_no, ha->host_no); - break; - - case IMM_NTFY_MSG_RX: - TRACE(TRACE_MGMT, "Immediate notify task %x", iocb->task_flags); - if (q2t_handle_task_mgmt(ha, iocb) == 0) - send_notify_ack = 0; - break; - - default: - PRINT_ERROR("qla2x00tgt(%ld): Received unknown immediate " - "notify status %x", ha->host_no, status); - break; + ctio = (ctio7_status1_entry_t *)tgt_data.req_pkt(ha); + if (ctio == NULL) { + PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate " + "request packet", ha->instance, __func__); + goto out; } + ctio->common.entry_type = CTIO_TYPE7; + ctio->common.entry_count = 1; + ctio->common.handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->common.nport_handle = sess->loop_id; + ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT); + ctio->common.initiator_id[0] = atio->fcp_hdr.s_id[2]; + ctio->common.initiator_id[1] = atio->fcp_hdr.s_id[1]; + ctio->common.initiator_id[2] = atio->fcp_hdr.s_id[0]; + ctio->common.exchange_addr = atio->exchange_addr; + ctio->flags = (atio->attr << 9) | __constant_cpu_to_le16( + CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS | + CTIO7_FLAGS_DONT_RET_CTIO); + /* + * CTIO from fw w/o scst_cmd doesn't provide enough info to retry it, + * if the explicit conformation is used. + */ + ctio->ox_id = swab16(atio->fcp_hdr.ox_id); + ctio->scsi_status = cpu_to_le16(status); + ctio->residual = atio->fcp_cmnd.data_length; + if (ctio->residual != 0) + ctio->scsi_status |= SS_RESIDUAL_UNDER; + + TRACE_BUFFER("CTIO7 BUSY packet data", ctio, REQUEST_ENTRY_SIZE); + + q2t_exec_queue(ha); out: - if (send_notify_ack) - q2t_send_notify_ack(ha, iocb, add_flags, 0, 0); - TRACE_EXIT(); return; } /* ha->hardware_lock supposed to be held on entry */ /* called via callback from qla2xxx */ -static void q2t_response_pkt(scsi_qla_host_t *ha, sts_entry_t *pkt) +static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio) { - struct atio_entry *atio; + int rc; + struct q2t_tgt *tgt = ha->tgt; TRACE_ENTRY(); + if (unlikely(tgt == NULL)) { + TRACE_MGMT_DBG("ATIO pkt, but no tgt (ha %p)", ha); + goto out; + } + + TRACE(TRACE_SCSI, "ATIO pkt %p: type %02x count %02x", + atio, atio->entry_type, atio->entry_count); + + /* + * In tgt_shutdown mode we also should allow all requests to pass. + * Otherwise, some commands can stuck. + */ + + tgt->irq_cmd_count++; + + switch (atio->entry_type) { + case ATIO_TYPE7: + if (unlikely(atio->entry_count > 1) || + unlikely(atio->fcp_cmnd.add_cdb_len != 0)) { + PRINT_ERROR("qla2x00tgt(%ld): Multi entry ATIO7 IOCBs " + "(%d), ie with CDBs>16 bytes (%d), are not " + "supported", ha->instance, atio->entry_count, + atio->fcp_cmnd.add_cdb_len); + break; + } + TRACE_DBG("ATIO_TYPE7 instance %ld " + "lun %Lx read/write %d/%d data_length %04x " + "s_id %x:%x:%x", + ha->instance, atio->fcp_cmnd.lun, + atio->fcp_cmnd.rddata, atio->fcp_cmnd.wrdata, + be32_to_cpu(atio->fcp_cmnd.data_length), + atio->fcp_hdr.s_id[0], atio->fcp_hdr.s_id[1], + atio->fcp_hdr.s_id[2]); + TRACE_BUFFER("Incoming ATIO7 packet data", atio, + REQUEST_ENTRY_SIZE); + PRINT_BUFF_FLAG(TRACE_SCSI, "FCP CDB", atio->fcp_cmnd.cdb, + sizeof(atio->fcp_cmnd.cdb)); + if (unlikely(atio->exchange_addr == + ATIO_EXCHANGE_ADDRESS_UNKNOWN)) { + TRACE(TRACE_OUT_OF_MEM, "qla2x00tgt(%ld): ATIO_TYPE7 " + "received with UNKNOWN exchange address, " + "sending QUEUE_FULL", ha->instance); + q24_send_busy(ha, atio, SAM_STAT_TASK_SET_FULL); + break; + } + if (likely(atio->fcp_cmnd.task_mgmt_flags == 0)) + rc = q2t_send_cmd_to_scst(ha, (atio_t *)atio); + else + rc = q2t_handle_task_mgmt(ha, atio); + if (unlikely(rc != 0)) { + if (rc == -ESRCH) { +#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ + q24_send_busy(ha, atio, SAM_STAT_BUSY); +#else + q24_send_term_exchange(ha, NULL, atio, 1); +#endif + } else { + PRINT_INFO("qla2x00tgt(%ld): Unable to send " + "command to SCST, sending BUSY status", + ha->instance); + q24_send_busy(ha, atio, SAM_STAT_BUSY); + } + } + break; + + case IMMED_NOTIFY_TYPE: + { + notify_entry_t *pkt = (notify_entry_t *)atio; + if (unlikely(pkt->entry_status != 0)) { + PRINT_ERROR("qla2x00tgt(%ld): Received ATIO packet %x " + "with error status %x", ha->instance, + pkt->entry_type, pkt->entry_status); + break; + } + TRACE_DBG("%s", "IMMED_NOTIFY ATIO"); + q2t_handle_imm_notify(ha, pkt); + break; + } + + default: + PRINT_ERROR("qla2x00tgt(%ld): Received unknown ATIO atio " + "type %x", ha->instance, atio->entry_type); + break; + } + + tgt->irq_cmd_count--; + +out: + TRACE_EXIT(); + return; +} + +/* ha->hardware_lock supposed to be held on entry */ +/* called via callback from qla2xxx */ +static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) +{ + struct q2t_tgt *tgt = ha->tgt; + + TRACE_ENTRY(); + + if (unlikely(tgt == NULL)) { + PRINT_ERROR("Response pkt %x received, but no tgt (ha %p)", + pkt->entry_type, ha); + goto out; + } + TRACE(TRACE_SCSI, "pkt %p: T %02x C %02x S %02x handle %#x", pkt, pkt->entry_type, pkt->entry_count, pkt->entry_status, pkt->handle); - if (unlikely(ha->tgt == NULL)) { - TRACE_DBG("response pkt, but no tgt. ha %p tgt_flag %d", - ha, ha->flags.enable_target_mode); - goto out; + /* + * In tgt_shutdown mode we also should allow all requests to pass. + * Otherwise, some commands can stuck. + */ + + if (unlikely(pkt->entry_status != 0)) { + PRINT_ERROR("qla2x00tgt(%ld): Received response packet %x " + "with error status %x", ha->instance, pkt->entry_type, + pkt->entry_status); + switch (pkt->entry_type) { + case ACCEPT_TGT_IO_TYPE: + case IMMED_NOTIFY_TYPE: + case ABTS_RECV_24XX: + goto out; + default: + break; + } } - if (pkt->entry_status != 0) { - PRINT_ERROR("qla2x00tgt(%ld): Received response packet %x " - "with error status %x", ha->host_no, pkt->entry_type, - pkt->entry_status); - goto out; - } + tgt->irq_cmd_count++; switch (pkt->entry_type) { + case CTIO_TYPE7: + { + ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt; + TRACE_DBG("CTIO_TYPE7: instance %ld", + ha->instance); + TRACE_BUFFER("Incoming CTIO7 packet data", entry, + REQUEST_ENTRY_SIZE); + q2t_do_ctio_completion(ha, entry->handle, + le16_to_cpu(entry->status)|(pkt->entry_status << 16), + entry); + break; + } + case ACCEPT_TGT_IO_TYPE: - if (ha->flags.enable_target_mode && ha->tgt != NULL) { - int rc; - atio = (struct atio_entry *)pkt; - TRACE_DBG("ACCEPT_TGT_IO host_no %ld status %04x " - "lun %04x read/write %d data_length %08x " - "target_id %02x exchange_id %04x ", - ha->host_no, le16_to_cpu(atio->status), - le16_to_cpu(atio->lun), - atio->execution_codes, - le32_to_cpu(atio->data_length), - GET_TARGET_ID(ha, atio), - le16_to_cpu(atio->exchange_id)); - if (atio->status != - __constant_cpu_to_le16(ATIO_CDB_VALID)) { - PRINT_ERROR("qla2x00tgt(%ld): ATIO with error " - "status %x received", ha->host_no, - le16_to_cpu(atio->status)); - break; - } - PRINT_BUFF_FLAG(TRACE_SCSI, "CDB", atio->cdb, - sizeof(atio->cdb)); - rc = q2t_send_cmd_to_scst(ha, atio); - if (unlikely(rc != 0)) { - if (rc == -ESRCH) { + { + atio_entry_t *atio; + int rc; + atio = (atio_entry_t *)pkt; + TRACE_DBG("ACCEPT_TGT_IO instance %ld status %04x " + "lun %04x read/write %d data_length %04x " + "target_id %02x rx_id %04x ", + ha->instance, le16_to_cpu(atio->status), + le16_to_cpu(atio->lun), + atio->execution_codes, + le32_to_cpu(atio->data_length), + GET_TARGET_ID(ha, atio), atio->rx_id); + TRACE_BUFFER("Incoming ATIO packet data", atio, + REQUEST_ENTRY_SIZE); + if (atio->status != __constant_cpu_to_le16(ATIO_CDB_VALID)) { + PRINT_ERROR("qla2x00tgt(%ld): ATIO with error " + "status %x received", ha->instance, + le16_to_cpu(atio->status)); + break; + } + TRACE_BUFFER("Incoming ATIO packet data", atio, REQUEST_ENTRY_SIZE); + PRINT_BUFF_FLAG(TRACE_SCSI, "FCP CDB", atio->cdb, + sizeof(atio->cdb)); + rc = q2t_send_cmd_to_scst(ha, (atio_t *)atio); + if (unlikely(rc != 0)) { + if (rc == -ESRCH) { #if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ - q2t_send_busy(ha, atio); + q2x_send_busy(ha, atio); #else - q2t_send_term_exchange(ha, NULL, - atio, 1); + q2x_send_term_exchange(ha, NULL, atio, 1); #endif - } else { - if (!ha->tgt->tgt_shutdown) { - PRINT_INFO("qla2x00tgt(%ld): " - "Unable to send the " - "command to SCSI target " - "mid-level, sending BUSY " - "status", ha->host_no); - } - q2t_send_busy(ha, atio); - } + } else { + PRINT_INFO("qla2x00tgt(%ld): Unable to send " + "command to SCST, sending BUSY status", + ha->instance); + q2x_send_busy(ha, atio); } - } else if (!ha->tgt->tgt_shutdown) { - PRINT_ERROR("qla2x00tgt(%ld): ATIO, but target mode " - "disabled", ha->host_no); } - break; - + } + break; + case CONTINUE_TGT_IO_TYPE: - if (ha->flags.enable_target_mode && ha->tgt != NULL) { - struct ctio_common_entry *entry = - (struct ctio_common_entry *)pkt; - TRACE_DBG("CONTINUE_TGT_IO: host_no %ld", - ha->host_no); - q2t_do_ctio_completion(ha, entry->handle, - le16_to_cpu(entry->status), - entry); - } else if (!ha->tgt->tgt_shutdown) { - PRINT_ERROR("qla2x00tgt(%ld): CTIO, but target mode " - "disabled", ha->host_no); - } + { + ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt; + TRACE_DBG("CONTINUE_TGT_IO: instance %ld", ha->instance); + TRACE_BUFFER("Incoming CTIO packet data", entry, + REQUEST_ENTRY_SIZE); + q2t_do_ctio_completion(ha, entry->handle, + le16_to_cpu(entry->status)|(pkt->entry_status << 16), + entry); break; - + } + case CTIO_A64_TYPE: - if (ha->flags.enable_target_mode && ha->tgt != NULL) { - struct ctio_common_entry *entry = - (struct ctio_common_entry *)pkt; - TRACE_DBG("CTIO_A64: host_no %ld", ha->host_no); - q2t_do_ctio_completion(ha, entry->handle, - le16_to_cpu(entry->status), - entry); - } else if (!ha->tgt->tgt_shutdown) { - PRINT_ERROR("qla2x00tgt(%ld): CTIO_A64, but target " - "mode disabled", ha->host_no); - } + { + ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt; + TRACE_DBG("CTIO_A64: instance %ld", ha->instance); + TRACE_BUFFER("Incoming CTIO_A64 packet data", entry, + REQUEST_ENTRY_SIZE); + q2t_do_ctio_completion(ha, entry->handle, + le16_to_cpu(entry->status)|(pkt->entry_status << 16), + entry); break; - + } + case IMMED_NOTIFY_TYPE: TRACE_DBG("%s", "IMMED_NOTIFY"); - q2t_handle_imm_notify(ha, (struct notify_entry *)pkt); + q2t_handle_imm_notify(ha, (notify_entry_t *)pkt); break; case NOTIFY_ACK_TYPE: - if (ha->tgt == NULL) { - PRINT_ERROR("qla2x00tgt(%ld): NOTIFY_ACK recieved " - "with NULL tgt", ha->host_no); - } else if (ha->tgt->notify_ack_expected > 0) { - struct nack_entry *entry = (struct nack_entry *)pkt; - TRACE_DBG("NOTIFY_ACK seq %04x status %x", + if (tgt->notify_ack_expected > 0) { + nack_entry_t *entry = (nack_entry_t *)pkt; + TRACE_DBG("NOTIFY_ACK seq %08x status %x", le16_to_cpu(entry->seq_id), le16_to_cpu(entry->status)); - ha->tgt->notify_ack_expected--; - if (entry->status != - __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) { + TRACE_BUFFER("Incoming NOTIFY_ACK packet data", pkt, + RESPONSE_ENTRY_SIZE); + tgt->notify_ack_expected--; + if (entry->status != __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) { PRINT_ERROR("qla2x00tgt(%ld): NOTIFY_ACK " - "failed %x", ha->host_no, + "failed %x", ha->instance, le16_to_cpu(entry->status)); } } else { PRINT_ERROR("qla2x00tgt(%ld): Unexpected NOTIFY_ACK " - "received", ha->host_no); + "received", ha->instance); } break; - case MODIFY_LUN_TYPE: - if ((ha->tgt != NULL) && (ha->tgt->modify_lun_expected > 0)) { - struct q2t_tgt *tgt = ha->tgt; - struct modify_lun_entry *entry = - (struct modify_lun_entry *)pkt; - TRACE_DBG("MODIFY_LUN %x, imm %c%d, cmd %c%d", - entry->status, - (entry->operators & MODIFY_LUN_IMM_ADD) ? '+' - : (entry->operators & MODIFY_LUN_IMM_SUB) ? - '-' : ' ', - entry->immed_notify_count, - (entry->operators & MODIFY_LUN_CMD_ADD) ? '+' - : (entry->operators & MODIFY_LUN_CMD_SUB) ? - '-' : ' ', - entry->command_count); + case ABTS_RECV_24XX: + TRACE_DBG("ABTS_RECV_24XX: instance %ld", ha->instance); + TRACE_BUFF_FLAG(TRACE_BUFF, "Incoming ABTS_RECV " + "packet data", pkt, REQUEST_ENTRY_SIZE); + q24_handle_abts(ha, (abts24_recv_entry_t *)pkt); + break; + + case ABTS_RESP_24XX: + if (tgt->abts_resp_expected > 0) { + abts24_resp_fw_entry_t *entry = + (abts24_resp_fw_entry_t *)pkt; + TRACE_DBG("ABTS_RESP_24XX: compl_status %x", + entry->compl_status); + TRACE_BUFF_FLAG(TRACE_BUFF, "Incoming ABTS_RESP " + "packet data", pkt, REQUEST_ENTRY_SIZE); + tgt->abts_resp_expected--; + if (le16_to_cpu(entry->compl_status) != ABTS_RESP_COMPL_SUCCESS) { + if ((entry->error_subcode1 == 0x1E) && + (entry->error_subcode2 == 0)) { + /* + * We've got a race here: aborted exchange not + * terminated, i.e. response for the aborted + * command was sent between the abort request + * was received and processed. Unfortunately, + * the firmware has a silly requirement that + * all aborted exchanges must be explicitely + * terminated, otherwise it refuses to send + * responses for the abort requests. So, we + * have to (re)terminate the exchange and + * retry the abort response. + */ + q24_retry_term_exchange(ha, entry); + } else + PRINT_ERROR("qla2x00tgt(%ld): ABTS_RESP_24XX " + "failed %x (subcode %x:%x)", ha->instance, + entry->compl_status, entry->error_subcode1, + entry->error_subcode2); + } + } else { + PRINT_ERROR("qla2x00tgt(%ld): Unexpected ABTS_RESP_24XX " + "received", ha->instance); + } + break; + + case MODIFY_LUN_TYPE: + if (tgt->modify_lun_expected > 0) { + modify_lun_entry_t *entry = (modify_lun_entry_t *)pkt; + TRACE_DBG("MODIFY_LUN %x, imm %c%d, cmd %c%d", + entry->status, + (entry->operators & MODIFY_LUN_IMM_ADD) ?'+' + :(entry->operators & MODIFY_LUN_IMM_SUB) ?'-' + :' ', + entry->immed_notify_count, + (entry->operators & MODIFY_LUN_CMD_ADD) ?'+' + :(entry->operators & MODIFY_LUN_CMD_SUB) ?'-' + :' ', + entry->command_count); tgt->modify_lun_expected--; if (entry->status != MODIFY_LUN_SUCCESS) { PRINT_ERROR("qla2x00tgt(%ld): MODIFY_LUN " - "failed %x", ha->host_no, + "failed %x", ha->instance, entry->status); } } else { PRINT_ERROR("qla2x00tgt(%ld): Unexpected MODIFY_LUN " - "received", (ha != NULL) ? ha->host_no : -1); + "received", (ha != NULL) ? (long)ha->instance : -1); } break; case ENABLE_LUN_TYPE: - if (ha->tgt != NULL) { - struct elun_entry *entry = (struct elun_entry *)pkt; - TRACE_DBG("ENABLE_LUN %x imm %u cmd %u ", - entry->status, entry->immed_notify_count, - entry->command_count); - if ((ha->flags.enable_target_mode) && - (entry->status == ENABLE_LUN_ALREADY_ENABLED)) { - TRACE_DBG("LUN is already enabled: %#x", - entry->status); - entry->status = ENABLE_LUN_SUCCESS; - } else if (entry->status == ENABLE_LUN_RC_NONZERO) { - TRACE_DBG("ENABLE_LUN succeeded, but with " - "error: %#x", entry->status); - entry->status = ENABLE_LUN_SUCCESS; - } else if (entry->status != ENABLE_LUN_SUCCESS) { - PRINT_ERROR("qla2x00tgt(%ld): ENABLE_LUN " - "failed %x", - ha->host_no, entry->status); - ha->flags.enable_target_mode = - ~ha->flags.enable_target_mode; - } /* else success */ - } - break; - - default: - PRINT_INFO("qla2x00tgt(%ld): Received unknown response pkt " - "type %x", ha->host_no, pkt->entry_type); + { + elun_entry_t *entry = (elun_entry_t *)pkt; + TRACE_DBG("ENABLE_LUN %x imm %u cmd %u ", + entry->status, entry->immed_notify_count, + entry->command_count); + if (entry->status == ENABLE_LUN_ALREADY_ENABLED) { + TRACE_DBG("LUN is already enabled: %#x", + entry->status); + entry->status = ENABLE_LUN_SUCCESS; + } else if (entry->status == ENABLE_LUN_RC_NONZERO) { + TRACE_DBG("ENABLE_LUN succeeded, but with " + "error: %#x", entry->status); + entry->status = ENABLE_LUN_SUCCESS; + } else if (entry->status != ENABLE_LUN_SUCCESS) { + PRINT_ERROR("qla2x00tgt(%ld): ENABLE_LUN " + "failed %x", ha->instance, entry->status); + qla_clear_tgt_mode(ha); + } /* else success */ break; } + default: + PRINT_ERROR("qla2x00tgt(%ld): Received unknown response pkt " + "type %x", ha->instance, pkt->entry_type); + break; + } + + tgt->irq_cmd_count--; + out: TRACE_EXIT(); return; } -/* ha->hardware_lock supposed to be held on entry */ -/* called via callback from qla2xxx */ +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha, uint16_t *mailbox) { + struct q2t_tgt *tgt = ha->tgt; + TRACE_ENTRY(); - sBUG_ON(ha == NULL); - - if (unlikely(ha->tgt == NULL)) { - TRACE(TRACE_DEBUG|TRACE_MGMT, - "ASYNC EVENT %#x, but no tgt. ha %p tgt_flag %d", - code, ha, ha->flags.enable_target_mode); + if (unlikely(tgt == NULL)) { + TRACE_DBG("ASYNC EVENT %#x, but no tgt (ha %p)", code, ha); goto out; } + /* + * In tgt_shutdown mode we also should allow all requests to pass. + * Otherwise, some commands can stuck. + */ + + tgt->irq_cmd_count++; + switch (code) { case MBA_RESET: /* Reset */ case MBA_SYSTEM_ERR: /* System Error */ case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ - case MBA_LOOP_DOWN: - case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ - case MBA_LIP_RESET: /* LIP reset occurred */ - case MBA_POINT_TO_POINT: /* Point to point mode. */ - case MBA_CHG_IN_CONNECTION: /* Change in connection mode. */ - TRACE_MGMT_DBG("Async event %#x occured: clear tgt_db", code); -#if 0 - /* - * ToDo: doing so we reset all holding RESERVE'ations, - * which could be unexpected, so be more carefull here - */ - q2t_clear_tgt_db(ha->tgt); -#endif - break; - case MBA_RSCN_UPDATE: - TRACE_MGMT_DBG("RSCN Update (%x) N_Port %#06x (fmt %x)", code, - ((mailbox[1] & 0xFF) << 16) | le16_to_cpu(mailbox[2]), - (mailbox[1] & 0xFF00) >> 8); - break; - - case MBA_PORT_UPDATE: /* Port database update occurred */ - TRACE_MGMT_DBG("Port DB Chng: L_ID %#4x did %d: ignore", - le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2])); + case MBA_ATIO_TRANSFER_ERR: /* ATIO Queue Transfer Error */ + TRACE(TRACE_MGMT, "System error async event %#x occured", code); break; case MBA_LOOP_UP: - default: - TRACE_MGMT_DBG("Async event %#x occured: ignore", code); - /* just don't DO anything */ + { + TRACE(TRACE_MGMT, "Async LOOP_UP occured (m[1]=%x, m[2]=%x, " + "m[3]=%x, m[4]=%x)", le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3]), + le16_to_cpu(mailbox[4])); + if (tgt->link_reinit_iocb_pending) { + q24_send_notify_ack(ha, &tgt->link_reinit_iocb, 0, 0, 0); + tgt->link_reinit_iocb_pending = 0; + } break; } + case MBA_LIP_OCCURRED: + case MBA_LOOP_DOWN: + case MBA_LIP_RESET: + TRACE(TRACE_MGMT, "Async event %#x occured (m[1]=%x, m[2]=%x, " + "m[3]=%x, m[4]=%x)", code, + le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), + le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); + break; + + case MBA_PORT_UPDATE: + case MBA_RSCN_UPDATE: + TRACE(TRACE_MGMT, "Port update async event %#x occured: " + "updating the ports database (m[1]=%x, m[2]=%x, " + "m[3]=%x, m[4]=%x)", code, + le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), + le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); + /* .mark_all_devices_lost() is handled by the initiator driver */ + break; + + default: + TRACE(TRACE_MGMT, "Async event %#x occured: ignore (m[1]=%x, " + "m[2]=%x, m[3]=%x, m[4]=%x)", code, + le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), + le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); + break; + } + + tgt->irq_cmd_count--; + out: TRACE_EXIT(); return; } +static int q2t_get_target_name(scsi_qla_host_t *ha, char **wwn) +{ + const int wwn_len = 3*WWN_SIZE+2; + int res = 0; + char *name; + + name = kmalloc(wwn_len, GFP_KERNEL); + if (name == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt name failed"); + res = -ENOMEM; + goto out; + } + + sprintf(name, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + ha->port_name[0], ha->port_name[1], + ha->port_name[2], ha->port_name[3], + ha->port_name[4], ha->port_name[5], + ha->port_name[6], ha->port_name[7]); + + *wwn = name; + +out: + return res; +} + +static int q24_get_loop_id(scsi_qla_host_t *ha, atio7_entry_t *atio7, + uint16_t *loop_id) +{ + dma_addr_t gid_list_dma; + struct gid_list_info *gid_list; + char *id_iter; + int res, rc, i; + uint16_t entries; + + TRACE_ENTRY(); + + gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, + &gid_list_dma, GFP_KERNEL); + if (gid_list == NULL) { + PRINT_ERROR("DMA Alloc failed of %zd", GID_LIST_SIZE); + res = -ENOMEM; + goto out; + } + + /* Get list of logged in devices */ + rc = tgt_data.get_id_list(ha, gid_list, gid_list_dma, &entries); + if (rc != QLA_SUCCESS) { + PRINT_ERROR("get_id_list() failed: %x", rc); + res = -1; + goto out_free_id_list; + } + + id_iter = (char *)gid_list; + res = -1; + for (i = 0; i < entries; i++) { + struct gid_list_info *gid = (struct gid_list_info *)id_iter; + if ((gid->al_pa == atio7->fcp_hdr.s_id[2]) && + (gid->area == atio7->fcp_hdr.s_id[1]) && + (gid->domain == atio7->fcp_hdr.s_id[0])) { + *loop_id = le16_to_cpu(gid->loop_id); + res = 0; + break; + } + id_iter += ha->gid_list_info_size; + } + + if (res != 0) { + PRINT_ERROR("Unable to find initiator with S_ID %x:%x:%x", + atio7->fcp_hdr.s_id[2], atio7->fcp_hdr.s_id[1], + atio7->fcp_hdr.s_id[0]); + } + +out_free_id_list: + dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, gid_list, gid_list_dma); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Must be called under tgt_mutex */ +static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio) +{ + struct q2t_sess *sess = NULL; + fc_port_t *fcport = NULL; + uint16_t loop_id = 0xFFFF; /* to remove warning */ + int rc; + + TRACE_ENTRY(); + + if (IS_FWI2_CAPABLE(ha)) { + rc = q24_get_loop_id(ha, (atio7_entry_t *)atio, &loop_id); + if (rc != 0) + goto out; + } else + loop_id = GET_TARGET_ID(ha, (atio_entry_t *)atio); + + fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); + if (fcport == NULL) { + PRINT_ERROR("%s", "Allocation of tmp FC port failed"); + goto out; + } + + TRACE_MGMT_DBG("loop_id %d", loop_id); + + fcport->loop_id = loop_id; + + rc = tgt_data.get_port_database(ha, fcport, 0); + if (rc != QLA_SUCCESS) { + PRINT_ERROR("Failed to retrieve fcport information " + "-- get_port_database() returned %x " + "(loop_id=0x%04x)\n", rc, loop_id); + goto out_free_fcport; + } + + sess = q2t_create_sess(ha, fcport, true); + +out_free_fcport: + kfree(fcport); + +out: + TRACE_EXIT_HRES((unsigned long)sess); + return sess; +} + +static int q2t_exec_sess_work(struct q2t_tgt *tgt, + struct q2t_sess_work_param *prm) +{ + scsi_qla_host_t *ha = tgt->ha; + int res = 0; + struct q2t_sess *sess = NULL; + struct q2t_cmd *cmd = prm->cmd; + atio_t *atio = (atio_t *)&cmd->atio; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("cmd %p", cmd); + + mutex_lock(&ha->tgt_mutex); + spin_lock_irq(&ha->hardware_lock); + + if (tgt->tgt_shutdown) + goto send; + + if (IS_FWI2_CAPABLE(ha)) { + atio7_entry_t *a = (atio7_entry_t *)atio; + sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id); + } else + sess = q2t_find_sess_by_loop_id(tgt, + GET_TARGET_ID(ha, (atio_entry_t *)atio)); + + if (sess != NULL) { + TRACE_MGMT_DBG("sess %p found", sess); + q2t_sess_get(sess); + } else { + /* + * We are under tgt_mutex, so a new sess can't be added + * behind us. + */ + spin_unlock_irq(&ha->hardware_lock); + sess = q2t_make_local_sess(ha, atio); + spin_lock_irq(&ha->hardware_lock); + /* sess has got an extra creation ref */ + } + +send: + if (!tgt->tm_to_unknown && !tgt->tgt_shutdown && (sess != NULL)) { + TRACE_MGMT_DBG("Sending work cmd %p to SCST", cmd); + res = q2t_do_send_cmd_to_scst(ha, cmd, sess); + } else { + /* + * Cmd might be already aborted behind us, so be safe and + * abort it. It was not sent to SCST yet, so pass NULL as + * the second argument. + */ + TRACE_MGMT_DBG("Terminating work cmd %p", cmd); + if (IS_FWI2_CAPABLE(ha)) + q24_send_term_exchange(ha, NULL , &cmd->atio.atio7, 1); + else + q2x_send_term_exchange(ha, NULL, &cmd->atio.atio2x, 1); + q2t_free_cmd(cmd); + } + + if (sess != NULL) + q2t_sess_put(sess); + + spin_unlock_irq(&ha->hardware_lock); + mutex_unlock(&ha->tgt_mutex); + + TRACE_EXIT_RES(res); + return res; +} + +static void q2t_sess_work_fn(struct work_struct *work) +{ + struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, sess_work); + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Sess work (tgt %p)", tgt); + + spin_lock_irq(&tgt->sess_work_lock); + while (!list_empty(&tgt->sess_works_list)) { + int rc; + struct q2t_sess_work_param *prm = list_entry( + tgt->sess_works_list.next, typeof(*prm), + sess_works_list_entry); + + spin_unlock_irq(&tgt->sess_work_lock); + + rc = q2t_exec_sess_work(tgt, prm); + + spin_lock_irq(&tgt->sess_work_lock); + + /* + * Prm must be in the list on the exec time to sync with + * tm_to_unknown clearance + */ + list_del(&prm->sess_works_list_entry); + + if (rc != 0) { + PRINT_CRIT_ERROR("%s","Unable to complete sess work"); + q2t_free_cmd(prm->cmd); + } + + kfree(prm); + } + spin_unlock_irq(&tgt->sess_work_lock); + + spin_lock_irq(&tgt->ha->hardware_lock); + spin_lock(&tgt->sess_work_lock); + if (list_empty(&tgt->sess_works_list)) + tgt->tm_to_unknown = 0; + spin_unlock(&tgt->sess_work_lock); + spin_unlock_irq(&tgt->ha->hardware_lock); + + TRACE_EXIT(); + return; +} + /* ha->hardware_lock supposed to be held and IRQs off */ static void q2t_cleanup_hw_pending_cmd(scsi_qla_host_t *ha, struct q2t_cmd *cmd) { @@ -2024,7 +4900,7 @@ static void q2t_cleanup_hw_pending_cmd(scsi_qla_host_t *ha, struct q2t_cmd *cmd) static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) { struct q2t_cmd *cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); - struct q2t_tgt *tgt = cmd->sess->tgt; + struct q2t_tgt *tgt = cmd->tgt; scsi_qla_host_t *ha = tgt->ha; unsigned long flags; @@ -2037,31 +4913,27 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) if (cmd->state == Q2T_STATE_PROCESSED) { TRACE_MGMT_DBG("Force finishing cmd %p", cmd); - if (q2t_has_data(scst_cmd)) { - pci_unmap_sg(ha->pdev, scst_cmd_get_sg(scst_cmd), - scst_cmd_get_sg_cnt(scst_cmd), - scst_to_tgt_dma_dir( - scst_cmd_get_data_direction(scst_cmd))); + if (q2t_has_data(cmd)) { + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); } } else if (cmd->state == Q2T_STATE_NEED_DATA) { TRACE_MGMT_DBG("Force rx_data cmd %p", cmd); q2t_cleanup_hw_pending_cmd(ha, cmd); - pci_unmap_sg(ha->pdev, scst_cmd_get_sg(scst_cmd), - scst_cmd_get_sg_cnt(scst_cmd), - scst_to_tgt_dma_dir( - scst_cmd_get_data_direction(scst_cmd))); + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, + scst_to_dma_dir(cmd->data_direction)); scst_rx_data(scst_cmd, SCST_RX_STATUS_ERROR_FATAL, SCST_CONTEXT_THREAD); goto out_unlock; } else if (cmd->state == Q2T_STATE_ABORTED) { - TRACE_MGMT_DBG("Force finishing aborted cmd %p (tag %ld)", - cmd, (unsigned long)scst_cmd->tag); + TRACE_MGMT_DBG("Force finishing aborted cmd %p (tag %d)", + cmd, cmd->tag); } else { PRINT_ERROR("qla2x00tgt(%ld): A command in state (%d) should " - "not be HW pending", ha->host_no, cmd->state); + "not be HW pending", ha->instance, cmd->state); goto out_unlock; } @@ -2076,152 +4948,175 @@ out_unlock: return; } -static int q2t_get_target_name(scsi_qla_host_t *ha, char **wwn) -{ - const int wwn_len = 3*WWN_SIZE+2; - int res = 0; - char *name; - - name = kmalloc(wwn_len, GFP_KERNEL); - if (name == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt name failed"); - res = -ENOMEM; - goto out; - } - - sprintf(name, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - ha->port_name[0], ha->port_name[1], - ha->port_name[2], ha->port_name[3], - ha->port_name[4], ha->port_name[5], - ha->port_name[6], ha->port_name[7]); - - *wwn = name; - -out: - return res; -} - -/* no lock held on entry */ -/* called via callback from qla2xxx */ -static void q2t_host_action(scsi_qla_host_t *ha, - enum qla2x_tgt_host_action action) +/* No lock held on entry, process context */ +static int q2t_host_action(scsi_qla_host_t *ha, + qla2x_tgt_host_action_t action) { struct q2t_tgt *tgt = NULL; - unsigned long flags = 0; + int res = 0; TRACE_ENTRY(); sBUG_ON(ha == NULL); + /* To sync with q2t_exit() */ + down_read(&q2t_unreg_rwsem); + switch (action) { case ENABLE_TARGET_MODE: { + struct scst_tgt_template *tgt_template; + fc_port_t *fcport; char *wwn; int sg_tablesize; + if (qla_tgt_mode_enabled(ha)) { + PRINT_INFO("qla2x00tgt(%ld): Target mode already " + "enabled", ha->instance); + break; + } + + PRINT_INFO("qla2x00tgt(%ld): Enabling target mode", + ha->instance); + + if (ha->tgt != NULL) + goto do_enable; + tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); if (tgt == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", - "Allocation of tgt failed"); - goto out; + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt " + "failed"); + res = -ENOMEM; + break; } - + tgt->ha = ha; - INIT_LIST_HEAD(&tgt->sess_list); init_waitqueue_head(&tgt->waitQ); + INIT_LIST_HEAD(&tgt->sess_list); + INIT_LIST_HEAD(&tgt->del_sess_list); + init_timer(&tgt->sess_del_timer); + tgt->sess_del_timer.data = (unsigned long)tgt; + tgt->sess_del_timer.function = q2t_del_sess_timer_fn; + spin_lock_init(&tgt->sess_work_lock); + INIT_WORK(&tgt->sess_work, q2t_sess_work_fn); + INIT_LIST_HEAD(&tgt->sess_works_list); + spin_lock_init(&tgt->srr_lock); + INIT_LIST_HEAD(&tgt->srr_ctio_list); + INIT_LIST_HEAD(&tgt->srr_imm_list); + INIT_WORK(&tgt->srr_work, q2t_handle_srr_work); - if (ha->flags.enable_64bit_addressing) { - PRINT_INFO("qla2x00tgt(%ld): 64 Bit PCI " - "Addressing Enabled", ha->host_no); - tgt->tgt_enable_64bit_addr = 1; - /* 3 is reserved */ - sg_tablesize = - QLA_MAX_SG64(ha->request_q_length - 3); - tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND64; - tgt->datasegs_per_cont = DATASEGS_PER_CONT64; - } else { - PRINT_INFO("qla2x00tgt(%ld): Using 32 Bit " - "PCI Addressing", ha->host_no); - sg_tablesize = - QLA_MAX_SG32(ha->request_q_length - 3); - tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND32; - tgt->datasegs_per_cont = DATASEGS_PER_CONT32; - } + if (IS_FWI2_CAPABLE(ha)) + tgt_template = &tgt24_template; + else + tgt_template = &tgt2x_template; if (q2t_get_target_name(ha, &wwn) != 0) { kfree(tgt); - goto out; + break; } - mutex_lock(&qla_mgmt_mutex); + mutex_lock(&ha->tgt_mutex); + + tgt->scst_tgt = scst_register(tgt_template, wwn); + + /* + * scst_tgt protected by tgt_mutex from not get freed before + * been initialized + */ - tgt->scst_tgt = scst_register(&tgt_template, wwn); kfree(wwn); + if (!tgt->scst_tgt) { PRINT_ERROR("qla2x00tgt(%ld): scst_register() " - "failed for host %ld(%p)", ha->host_no, + "failed for host %ld(%p)", ha->instance, ha->host_no, ha); kfree(tgt); - goto out_unlock; + res = -ENOMEM; + break; + } + + if (IS_FWI2_CAPABLE(ha)) { + PRINT_INFO("qla2400tgt(%ld): using 64 Bit PCI " + "addressing", ha->instance); + tgt->tgt_enable_64bit_addr = 1; + /* 3 is reserved */ + sg_tablesize = + QLA_MAX_SG_24XX(ha->request_q_length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND_24XX; + tgt->datasegs_per_cont = DATASEGS_PER_CONT_24XX; + } else { + if (ha->flags.enable_64bit_addressing) { + PRINT_INFO("qla2x00tgt(%ld): 64 Bit PCI " + "addressing enabled", ha->instance); + tgt->tgt_enable_64bit_addr = 1; + /* 3 is reserved */ + sg_tablesize = + QLA_MAX_SG64(ha->request_q_length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND64; + tgt->datasegs_per_cont = DATASEGS_PER_CONT64; + } else { + PRINT_INFO("qla2x00tgt(%ld): Using 32 Bit " + "PCI addressing", ha->instance); + sg_tablesize = + QLA_MAX_SG32(ha->request_q_length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND32; + tgt->datasegs_per_cont = DATASEGS_PER_CONT32; + } } scst_tgt_set_sg_tablesize(tgt->scst_tgt, sg_tablesize); scst_tgt_set_tgt_priv(tgt->scst_tgt, tgt); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irq(&ha->hardware_lock); ha->tgt = tgt; - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irq(&ha->hardware_lock); - TRACE_DBG("Enable lun for host %ld(%ld,%p)", - ha->host_no, ha->host_no, ha); - tgt_data.enable_lun(ha); + mutex_unlock(&ha->tgt_mutex); - mutex_unlock(&qla_mgmt_mutex); - break; - } - case DISABLE_TARGET_MODE: - mutex_lock(&qla_mgmt_mutex); - - spin_lock_irqsave(&ha->hardware_lock, flags); - if (ha->tgt == NULL) { - /* ensure target mode is marked as off */ - ha->flags.enable_target_mode = 0; - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - if (!ha->flags.host_shutting_down) - tgt_data.disable_lun(ha); - - goto out_unlock; + list_for_each_entry_rcu(fcport, &ha->fcports, list) { + q2t_fc_port_added(ha, fcport); } - tgt = ha->tgt; - spin_unlock_irqrestore(&ha->hardware_lock, flags); +do_enable: + TRACE_DBG("Enable tgt mode for host %ld(%ld,%p)", + ha->host_no, ha->instance, ha); + tgt_data.enable_tgt_mode(ha); + break; + } - mutex_unlock(&qla_mgmt_mutex); + case DISABLE_TARGET_MODE: + if (!qla_tgt_mode_enabled(ha)) { + PRINT_INFO("qla2x00tgt(%ld): Target mode already " + "disabled", ha->instance); + break; + } - TRACE_DBG("Shutting down host %ld(%ld,%p)", - ha->host_no, ha->host_no, ha); - scst_unregister(tgt->scst_tgt); - /* + PRINT_INFO("qla2x00tgt(%ld): Disabling target mode", + ha->instance); + + sBUG_ON(ha->tgt == NULL); + + TRACE_DBG("Unregistering target for host %ld(%p)", + ha->host_no, ha); + scst_unregister(ha->tgt->scst_tgt); + /* * Free of tgt happens via callback q2t_target_release - * called from scst_unregister, so it might be dead here! - * Let's clear it just in case. + * called from scst_unregister, so we shouldn't touch + * it again. */ - tgt = NULL; break; default: - PRINT_ERROR("Unknown action %d", action); + PRINT_ERROR("qla2x00tgt(%ld): %s: unsupported action %d", + ha->instance, __func__, action); + res = -EINVAL; break; } -out: - TRACE_EXIT(); - return; + up_read(&q2t_unreg_rwsem); -out_unlock: - mutex_unlock(&qla_mgmt_mutex); - goto out; + TRACE_EXIT_RES(res); + return res; } #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -2255,23 +5150,70 @@ static ssize_t q2t_proc_log_entry_write(struct file *file, TRACE_EXIT_RES(res); return res; } +#endif +static int q2t_version_info_show(struct seq_file *seq, void *v) +{ + TRACE_ENTRY(); + + seq_printf(seq, "%s\n", Q2T_VERSION_STRING); + +#ifdef CONFIG_SCST_EXTRACHECKS + seq_printf(seq, "EXTRACHECKS\n"); +#endif + +#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD + seq_printf(seq, "DEBUG_WORK_IN_THREAD\n"); +#endif + +#ifdef CONFIG_SCST_TRACING + seq_printf(seq, "TRACING\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG + seq_printf(seq, "DEBUG\n"); +#endif + +#ifdef CONFIG_QLA_TGT_DEBUG_SRR + seq_printf(seq, "DEBUG_SRR\n"); +#endif + + TRACE_EXIT(); + return 0; +} + +static struct scst_proc_data q2t_version_proc_data = { + SCST_DEF_RW_SEQ_OP(NULL) + .show = q2t_version_info_show, +}; + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) static struct scst_proc_data q2t_log_proc_data = { SCST_DEF_RW_SEQ_OP(q2t_proc_log_entry_write) .show = q2t_log_info_show, }; #endif -static int q2t_proc_log_entry_build(struct scst_tgt_template *templ) +static __init int q2t_proc_log_entry_build(struct scst_tgt_template *templ) { int res = 0; -#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) struct proc_dir_entry *p, *root; TRACE_ENTRY(); root = scst_proc_get_tgt_root(templ); if (root) { + p = scst_create_proc_entry(root, Q2T_PROC_VERSION_NAME, + &q2t_version_proc_data); + if (p == NULL) { + PRINT_ERROR("Not enough memory to register " + "target driver %s entry %s in /proc", + templ->name, Q2T_PROC_VERSION_NAME); + res = -ENOMEM; + goto out; + } + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) /* create the proc file entry for the device */ q2t_log_proc_data.data = (void *)templ->name; p = scst_create_proc_entry(root, Q2T_PROC_LOG_ENTRY_NAME, @@ -2281,30 +5223,37 @@ static int q2t_proc_log_entry_build(struct scst_tgt_template *templ) "target driver %s entry %s in /proc", templ->name, Q2T_PROC_LOG_ENTRY_NAME); res = -ENOMEM; - goto out; + goto out_remove_ver; } +#endif } out: - TRACE_EXIT_RES(res); -#endif return res; + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +out_remove_ver: + remove_proc_entry(Q2T_PROC_VERSION_NAME, root); + goto out; +#endif } static void q2t_proc_log_entry_clean(struct scst_tgt_template *templ) { -#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) struct proc_dir_entry *root; TRACE_ENTRY(); root = scst_proc_get_tgt_root(templ); - if (root) + if (root) { +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) remove_proc_entry(Q2T_PROC_LOG_ENTRY_NAME, root); +#endif + remove_proc_entry(Q2T_PROC_VERSION_NAME, root); + } TRACE_EXIT(); -#endif return; } @@ -2314,6 +5263,8 @@ static int __init q2t_init(void) TRACE_ENTRY(); + BUILD_BUG_ON(sizeof(atio7_entry_t) != sizeof(atio_entry_t)); + PRINT_INFO("Initializing QLogic Fibre Channel HBA Driver target mode " "addon version %s", Q2T_VERSION_STRING); @@ -2323,30 +5274,64 @@ static int __init q2t_init(void) goto out; } - res = scst_register_target_template(&tgt_template); + q2t_mgmt_cmd_cachep = KMEM_CACHE(q2t_mgmt_cmd, SCST_SLAB_FLAGS); + if (q2t_mgmt_cmd_cachep == NULL) { + res = -ENOMEM; + goto out_cmd_free; + } + + q2t_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab, + mempool_free_slab, q2t_mgmt_cmd_cachep); + if (q2t_mgmt_cmd_mempool == NULL) { + res = -ENOMEM; + goto out_kmem_free; + } + + res = scst_register_target_template(&tgt2x_template); if (res < 0) - goto out_free_kmem; + goto out_mempool_free; + + res = scst_register_target_template(&tgt24_template); + if (res < 0) + goto out_unreg_target2x; /* - * qla2xxx_tgt_register_driver() happens in q2t_target_detect + * qla2xxx_tgt_register_driver() happens in q2t_target_detect * called via scst_register_target_template() */ - res = q2t_proc_log_entry_build(&tgt_template); - if (res < 0) - goto out_unreg_target; + res = q2t_proc_log_entry_build(&tgt2x_template); + if (res < 0) { + goto out_unreg_target24; + } + + res = q2t_proc_log_entry_build(&tgt24_template); + if (res < 0) { + goto out_unreg_proc2x; + } out: - TRACE_EXIT(); + TRACE_EXIT_RES(res); return res; -out_unreg_target: - scst_unregister_target_template(&tgt_template); +out_unreg_proc2x: + q2t_proc_log_entry_clean(&tgt2x_template); -out_free_kmem: - kmem_cache_destroy(q2t_cmd_cachep); +out_unreg_target24: + scst_unregister_target_template(&tgt24_template); +out_unreg_target2x: + scst_unregister_target_template(&tgt2x_template); qla2xxx_tgt_unregister_driver(); + +out_mempool_free: + mempool_destroy(q2t_mgmt_cmd_mempool); + +out_kmem_free: + kmem_cache_destroy(q2t_mgmt_cmd_cachep); + +out_cmd_free: + kmem_cache_destroy(q2t_cmd_cachep); goto out; } @@ -2354,12 +5339,24 @@ static void __exit q2t_exit(void) { TRACE_ENTRY(); - q2t_proc_log_entry_clean(&tgt_template); + PRINT_INFO("%s", "Unloading QLogic Fibre Channel HBA Driver target " + "mode addon driver"); - scst_unregister_target_template(&tgt_template); + /* To sync with q2t_host_action() */ + down_write(&q2t_unreg_rwsem); + + q2t_proc_log_entry_clean(&tgt24_template); + scst_unregister_target_template(&tgt24_template); + + q2t_proc_log_entry_clean(&tgt2x_template); + scst_unregister_target_template(&tgt2x_template); + + up_write(&q2t_unreg_rwsem); qla2xxx_tgt_unregister_driver(); + mempool_destroy(q2t_mgmt_cmd_mempool); + kmem_cache_destroy(q2t_mgmt_cmd_cachep); kmem_cache_destroy(q2t_cmd_cachep); TRACE_EXIT(); @@ -2369,7 +5366,7 @@ static void __exit q2t_exit(void) module_init(q2t_init); module_exit(q2t_exit); -MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar & Nathaniel Clark"); -MODULE_DESCRIPTION("Target mode logic for qla2xxx"); +MODULE_AUTHOR("Vladislav Bolkhovitin, Leonid Stoljar & Nathaniel Clark"); +MODULE_DESCRIPTION("Target mode addon for qla2[2,3,4+]xx"); MODULE_LICENSE("GPL"); MODULE_VERSION(Q2T_VERSION_STRING); diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index 20c0d3834..f7b24ac21 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -1,17 +1,18 @@ /* * qla2x00t.h - * + * * Copyright (C) 2004 - 2009 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2006 Nathaniel Clark + * Copyright (C) 2006 - 2009 ID7 Ltd. * - * QLogic 2x00 SCSI target driver. - * + * QLogic 22xx/23xx/24xx/25xx FC target driver. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, version 2 * of the License. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -21,24 +22,26 @@ #ifndef __QLA2X00T_H #define __QLA2X00T_H -/* Sneaky hack to skip redefinitions from qla_dbg.h */ -#define __QLA_DBG_H #include -#include "qla2x_tgt_def.h" +#include +#include #include /* Version numbers, the same as for the kernel */ -#define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d)) -#define Q2T_VERSION_CODE Q2T_VERSION(1, 0, 2, 0) -#define Q2T_VERSION_STRING "1.0.2" +#define Q2T_VERSION(a,b,c,d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d)) +#define Q2T_VERSION_CODE Q2T_VERSION(1,0,2,0) +#define Q2T_VERSION_STRING "1.0.2priv" +#define Q2T_PROC_VERSION_NAME "version" #define Q2T_MAX_CDB_LEN 16 #define Q2T_TIMEOUT 10 /* in seconds */ + #define Q2T_MAX_HW_PENDING_TIME 60 /* in seconds */ /* Immediate notify status constants */ #define IMM_NTFY_LIP_RESET 0x000E +#define IMM_NTFY_LIP_LINK_REINIT 0x000F #define IMM_NTFY_IOCB_OVERFLOW 0x0016 #define IMM_NTFY_ABORT_TASK 0x0020 #define IMM_NTFY_PORT_LOGOUT 0x0029 @@ -47,13 +50,21 @@ #define IMM_NTFY_GLBL_LOGO 0x002E #define IMM_NTFY_RESOURCE 0x0034 #define IMM_NTFY_MSG_RX 0x0036 +#define IMM_NTFY_SRR 0x0045 +#define IMM_NTFY_ELS 0x0046 /* Immediate notify task flags */ -#define IMM_NTFY_CLEAR_ACA 0x4000 -#define IMM_NTFY_TARGET_RESET 0x2000 -#define IMM_NTFY_LUN_RESET 0x1000 -#define IMM_NTFY_CLEAR_TS 0x0400 -#define IMM_NTFY_ABORT_TS 0x0200 +#define IMM_NTFY_TASK_MGMT_SHIFT 9 + +#define Q2T_CLEAR_ACA 0x40 +#define Q2T_TARGET_RESET 0x20 +#define Q2T_LUN_RESET 0x10 +#define Q2T_CLEAR_TS 0x04 +#define Q2T_ABORT_TS 0x02 +#define Q2T_ABORT_ALL_SESS 0xFFFF +#define Q2T_ABORT_ALL 0xFFFE +#define Q2T_NEXUS_LOSS_SESS 0xFFFD +#define Q2T_NEXUS_LOSS 0xFFFC /* Notify Acknowledge flags */ #define NOTIFY_ACK_RES_COUNT BIT_8 @@ -62,26 +73,36 @@ /* Command's states */ #define Q2T_STATE_NEW 0 /* New command and SCST processing it */ -#define Q2T_STATE_PROCESSED 1 /* SCST done processing */ -#define Q2T_STATE_NEED_DATA 2 /* SCST needs data to continue */ -#define Q2T_STATE_DATA_IN 3 /* Data arrived and SCST processing */ - /* them */ +#define Q2T_STATE_NEED_DATA 1 /* SCST needs data to continue */ +#define Q2T_STATE_DATA_IN 2 /* Data arrived and SCST processing it */ +#define Q2T_STATE_PROCESSED 3 /* SCST done processing */ #define Q2T_STATE_ABORTED 4 /* Command aborted */ -/* Misc */ +/* Special handles */ #define Q2T_NULL_HANDLE 0 -#define Q2T_SKIP_HANDLE (0xFFFFFFFE & ~CTIO_COMPLETION_HANDLE_MARK) -#define Q2T_BUSY_HANDLE (0xFFFFFFFF & ~CTIO_COMPLETION_HANDLE_MARK) +#define Q2T_SKIP_HANDLE (0xFFFFFFFF & ~CTIO_COMPLETION_HANDLE_MARK) -/* ATIO task_codes fields */ +/* ATIO task_codes field */ #define ATIO_SIMPLE_QUEUE 0 #define ATIO_HEAD_OF_QUEUE 1 #define ATIO_ORDERED_QUEUE 2 #define ATIO_ACA_QUEUE 4 #define ATIO_UNTAGGED 5 -/* TM failed response code, see FCP */ -#define FC_TM_FAILED 0x5 +/* TM failed response codes, see FCP (9.4.11 FCP_RSP_INFO) */ +#define FC_TM_SUCCESS 0 +#define FC_TM_BAD_FCP_DATA 1 +#define FC_TM_BAD_CMD 2 +#define FC_TM_FCP_DATA_MISMATCH 3 +#define FC_TM_REJECT 4 +#define FC_TM_FAILED 5 + +/* + * Error code of q2t_pre_xmit_response() meaning that cmd's exchange was + * terminated, so no more actions is needed and success should be returned + * to SCST. Must be different from any SCST_TGT_RES_* codes. + */ +#define Q2T_PRE_XMIT_RESP_CMD_ABORTED 0x1717 #if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G) #define pci_dma_lo32(a) (a & 0xffffffff) @@ -91,74 +112,163 @@ #define pci_dma_hi32(a) 0 #endif +struct q2t_tgt +{ + struct scst_tgt *scst_tgt; + scsi_qla_host_t *ha; + + /* + * To sync between IRQ handlers and q2t_target_release(). Needed, + * because req_pkt() can drop/reaquire HW lock inside. Protected by + * HW lock. + */ + int irq_cmd_count; + + int datasegs_per_cmd, datasegs_per_cont; + + /* Target's flags, serialized by ha->hardware_lock */ + unsigned int tgt_enable_64bit_addr:1; /* 64-bits PCI addressing enabled */ + unsigned int link_reinit_iocb_pending:1; + unsigned int tm_to_unknown:1; /* TM to unknown session was sent */ + + /* + * Protected by tgt_mutex AND hardware_lock for writing and tgt_mutex + * OR hardware_lock for reading. + */ + unsigned long tgt_shutdown; /* the driver is being released */ + + /* Count of sessions refering q2t_tgt. Protected by hardware_lock. */ + int sess_count; + + /* Protected by hardware_lock. Addition also protected by tgt_mutex. */ + struct list_head sess_list; + + /* Protected by hardware_lock */ + struct list_head del_sess_list; + struct timer_list sess_del_timer; + + spinlock_t sess_work_lock; + struct list_head sess_works_list; + struct work_struct sess_work; + + notify24xx_entry_t link_reinit_iocb; + wait_queue_head_t waitQ; + int notify_ack_expected; + int abts_resp_expected; + int modify_lun_expected; + + int ctio_srr_id; + int imm_srr_id; + spinlock_t srr_lock; + struct list_head srr_ctio_list; + struct list_head srr_imm_list; + struct work_struct srr_work; +}; + /* * Equivilant to IT Nexus (Initiator-Target) */ -struct q2t_sess { - struct list_head list; +struct q2t_sess +{ + uint16_t loop_id; + port_id_t s_id; + + unsigned int conf_compl_supported:1; + unsigned int deleted:1; + unsigned int local:1; + struct scst_session *scst_sess; struct q2t_tgt *tgt; - int loop_id; + + int sess_ref; /* protected by hardware_lock */ + + struct list_head sess_list_entry; + unsigned long expires; + struct list_head del_list_entry; + + uint8_t port_name[WWN_SIZE]; }; -struct q2t_cmd { +struct q2t_cmd +{ struct q2t_sess *sess; - struct scst_cmd *scst_cmd; int state; - struct atio_entry atio; - dma_addr_t dma_handle; - uint32_t iocb_cnt; -}; + struct scst_cmd *scst_cmd; -struct q2t_tgt { - struct scst_tgt *scst_tgt; - scsi_qla_host_t *ha; - int datasegs_per_cmd, datasegs_per_cont; - /* Target's flags, serialized by ha->hardware_lock */ - unsigned int tgt_shutdown:1; /* The driver is being released */ - unsigned int tgt_enable_64bit_addr:1; /* 64bit PCI addressing enabled */ - wait_queue_head_t waitQ; - int notify_ack_expected; - int modify_lun_expected; - /* Count of sessions refering q2t_tgt, protected by hardware_lock */ - int sess_count; - struct list_head sess_list; -}; + unsigned int conf_compl_supported:1;/* to save extra sess dereferences */ + unsigned int free_sg:1; + unsigned int aborted:1; /* Needed in case of SRR */ -struct q2t_mgmt_cmd { - struct q2t_sess *sess; - struct notify_entry notify_entry; -}; - -struct q2t_prm { - struct q2t_tgt *tgt; - uint16_t req_cnt; - uint16_t seg_cnt; - int sg_cnt; - struct scatterlist *sg; - int bufflen; + struct scatterlist *sg; /* cmd data buffer SG vector */ + int sg_cnt; /* SG segments count */ + int bufflen; /* cmd buffer length */ + int offset; scst_data_direction data_direction; + uint32_t tag; + dma_addr_t dma_handle; + + uint16_t loop_id; /* to save extra sess dereferences */ + struct q2t_tgt *tgt; /* to save extra sess dereferences */ + + union { + atio7_entry_t atio7; + atio_entry_t atio2x; + } __attribute__((packed)) atio; +}; + +struct q2t_sess_work_param { + struct list_head sess_works_list_entry; + struct q2t_cmd *cmd; +}; + +struct q2t_mgmt_cmd +{ + struct q2t_sess *sess; + unsigned int flags; +#define Q24_MGMT_SEND_NACK 1 + union { + atio7_entry_t atio7; + notify_entry_t notify_entry; + notify24xx_entry_t notify_entry24; + abts24_recv_entry_t abts; + } __attribute__((packed)) orig_iocb; +}; + +struct q2t_prm +{ + struct q2t_cmd *cmd; + struct q2t_tgt *tgt; + void *pkt; + struct scatterlist *sg; /* cmd data buffer SG vector */ + int seg_cnt; + int req_cnt; uint16_t rq_result; uint16_t scsi_status; unsigned char *sense_buffer; - unsigned int sense_buffer_len; + int sense_buffer_len; int residual; - struct q2t_cmd *cmd; - struct ctio_common_entry *pkt; + int add_status_pkt; }; -/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ -static inline struct q2t_sess *q2t_find_sess_by_lid(struct q2t_tgt *tgt, - uint16_t lid) +struct srr_imm { - struct q2t_sess *sess; - sBUG_ON(tgt == NULL); - list_for_each_entry(sess, &tgt->sess_list, list) { - if (lid == sess->loop_id) - return sess; - } + struct list_head srr_list_entry; + int srr_id; + union { + notify_entry_t notify_entry; + notify24xx_entry_t notify_entry24; + } __attribute__((packed)) imm; +}; - return NULL; -} +struct srr_ctio +{ + struct list_head srr_list_entry; + int srr_id; + struct q2t_cmd *cmd; +}; + +#define Q2T_XMIT_DATA 1 +#define Q2T_XMIT_STATUS 2 +#define Q2T_XMIT_ALL (Q2T_XMIT_STATUS|Q2T_XMIT_DATA) #endif /* __QLA2X00T_H */ diff --git a/qla2x00t/qla2x_tgt.h b/qla2x00t/qla2x_tgt.h index 9c6b87afb..ad78d7d5a 100644 --- a/qla2x00t/qla2x_tgt.h +++ b/qla2x00t/qla2x_tgt.h @@ -1,16 +1,18 @@ /* * qla2x_tgt.h - * - * Copyright (C) 2004 - 2009 Vladislav Bolkhovitin + * + * Copyright (C) 2004 - 2008 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar + * Copyright (C) 2006 Nathaniel Clark + * Copyright (C) 2007 - 2008 CMS Distribution Limited * * Additional file for the target driver support. - * + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -20,19 +22,42 @@ * This should be included only from within qla2xxx module. */ + #ifndef __QLA2X_TGT_H #define __QLA2X_TGT_H -#ifndef CONFIG_SCSI_QLA2XXX_TARGET -#error __FILE__ " included without CONFIG_SCSI_QLA2XXX_TARGET" -#endif +extern request_t *qla2x00_req_pkt(scsi_qla_host_t *ha); + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET #include "qla2x_tgt_def.h" -extern struct qla2x_tgt_initiator qla_target; +extern struct qla_tgt_initiator qla_target; -/* declare here because it doesn't appear in any .h file just in qla_iocb.c */ -extern request_t *qla2x00_req_pkt(scsi_qla_host_t *ha); +/* Called under HW lock */ +static inline void qla_set_tgt_mode(scsi_qla_host_t *ha) +{ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE + ha->host->active_mode = MODE_TARGET; +#else + ha->host->active_mode |= MODE_TARGET; +#endif +} + +/* Called under HW lock */ +static inline void qla_clear_tgt_mode(scsi_qla_host_t *ha) +{ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE + ha->host->active_mode = MODE_INITIATOR; +#else + ha->host->active_mode &= ~MODE_TARGET; +#endif +} + +static inline bool qla_tgt_mode_enabled(scsi_qla_host_t *ha) +{ + return ha->host->active_mode & MODE_TARGET; +} /********************************************************************\ * ISP Queue types left out of new QLogic driver (from old version) @@ -41,80 +66,69 @@ extern request_t *qla2x00_req_pkt(scsi_qla_host_t *ha); /* * qla2x00_do_en_dis_lun * Issue enable or disable LUN entry IOCB. - * Also set enable_target_mode in ha * * Input: * ha = adapter block pointer. + * + * Caller MUST have hardware lock held. This function might release it, + * then reaquire. */ -/* Caller MUST have hardware lock held */ static inline void -__qla2x00_en_dis_lun(scsi_qla_host_t *ha, int enable) +__qla2x00_send_enable_lun(scsi_qla_host_t *ha, int enable) { - struct elun_entry *pkt; + elun_entry_t *pkt; - if ((pkt = (struct elun_entry *)qla2x00_req_pkt(ha)) != NULL) { + BUG_ON(IS_FWI2_CAPABLE(ha)); + + if ((pkt = (elun_entry_t *)qla2x00_req_pkt(ha)) != NULL) { pkt->entry_type = ENABLE_LUN_TYPE; if (enable) { pkt->command_count = QLA2X00_COMMAND_COUNT_INIT; pkt->immed_notify_count = QLA2X00_IMMED_NOTIFY_COUNT_INIT; pkt->timeout = 0xffff; } - else - { + else { pkt->command_count = 0; pkt->immed_notify_count = 0; pkt->timeout = 0; } - DEBUG2(printk(KERN_DEBUG + DEBUG2(printk(KERN_DEBUG "scsi%lu:ENABLE_LUN IOCB imm %u cmd %u timeout %u\n", ha->host_no, pkt->immed_notify_count, pkt->command_count, pkt->timeout)); /* Issue command to ISP */ qla2x00_isp_cmd(ha); - ha->flags.enable_target_mode = enable; - } + + } else + qla_clear_tgt_mode(ha); #if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) if (!pkt) - printk("qla2100_en_dis_lun: **** FAILED ****\n"); + printk("%s: **** FAILED ****\n", __func__); #endif -} -/* get-lock version */ -static inline void -qla2x00_en_dis_lun(scsi_qla_host_t *ha, int enable) -{ - unsigned long flags = 0; - spin_lock_irqsave(&ha->hardware_lock, flags); - __qla2x00_en_dis_lun(ha, enable); - spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; } - /* - * qla2x00_enable_lun + * qla2x00_send_enable_lun * Issue enable LUN entry IOCB. * * Input: * ha = adapter block pointer. + * enable = enable/disable flag. */ static inline void -qla2x00_enable_lun(scsi_qla_host_t *ha) +qla2x00_send_enable_lun(scsi_qla_host_t *ha, bool enable) { - qla2x00_en_dis_lun(ha, 1); + if (!IS_FWI2_CAPABLE(ha)) { + unsigned long flags; + spin_lock_irqsave(&ha->hardware_lock, flags); + __qla2x00_send_enable_lun(ha, enable); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } } -/* - * qla2x00_disable_lun - * Issue enable LUN entry IOCB (command_count = 0). - * - * Input: - * ha = adapter block pointer. - */ -static inline void -qla2x00_disable_lun(scsi_qla_host_t *ha) -{ - qla2x00_en_dis_lun(ha, 0); -} +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ #endif /* __QLA2X_TGT_H */ diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index 5b08de480..557198ff1 100644 --- a/qla2x00t/qla2x_tgt_def.h +++ b/qla2x00t/qla2x_tgt_def.h @@ -1,17 +1,18 @@ /* * qla2x_tgt_def.h - * - * Copyright (C) 2004 - 2009 Vladislav Bolkhovitin + * + * Copyright (C) 2004 - 2008 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar + * Copyright (C) 2006 Nathaniel Clark + * Copyright (C) 2007 - 2008 CMS Distribution Limited * - * Additional file for the target driver support. Intended to define - * for 2200 and 2300 thier own exported symbols with unique names. - * + * Additional file for the target driver support. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -39,56 +40,66 @@ #define LEAVE(a) #endif -#define QLA2X_TARGET_MAGIC 153 -#define QLA2X_INITIATOR_MAGIC 54208 +#define QLA2X_TARGET_MAGIC 263 +#define QLA2X_INITIATOR_MAGIC 57216 #define QLA2X00_COMMAND_COUNT_INIT 250 #define QLA2X00_IMMED_NOTIFY_COUNT_INIT 250 -#define QLA_EXTENDED_LUN 1 - -/* +/* * Used to mark which completion handles (for RIO Status's) are for CTIO's - * vs. regular (non-target) info. + * vs. regular (non-target) info. This is checked for in + * qla2x00_process_response_queue() to see if a handle coming back in a + * multi-complete should come to the tgt driver or be handled there by qla2xxx */ -#define CTIO_COMPLETION_HANDLE_MARK BIT_15 +#define CTIO_COMPLETION_HANDLE_MARK BIT_29 #if (CTIO_COMPLETION_HANDLE_MARK <= MAX_OUTSTANDING_COMMANDS) -#error "Hackish CTIO_COMPLETION_HANDLE_MARK no longer larger than \ - MAX_OUTSTANDING_COMMANDS" +#error "Hackish CTIO_COMPLETION_HANDLE_MARK no longer larger than MAX_OUTSTANDING_COMMANDS" #endif #define HANDLE_IS_CTIO_COMP(h) (h & CTIO_COMPLETION_HANDLE_MARK) +/* Used to mark CTIO as intermediate */ +#define CTIO_INTERMEDIATE_HANDLE_MARK BIT_30 + #ifndef OF_SS_MODE_0 /* * ISP target entries - Flags bit definitions. */ -#define OF_SS_MODE_0 0 -#define OF_SS_MODE_1 1 -#define OF_SS_MODE_2 2 -#define OF_SS_MODE_3 3 +#define OF_SS_MODE_0 0 +#define OF_SS_MODE_1 1 +#define OF_SS_MODE_2 2 +#define OF_SS_MODE_3 3 -#define OF_RESET BIT_5 /* Reset LIP flag */ -#define OF_DATA_IN BIT_6 /* Data in to initiator */ - /* (data from target to initiator) */ -#define OF_DATA_OUT BIT_7 /* Data out from initiator */ - /* (data from initiator to target) */ -#define OF_NO_DATA (BIT_7 | BIT_6) -#define OF_INC_RC BIT_8 /* Increment command resource count */ -#define OF_FAST_POST BIT_9 /* Enable mailbox fast posting. */ -#define OF_TERM_EXCH BIT_14 /* Terminate exchange */ -#define OF_SSTS BIT_15 /* Send SCSI status */ +#define OF_EXPL_CONF BIT_5 /* Explicit Confirmation Requested */ +#define OF_DATA_IN BIT_6 /* Data in to initiator */ + /* (data from target to initiator) */ +#define OF_DATA_OUT BIT_7 /* Data out from initiator */ + /* (data from initiator to target) */ +#define OF_NO_DATA (BIT_7 | BIT_6) +#define OF_INC_RC BIT_8 /* Increment command resource count */ +#define OF_FAST_POST BIT_9 /* Enable mailbox fast posting. */ +#define OF_CONF_REQ BIT_13 /* Confirmation Requested */ +#define OF_TERM_EXCH BIT_14 /* Terminate exchange */ +#define OF_SSTS BIT_15 /* Send SCSI status */ #endif #ifndef DATASEGS_PER_COMMAND32 -#define DATASEGS_PER_COMMAND32 3 -#define DATASEGS_PER_CONT32 7 +#define DATASEGS_PER_COMMAND32 3 +#define DATASEGS_PER_CONT32 7 #define QLA_MAX_SG32(ql) \ - (DATASEGS_PER_COMMAND32 + (((ql) > 0) ? DATASEGS_PER_CONT32*((ql) - 1) : 0)) + (((ql) > 0) ? (DATASEGS_PER_COMMAND32 + DATASEGS_PER_CONT32*((ql) - 1)) : 0) -#define DATASEGS_PER_COMMAND64 2 -#define DATASEGS_PER_CONT64 5 +#define DATASEGS_PER_COMMAND64 2 +#define DATASEGS_PER_CONT64 5 #define QLA_MAX_SG64(ql) \ - (DATASEGS_PER_COMMAND64 + (((ql) > 0) ? DATASEGS_PER_CONT64*((ql) - 1) : 0)) + (((ql) > 0) ? (DATASEGS_PER_COMMAND64 + DATASEGS_PER_CONT64*((ql) - 1)) : 0) +#endif + +#ifndef DATASEGS_PER_COMMAND_24XX +#define DATASEGS_PER_COMMAND_24XX 1 +#define DATASEGS_PER_CONT_24XX 5 +#define QLA_MAX_SG_24XX(ql) \ + (min(1270, ((ql) > 0) ? (DATASEGS_PER_COMMAND_24XX + DATASEGS_PER_CONT_24XX*((ql) - 1)) : 0)) #endif /********************************************************************\ @@ -100,7 +111,8 @@ /* * ISP queue - enable LUN entry structure definition. */ -struct elun_entry { +typedef struct +{ uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ @@ -113,29 +125,29 @@ struct elun_entry { uint8_t status; uint8_t reserved_4; uint8_t command_count; /* Number of ATIOs allocated. */ - uint8_t immed_notify_count; /* Number of Immediate Notify entries */ - /* allocated. */ + uint8_t immed_notify_count; /* Number of Immediate Notify entries allocated. */ uint16_t reserved_5; uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ uint16_t reserved_6[20]; -}; -#define ENABLE_LUN_SUCCESS 0x01 -#define ENABLE_LUN_RC_NONZERO 0x04 -#define ENABLE_LUN_INVALID_REQUEST 0x06 -#define ENABLE_LUN_ALREADY_ENABLED 0x3E +} __attribute__((packed)) elun_entry_t; +#define ENABLE_LUN_SUCCESS 0x01 +#define ENABLE_LUN_RC_NONZERO 0x04 +#define ENABLE_LUN_INVALID_REQUEST 0x06 +#define ENABLE_LUN_ALREADY_ENABLED 0x3E #endif #ifndef MODIFY_LUN_TYPE -#define MODIFY_LUN_TYPE 0x0C +#define MODIFY_LUN_TYPE 0x0C /* Modify LUN entry. */ /* * ISP queue - modify LUN entry structure definition. */ -struct modify_lun_entry { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - uint32_t sys_define_2; /* System defined. */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t sys_define_2; /* System defined. */ uint8_t reserved_8; uint8_t reserved_1; uint8_t operators; @@ -143,87 +155,111 @@ struct modify_lun_entry { uint32_t reserved_3; uint8_t status; uint8_t reserved_4; - uint8_t command_count; /* Number of ATIOs allocated. */ - uint8_t immed_notify_count; /* Number of Immediate Notify */ + uint8_t command_count; /* Number of ATIOs allocated. */ + uint8_t immed_notify_count; /* Number of Immediate Notify */ /* entries allocated. */ uint16_t reserved_5; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ uint16_t reserved_7[20]; -}; +} __attribute__((packed)) modify_lun_entry_t; #define MODIFY_LUN_SUCCESS 0x01 -#define MODIFY_LUN_CMD_ADD BIT_0 -#define MODIFY_LUN_CMD_SUB BIT_1 -#define MODIFY_LUN_IMM_ADD BIT_2 -#define MODIFY_LUN_IMM_SUB BIT_3 +#define MODIFY_LUN_CMD_ADD BIT_0 +#define MODIFY_LUN_CMD_SUB BIT_1 +#define MODIFY_LUN_IMM_ADD BIT_2 +#define MODIFY_LUN_IMM_SUB BIT_3 #endif - #define GET_TARGET_ID(ha, iocb) ((HAS_EXTENDED_IDS(ha)) \ ? le16_to_cpu((iocb)->target.extended) \ : (uint16_t)(iocb)->target.id.standard) #ifndef IMMED_NOTIFY_TYPE -#define IMMED_NOTIFY_TYPE 0x0D /* Immediate notify entry. */ +#define IMMED_NOTIFY_TYPE 0x0D /* Immediate notify entry. */ /* * ISP queue - immediate notify entry structure definition. */ -struct notify_entry { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - uint32_t sys_define_2; /* System defined. */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t sys_define_2; /* System defined. */ target_id_t target; uint16_t lun; - uint32_t reserved_2; + uint8_t target_id; + uint8_t reserved_1; + uint16_t status_modifier; uint16_t status; uint16_t task_flags; uint16_t seq_id; - uint16_t reserved_5[11]; - uint16_t scsi_status; - uint8_t sense_data[16]; + uint16_t srr_rx_id; + uint32_t srr_rel_offs; + uint16_t srr_ui; +#define SRR_IU_DATA_IN 0x1 +#define SRR_IU_DATA_OUT 0x5 +#define SRR_IU_STATUS 0x7 + uint16_t srr_ox_id; + uint8_t reserved_2[30]; uint16_t ox_id; -}; +} __attribute__((packed)) notify_entry_t; #endif #ifndef NOTIFY_ACK_TYPE -#define NOTIFY_ACK_TYPE 0x0E /* Notify acknowledge entry. */ +#define NOTIFY_ACK_TYPE 0x0E /* Notify acknowledge entry. */ /* * ISP queue - notify acknowledge entry structure definition. */ -struct nack_entry { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - uint32_t sys_define_2; /* System defined. */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t sys_define_2; /* System defined. */ target_id_t target; - uint8_t reserved_1; uint8_t target_id; + uint8_t reserved_1; uint16_t flags; uint16_t resp_code; uint16_t status; uint16_t task_flags; uint16_t seq_id; - uint16_t reserved_3[20]; + uint16_t srr_rx_id; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_flags; + uint16_t srr_reject_code; + uint8_t srr_reject_vendor_uniq; + uint8_t srr_reject_code_expl; + uint8_t reserved_2[26]; uint16_t ox_id; -}; -#define NOTIFY_ACK_SUCCESS 0x01 +} __attribute__((packed)) nack_entry_t; +#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0 +#define NOTIFY_ACK_SRR_FLAGS_REJECT 1 + +#define NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM 0x9 + +#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL 0 +#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_UNABLE_TO_SUPPLY_DATA 0x2a + +#define NOTIFY_ACK_SUCCESS 0x01 #endif #ifndef ACCEPT_TGT_IO_TYPE -#define ACCEPT_TGT_IO_TYPE 0x16 /* Accept target I/O entry. */ +#define ACCEPT_TGT_IO_TYPE 0x16 /* Accept target I/O entry. */ /* * ISP queue - Accept Target I/O (ATIO) entry structure definition. */ -struct atio_entry { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - uint32_t sys_define_2; /* System defined. */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t sys_define_2; /* System defined. */ target_id_t target; - uint16_t exchange_id; + uint16_t rx_id; uint16_t flags; uint16_t status; uint8_t command_ref; @@ -234,82 +270,88 @@ struct atio_entry { uint32_t data_length; uint16_t lun; uint8_t initiator_port_name[WWN_SIZE]; /* on qla23xx */ - uint8_t reserved2[12]; + uint16_t reserved_32[6]; uint16_t ox_id; -}; +} __attribute__((packed)) atio_entry_t; #endif #ifndef CONTINUE_TGT_IO_TYPE -#define CONTINUE_TGT_IO_TYPE 0x17 +#define CONTINUE_TGT_IO_TYPE 0x17 /* * ISP queue - Continue Target I/O (CTIO) entry for status mode 0 * structure definition. */ -struct ctio_common_entry { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - uint32_t handle; /* System defined handle */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System defined handle */ target_id_t target; - uint16_t exchange_id; + uint16_t rx_id; uint16_t flags; uint16_t status; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t dseg_count; /* Data segment count. */ + uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + uint16_t dseg_count; /* Data segment count. */ uint32_t relative_offset; uint32_t residual; uint16_t reserved_1[3]; uint16_t scsi_status; uint32_t transfer_length; uint32_t dseg_0_address[0]; -}; -#define ATIO_PATH_INVALID 0x07 -#define ATIO_CANT_PROV_CAP 0x16 -#define ATIO_CDB_VALID 0x3D +} __attribute__((packed)) ctio_common_entry_t; +#define ATIO_PATH_INVALID 0x07 +#define ATIO_CANT_PROV_CAP 0x16 +#define ATIO_CDB_VALID 0x3D -#define ATIO_EXEC_READ BIT_1 -#define ATIO_EXEC_WRITE BIT_0 +#define ATIO_EXEC_READ BIT_1 +#define ATIO_EXEC_WRITE BIT_0 #endif #ifndef CTIO_A64_TYPE -#define CTIO_A64_TYPE 0x1F -struct ctio_entry { - struct ctio_common_entry common; - uint32_t dseg_0_address; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ - uint32_t dseg_2_address; /* Data segment 2 address. */ - uint32_t dseg_2_length; /* Data segment 2 length. */ -}; -#define CTIO_SUCCESS 0x01 -#define CTIO_ABORTED 0x02 -#define CTIO_INVALID_RX_ID 0x08 -#define CTIO_TIMEOUT 0x0B -#define CTIO_LIP_RESET 0x0E -#define CTIO_TARGET_RESET 0x17 -#define CTIO_PORT_UNAVAILABLE 0x28 -#define CTIO_PORT_LOGGED_OUT 0x29 +#define CTIO_A64_TYPE 0x1F +typedef struct +{ + ctio_common_entry_t common; + uint32_t dseg_0_address; /* Data segment 0 address. */ + uint32_t dseg_0_length; /* Data segment 0 length. */ + uint32_t dseg_1_address; /* Data segment 1 address. */ + uint32_t dseg_1_length; /* Data segment 1 length. */ + uint32_t dseg_2_address; /* Data segment 2 address. */ + uint32_t dseg_2_length; /* Data segment 2 length. */ +} __attribute__((packed)) ctio_entry_t; +#define CTIO_SUCCESS 0x01 +#define CTIO_ABORTED 0x02 +#define CTIO_INVALID_RX_ID 0x08 +#define CTIO_TIMEOUT 0x0B +#define CTIO_LIP_RESET 0x0E +#define CTIO_TARGET_RESET 0x17 +#define CTIO_PORT_UNAVAILABLE 0x28 +#define CTIO_PORT_LOGGED_OUT 0x29 +#define CTIO_PORT_CONF_CHANGED 0x2A +#define CTIO_SRR_RECEIVED 0x45 + #endif #ifndef CTIO_RET_TYPE -#define CTIO_RET_TYPE 0x17 /* CTIO return entry */ +#define CTIO_RET_TYPE 0x17 /* CTIO return entry */ /* * ISP queue - CTIO returned entry structure definition. */ -struct ctio_ret_entry { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t sys_define; /* System defined. */ - uint8_t entry_status; /* Entry Status. */ - uint32_t handle; /* System defined handle. */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System defined handle. */ target_id_t target; - uint16_t exchange_id; + uint16_t rx_id; uint16_t flags; uint16_t status; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t dseg_count; /* Data segment count. */ + uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + uint16_t dseg_count; /* Data segment count. */ uint32_t relative_offset; uint32_t residual; uint16_t reserved_1[2]; @@ -317,46 +359,406 @@ struct ctio_ret_entry { uint16_t scsi_status; uint16_t response_length; uint8_t sense_data[26]; -}; +} __attribute__((packed)) ctio_ret_entry_t; #endif +#define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */ + +typedef struct +{ + uint8_t r_ctl; + uint8_t d_id[3]; + uint8_t cs_ctl; + uint8_t s_id[3]; + uint8_t type; + uint8_t f_ctl[3]; + uint8_t seq_id; + uint8_t df_ctl; + uint16_t seq_cnt; + uint16_t ox_id; + uint16_t rx_id; + uint32_t parameter; +} __attribute__((packed)) fcp_hdr_t; + +typedef struct +{ + uint8_t d_id[3]; + uint8_t r_ctl; + uint8_t s_id[3]; + uint8_t cs_ctl; + uint8_t f_ctl[3]; + uint8_t type; + uint16_t seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + uint16_t rx_id; + uint16_t ox_id; + uint32_t parameter; +} __attribute__((packed)) fcp_hdr_le_t; + +#define F_CTL_EXCH_CONTEXT_RESP BIT_23 +#define F_CTL_SEQ_CONTEXT_RESIP BIT_22 +#define F_CTL_LAST_SEQ BIT_20 +#define F_CTL_END_SEQ BIT_19 +#define F_CTL_SEQ_INITIATIVE BIT_16 + +#define R_CTL_BASIC_LINK_SERV 0x80 +#define R_CTL_B_ACC 0x4 +#define R_CTL_B_RJT 0x5 + +typedef struct +{ + uint64_t lun; + uint8_t cmnd_ref; + uint8_t task_attr:3; + uint8_t reserved:5; + uint8_t task_mgmt_flags; +#define FCP_CMND_TASK_MGMT_CLEAR_ACA 6 +#define FCP_CMND_TASK_MGMT_TARGET_RESET 5 +#define FCP_CMND_TASK_MGMT_LU_RESET 4 +#define FCP_CMND_TASK_MGMT_CLEAR_TASK_SET 2 +#define FCP_CMND_TASK_MGMT_ABORT_TASK_SET 1 + uint8_t wrdata:1; + uint8_t rddata:1; + uint8_t add_cdb_len:6; + uint8_t cdb[16]; + /* Valid only if add_cdb_len=0, otherwise this is additional CDB data */ + uint32_t data_length; +} __attribute__((packed)) fcp_cmnd_t; + +/* + * ISP queue - Accept Target I/O (ATIO) type 7 entry for 24xx structure + * definition. + */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t fcp_cmnd_len_low; + uint8_t fcp_cmnd_len_high:4; + uint8_t attr:4; + uint32_t exchange_addr; +#define ATIO_EXCHANGE_ADDRESS_UNKNOWN 0xFFFFFFFF + fcp_hdr_t fcp_hdr; + fcp_cmnd_t fcp_cmnd; +} __attribute__((packed)) atio7_entry_t; + +#define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */ + +/* + * ISP queue - Continue Target I/O (ATIO) type 7 entry (for 24xx) structure + * definition. + */ + +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System defined handle */ + uint16_t nport_handle; +#define CTIO7_NHANDLE_UNRECOGNIZED 0xFFFF + uint16_t timeout; + uint16_t dseg_count; /* Data segment count. */ + uint8_t vp_index; + uint8_t add_flags; + uint8_t initiator_id[3]; + uint8_t reserved; + uint32_t exchange_addr; +} __attribute__((packed)) ctio7_common_entry_t; + +typedef struct +{ + ctio7_common_entry_t common; + uint16_t reserved1; + uint16_t flags; + uint32_t residual; + uint16_t ox_id; + uint16_t scsi_status; + uint32_t relative_offset; + uint32_t reserved2; + uint32_t transfer_length; + uint32_t reserved3; + uint32_t dseg_0_address[2]; /* Data segment 0 address. */ + uint32_t dseg_0_length; /* Data segment 0 length. */ +} __attribute__((packed)) ctio7_status0_entry_t; + +typedef struct +{ + ctio7_common_entry_t common; + uint16_t sense_length; + uint16_t flags; + uint32_t residual; + uint16_t ox_id; + uint16_t scsi_status; + uint16_t response_len; + uint16_t reserved; + uint8_t sense_data[24]; +} __attribute__((packed)) ctio7_status1_entry_t; + +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System defined handle */ + uint16_t status; + uint16_t timeout; + uint16_t dseg_count; /* Data segment count. */ + uint8_t reserved1[6]; + uint32_t exchange_address; + uint16_t reserved2; + uint16_t flags; + uint32_t residual; + uint16_t ox_id; + uint16_t reserved3; + uint32_t relative_offset; + uint8_t reserved4[24]; +} __attribute__((packed)) ctio7_fw_entry_t; + +/* CTIO7 flags values */ +#define CTIO7_FLAGS_SEND_STATUS BIT_15 +#define CTIO7_FLAGS_TERMINATE BIT_14 +#define CTIO7_FLAGS_CONFORM_REQ BIT_13 +#define CTIO7_FLAGS_DONT_RET_CTIO BIT_8 +#define CTIO7_FLAGS_STATUS_MODE_0 0 +#define CTIO7_FLAGS_STATUS_MODE_1 BIT_6 +#define CTIO7_FLAGS_EXPLICIT_CONFORM BIT_5 +#define CTIO7_FLAGS_CONFIRM_SATISF BIT_4 +#define CTIO7_FLAGS_DSD_PTR BIT_2 +#define CTIO7_FLAGS_DATA_IN BIT_1 +#define CTIO7_FLAGS_DATA_OUT BIT_0 + +/* + * ISP queue - immediate notify entry structure definition for 24xx. + */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t reserved; + uint16_t nport_handle; + uint16_t reserved_2; + uint16_t flags; +#define NOTIFY24XX_FLAGS_GLOBAL_TPRLO BIT_1 +#define NOTIFY24XX_FLAGS_PUREX_IOCB BIT_0 + uint16_t srr_rx_id; + uint16_t status; + uint8_t status_subcode; + uint8_t reserved_3; + uint32_t exchange_address; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_ox_id; + uint8_t reserved_4[19]; + uint8_t vp_index; + uint32_t reserved_5; + uint8_t port_id[3]; + uint8_t reserved_6; + uint16_t reserved_7; + uint16_t ox_id; +} __attribute__((packed)) notify24xx_entry_t; + +#define ELS_PLOGI 0x3 +#define ELS_FLOGI 0x4 +#define ELS_LOGO 0x5 +#define ELS_PRLI 0x20 +#define ELS_PRLO 0x21 +#define ELS_TPRLO 0x24 +#define ELS_PDISC 0x50 +#define ELS_ADISC 0x52 + +/* + * ISP queue - notify acknowledge entry structure definition for 24xx. + */ +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; + uint16_t nport_handle; + uint16_t reserved_1; + uint16_t flags; + uint16_t srr_rx_id; + uint16_t status; + uint8_t status_subcode; + uint8_t reserved_3; + uint32_t exchange_address; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_flags; + uint8_t reserved_4[19]; + uint8_t vp_index; + uint8_t srr_reject_vendor_uniq; + uint8_t srr_reject_code_expl; + uint8_t srr_reject_code; + uint8_t reserved_5[7]; + uint16_t ox_id; +} __attribute__((packed)) nack24xx_entry_t; + +/* + * ISP queue - ABTS received/response entries structure definition for 24xx. + */ +#define ABTS_RECV_24XX 0x54 /* ABTS received (for 24xx) */ +#define ABTS_RESP_24XX 0x55 /* ABTS responce (for 24xx) */ + +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint8_t reserved_1[6]; + uint16_t nport_handle; + uint8_t reserved_2[3]; + uint8_t reserved_3:4; + uint8_t sof_type:4; + uint32_t exchange_address; + fcp_hdr_le_t fcp_hdr_le; + uint8_t reserved_4[16]; + uint32_t exchange_addr_to_abort; +} __attribute__((packed)) abts24_recv_entry_t; + +#define ABTS_PARAM_ABORT_SEQ BIT_0 + +typedef struct +{ + uint16_t reserved; + uint8_t seq_id_last; + uint8_t seq_id_valid; +#define SEQ_ID_VALID 0x80 +#define SEQ_ID_INVALID 0x00 + uint16_t rx_id; + uint16_t ox_id; + uint16_t high_seq_cnt; + uint16_t low_seq_cnt; +} __attribute__((packed)) ba_acc_le_t; + +typedef struct +{ + uint8_t vendor_uniq; + uint8_t reason_expl; + uint8_t reason_code; +#define BA_RJT_REASON_CODE_INVALID_COMMAND 0x1 +#define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM 0x9 + uint8_t reserved; +} __attribute__((packed)) ba_rjt_le_t; + +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; + uint16_t reserved_1; + uint16_t nport_handle; + uint16_t control_flags; +#define ABTS_CONTR_FLG_TERM_EXCHG BIT_0 + uint8_t reserved_2; + uint8_t reserved_3:4; + uint8_t sof_type:4; + uint32_t exchange_address; + fcp_hdr_le_t fcp_hdr_le; + union { + ba_acc_le_t ba_acct; + ba_rjt_le_t ba_rjt; + } __attribute__((packed)) payload; + uint32_t reserved_4; + uint32_t exchange_addr_to_abort; +} __attribute__((packed)) abts24_resp_entry_t; + +typedef struct +{ + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; + uint16_t compl_status; +#define ABTS_RESP_COMPL_SUCCESS 0 +#define ABTS_RESP_COMPL_SUBCODE_ERROR 0x31 + uint16_t nport_handle; + uint16_t reserved_1; + uint8_t reserved_2; + uint8_t reserved_3:4; + uint8_t sof_type:4; + uint32_t exchange_address; + fcp_hdr_le_t fcp_hdr_le; + uint8_t reserved_4[8]; + uint32_t error_subcode1; +#define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM 0x1E + uint32_t error_subcode2; + uint32_t exchange_addr_to_abort; +} __attribute__((packed)) abts24_resp_fw_entry_t; + /********************************************************************\ * Type Definitions used by initiator & target halves \********************************************************************/ -enum qla2x_tgt_host_action { - DISABLE_TARGET_MODE = 0, - ENABLE_TARGET_MODE = 1 -}; +typedef enum { + DISABLE_TARGET_MODE = 0, + ENABLE_TARGET_MODE = 1 +} qla2x_tgt_host_action_t; -struct qla2x_tgt_initiator { +struct qla_tgt_initiator +{ int magic; /* Callbacks */ - void (*tgt_response_pkt)(scsi_qla_host_t *ha, sts_entry_t *pkt); - void (*tgt_ctio_completion)(scsi_qla_host_t *ha, uint32_t handle); + void (*tgt24_atio_pkt)(scsi_qla_host_t *ha, atio7_entry_t *pkt); + void (*tgt_response_pkt)(scsi_qla_host_t *ha, response_t *pkt); + void (*tgt2x_ctio_completion)(scsi_qla_host_t *ha, uint32_t handle); void (*tgt_async_event)(uint16_t code, scsi_qla_host_t *ha, uint16_t *mailbox); - void (*tgt_host_action)(scsi_qla_host_t *ha, - enum qla2x_tgt_host_action action); + int (*tgt_host_action)(scsi_qla_host_t *ha, qla2x_tgt_host_action_t + action); + void (*tgt_fc_port_added)(scsi_qla_host_t *ha, fc_port_t *fcport); + void (*tgt_fc_port_deleted)(scsi_qla_host_t *ha, fc_port_t *fcport); }; -struct qla2x_tgt_target { +struct qla_target +{ int magic; - - /* Callbacks - H/W lock MUST be held while calling any */ - request_t *(*req_pkt)(scsi_qla_host_t *ha); + + /* + * Callbacks - H/W lock MUST be held while calling any. + * + * !!! req_pkt() and issue_marker() could unlock/lock it inside !!! + * + * But it isn't an issue, since in the current implementation: + * + * - Either we in IRQ and only IRQ handler can modify HW data, + * including rings related fields. + * + * - Or access to target mode variables from struct q2t_tgt don't + * cross those functions boundaries, except tgt_shutdown, which + * additionally protected by irq_cmd_count. + */ + request_t *(*req_pkt)(scsi_qla_host_t *ha); void (*isp_cmd)(scsi_qla_host_t *ha); - void (*enable_lun)(scsi_qla_host_t *ha); - void (*disable_lun)(scsi_qla_host_t *ha); - int (*issue_marker)(scsi_qla_host_t *ha); - cont_entry_t *(*req_cont_pkt)(scsi_qla_host_t *ha); - int (*get_counts)(scsi_qla_host_t *ha, uint8_t *cmd, uint8_t *imm); + void (*enable_tgt_mode)(scsi_qla_host_t *ha); + void (*disable_tgt_mode)(scsi_qla_host_t *ha); + int (*issue_marker)(scsi_qla_host_t *ha, int ha_locked); + void (*mark_all_devices_lost)(scsi_qla_host_t *ha, int defer); + int (*get_id_list)(scsi_qla_host_t *ha, void *id_list, + dma_addr_t id_list_dma, uint16_t *entries); + int (*get_port_database)(scsi_qla_host_t *ha, fc_port_t *fcport, + uint8_t opt); }; -int qla2xxx_tgt_register_driver(/* IN */ struct qla2x_tgt_initiator *tgt, - /* OUT */ struct qla2x_tgt_target *init); +int qla2xxx_tgt_register_driver(/* IN */ struct qla_tgt_initiator *tgt, + /* OUT */ struct qla_target *init); void qla2xxx_tgt_unregister_driver(void); -#endif +int qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha); +int qla2x00_wait_for_hba_online(scsi_qla_host_t *ha); + +#endif /* __QLA2X_TGT_DEF_H */ diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index 456868b2a..be78a1a22 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -9,83 +9,208 @@ #include #include #include -#include #include -#ifdef CONFIG_SCSI_QLA2XXX_TARGET #include "qla2x_tgt.h" #include -#endif static int qla24xx_vport_disable(struct fc_vport *, bool); /* SYSFS attributes --------------------------------------------------------- */ -#if defined(CONFIG_SCSI_QLA2XXX_TARGET) - static ssize_t -qla2x00_show_tgt_enabled(struct device *dev, - struct device_attribute *attr, char *buf) +qla2x00_show_class2_enabled(struct device *dev, + struct device_attribute *attr, char *buffer) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); ulong max_size = PAGE_SIZE; ulong size; - size = scnprintf(buf, max_size, "%d\n", ha->flags.enable_target_mode); + size = scnprintf(buffer, max_size, "%d\n", ha->enable_class_2); return size; } static ssize_t -qla2x00_store_tgt_enabled(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) +qla2x00_store_class2_enabled(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t size) { struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); - int force = 0; + int reset = 0; + unsigned long flags; + int res = size; - if (buf == NULL) - return size; + if (buffer == NULL) + goto out; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + switch (buffer[0]) { + case '0' : + if (ha->enable_class_2) { + ha->enable_class_2 = 0; + reset = 1; + } + break; + case '1' : + if (!ha->enable_class_2) { + if (ha->fw_attributes & __constant_cpu_to_le32(BIT_0)) { + qla_printk(KERN_INFO, ha, "Enabling class 2 " + "operations.\n"); + ha->enable_class_2 = 1; + reset = 1; + } else { + qla_printk(KERN_INFO, ha, "Firmware doesn't " + "support class 2 operations.\n"); + res = -EINVAL; + goto out_unlock; + } + } + break; + default: +#if defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_11) + printk("%s: Requested action not understood: %s\n", + __func__, buffer); +#endif + res = -EINVAL; + goto out_unlock; + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (reset) + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + +out: + return size; + +out_unlock: + spin_unlock_irqrestore(&ha->hardware_lock, flags); + goto out; +} + +static DEVICE_ATTR(class2_enabled, + S_IRUGO|S_IWUSR, + qla2x00_show_class2_enabled, + qla2x00_store_class2_enabled); + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + +static ssize_t +qla2x00_show_tgt_enabled(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + ssize_t size; + + size = scnprintf(buffer, PAGE_SIZE, "%d\n", qla_tgt_mode_enabled(ha)); + + return size; +} + +static ssize_t +qla2x00_store_tgt_enabled(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t size) +{ + struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); + int res = size; + + if ((buffer == NULL) || (size == 0)) + goto out; if (qla_target.tgt_host_action == NULL) { printk(KERN_INFO "%s: not acting for lack of target driver\n", __func__); - return size; + res = -EINVAL; + goto out; } - if ((size > 1) && (buf[1] == 'f')) { - force = 1; - printk(KERN_DEBUG "%s: forcing the matter\n", __func__); - } - - switch (buf[0]) { - case '0' : - if ((ha->flags.enable_target_mode) || force) - qla_target.tgt_host_action(ha, DISABLE_TARGET_MODE); + switch (buffer[0]) { + case '0' : + res = qla_target.tgt_host_action(ha, DISABLE_TARGET_MODE); break; case '1' : - if ((ha->flags.enable_target_mode == 0) || force) - qla_target.tgt_host_action(ha, ENABLE_TARGET_MODE); + res = qla_target.tgt_host_action(ha, ENABLE_TARGET_MODE); break; default: - printk(KERN_INFO "%s: Requested action not understood: %s\n", - __func__, buf); - break; + printk("%s: Requested action not understood: %s\n", + __func__, buffer); + res = -EINVAL; + goto out; } - if ((size > 2) && (buf[2] == 'r')) + if (res == 0) + res = size; + + if ((size > 1) && (buffer[1] == 'r')) set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); +out: + return res; +} + +static DEVICE_ATTR(target_mode_enabled, + S_IRUGO|S_IWUSR, + qla2x00_show_tgt_enabled, + qla2x00_store_tgt_enabled); + +static ssize_t +qla2x00_show_expl_conf_enabled(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); + ulong max_size = PAGE_SIZE; + ulong size; + + size = scnprintf(buffer, max_size, "%d\n", ha->enable_explicit_conf); + return size; } -static DEVICE_ATTR(target_mode_enabled, - S_IRUGO|S_IWUSR, - qla2x00_show_tgt_enabled, - qla2x00_store_tgt_enabled); +static ssize_t +qla2x00_store_expl_conf_enabled(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t size) +{ + struct scsi_qla_host *ha = shost_priv(class_to_shost(dev)); + unsigned long flags; + + if (buffer == NULL) + return size; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + switch (buffer[0]) { + case '0' : + ha->enable_explicit_conf = 0; + printk("qla2xxx(%ld): explicit conformation disabled\n", + ha->instance); + break; + case '1' : + ha->enable_explicit_conf = 1; + printk("qla2xxx(%ld): explicit conformation enabled\n", + ha->instance); + break; + default: +#if defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_11) + printk("%s: Requested action not understood: %s\n", + __func__, buffer); +#endif + break; + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return size; +} + +static DEVICE_ATTR(explicit_conform_enabled, + S_IRUGO|S_IWUSR, + qla2x00_show_expl_conf_enabled, + qla2x00_store_expl_conf_enabled); static ssize_t qla2x00_show_resource_counts(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buffer) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); ulong max_size = PAGE_SIZE; @@ -96,34 +221,37 @@ qla2x00_show_resource_counts(struct device *dev, mc.mb[0] = MBC_GET_RESOURCE_COUNTS; mc.out_mb = MBX_0; mc.in_mb = MBX_0|MBX_1|MBX_2; - mc.tov = 30; - mc.flags = 0; + mc.tov = 30; + mc.flags = 0; rval = qla2x00_mailbox_command(ha, &mc); if (rval != QLA_SUCCESS) { - size = scnprintf(buf, max_size, - "Mailbox Command failed %d, mb %#x", - rval, mc.mb[0]); - goto out; - } - - size = scnprintf(buf, max_size, + size = scnprintf(buffer, max_size, + "Mailbox Command failed %d, mb %#x", + rval, mc.mb[0]); + } else { + size = scnprintf(buffer, max_size, "immed_notify\t%d\ncommand\t\t%d\n", mc.mb[2], mc.mb[1]); + } -out: return size; } static DEVICE_ATTR(resource_counts, - S_IRUGO, - qla2x00_show_resource_counts, - NULL); + S_IRUGO, + qla2x00_show_resource_counts, + NULL); + +typedef struct { + uint8_t port_name[WWN_SIZE]; + uint16_t loop_id; +} port_data_t; static ssize_t qla2x00_show_port_database(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buffer) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); ulong max_size = PAGE_SIZE; @@ -132,89 +260,91 @@ qla2x00_show_port_database(struct device *dev, uint16_t entries; mbx_cmd_t mc; dma_addr_t pmap_dma; - struct port_data { - uint8_t port_name[WWN_SIZE]; - uint16_t loop_id; - } *pmap; + port_data_t *pmap; ulong dma_size = 0x100*sizeof(*pmap); - pmap = (struct port_data *)dma_alloc_coherent(&ha->pdev->dev, dma_size, + pmap = (port_data_t*)dma_alloc_coherent(&ha->pdev->dev, dma_size, &pmap_dma, GFP_KERNEL); if (pmap == NULL) { - size = scnprintf(buf, max_size, "DMA Alloc failed of %ld", + size = scnprintf(buffer, max_size, "DMA Alloc failed of %ld", dma_size); goto out; } mc.mb[0] = MBC_PORT_NODE_NAME_LIST; - mc.mb[1] = BIT_1; + mc.mb[1] = BIT_1 | BIT_3; mc.mb[2] = MSW(pmap_dma); mc.mb[3] = LSW(pmap_dma); mc.mb[6] = MSW(MSD(pmap_dma)); mc.mb[7] = LSW(MSD(pmap_dma)); + mc.mb[8] = 0xFF; mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7; mc.in_mb = MBX_0|MBX_1; - mc.tov = 30; - mc.flags = MBX_DMA_IN; + mc.tov = 30; + mc.flags = MBX_DMA_IN; rval = qla2x00_mailbox_command(ha, &mc); if (rval != QLA_SUCCESS) { - size = scnprintf(buf, max_size, - "Mailbox Command failed %d, mb0 %#x mb1 %#x\n", + size = scnprintf(buffer, max_size, + "Mailbox Command failed %d, mb0 %#x mb1 %#x\n", rval, mc.mb[0], mc.mb[1]); goto out_free; } entries = le16_to_cpu(mc.mb[1])/sizeof(*pmap); - - size += scnprintf(buf+size, max_size-size, - "Port Name List (%#04x) returned %d bytes\n" - "L_ID WWPN\n", MBC_PORT_NODE_NAME_LIST, - le16_to_cpu(mc.mb[1])); + + size += scnprintf(buffer+size, max_size-size, + "Port Name List (%#04x) returned %d bytes\nL_ID WWPN\n", + MBC_PORT_NODE_NAME_LIST, le16_to_cpu(mc.mb[1])); for (i = 0; (i < entries) && (size < max_size); ++i) { - size += scnprintf(buf+size, max_size-size, + size += scnprintf(buffer+size, max_size-size, "%04x %02x%02x%02x%02x%02x%02x%02x%02x\n", le16_to_cpu(pmap[i].loop_id), - pmap[i].port_name[7], pmap[i].port_name[6], - pmap[i].port_name[5], pmap[i].port_name[4], - pmap[i].port_name[3], pmap[i].port_name[2], + pmap[i].port_name[7], pmap[i].port_name[6], + pmap[i].port_name[5], pmap[i].port_name[4], + pmap[i].port_name[3], pmap[i].port_name[2], pmap[i].port_name[1], pmap[i].port_name[0]); } out_free: - dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); + dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); if (size < max_size) { - /*id_list_t *pc; */ + dma_addr_t gid_list_dma; + struct gid_list_info *gid_list; char *id_iter; - unsigned long flags; struct gid_list_info *gid; - spin_lock_irqsave(&ha->hardware_lock, flags); - /* Get list of logged in devices. */ - memset(ha->gid_list, 0, GID_LIST_SIZE); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - rval = qla2x00_get_id_list(ha, ha->gid_list, ha->gid_list_dma, - &entries); - if (rval != QLA_SUCCESS) { - size += scnprintf(buf+size, max_size-size, - "qla2x00_get_id_list failed: %d", - rval); - goto get_id_failed; + gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, + &gid_list_dma, GFP_KERNEL); + if (gid_list == NULL) { + size += scnprintf(buffer+size, max_size-size, + "Unable to allocate gid_list"); + goto out_id_list_failed; } - size += scnprintf(buf+size, max_size-size, + /* Get list of logged in devices. */ + rval = qla2x00_get_id_list(ha, gid_list, gid_list_dma, + &entries); + if (rval != QLA_SUCCESS) { + size += scnprintf(buffer+size, max_size-size, + "qla2x00_get_id_list failed: %d", + rval); + goto out_free_id_list; + } + + size += scnprintf(buffer+size, max_size-size, "\nGet ID List (0x007C) returned %d entries\n" "L_ID PortID\n", entries); - - id_iter = (char *)ha->gid_list; + + id_iter = (char *)gid_list; for (i = 0; (i < entries) && (size < max_size); ++i) { gid = (struct gid_list_info *)id_iter; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - size += scnprintf(buf+size, max_size-size, + size += scnprintf(buffer+size, max_size-size, " %02x %02x%02x%02x\n", gid->loop_id_2100, gid->domain, @@ -222,110 +352,113 @@ out_free: gid->al_pa); } else { - size += scnprintf(buf+size, max_size-size, + size += scnprintf(buffer+size, max_size-size, "%04x %02x%02x%02x %02x\n", le16_to_cpu(gid->loop_id), gid->domain, gid->area, gid->al_pa, gid->loop_id_2100); - + } id_iter += ha->gid_list_info_size; } +out_free_id_list: + dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, gid_list, + gid_list_dma); } -get_id_failed: +out_id_list_failed: if (size < max_size) { fc_port_t *fcport; - char *state; + char * state; char port_type[] = "URSBIT"; - - size += scnprintf(buf+size, max_size-size, + + size += scnprintf(buffer+size, max_size-size, "\nfc_ports database\n"); list_for_each_entry_rcu(fcport, &ha->fcports, list) { if (size >= max_size) goto out; switch (atomic_read(&fcport->state)) { - case FCS_UNCONFIGURED: - state = "Unconfigured"; - break; - case FCS_DEVICE_DEAD: - state = "Dead"; - break; - case FCS_DEVICE_LOST: - state = "Lost"; - break; - case FCS_ONLINE: - state = "Online"; - break; - case FCS_NOT_SUPPORTED: - state = "Not Supported"; - break; - case FCS_FAILOVER: - state = "Failover"; - break; - case FCS_FAILOVER_FAILED: - state = "Failover Failed"; - break; - default: - state = "Unknown"; - break; + case FCS_UNCONFIGURED : state = "Unconfigured"; break; + case FCS_DEVICE_DEAD : state = "Dead"; break; + case FCS_DEVICE_LOST : state = "Lost"; break; + case FCS_ONLINE : state = "Online"; break; + case FCS_NOT_SUPPORTED : state = "Not Supported"; break; + case FCS_FAILOVER : state = "Failover"; break; + case FCS_FAILOVER_FAILED : state = "Failover Failed"; break; + default: state = "Unknown"; break; } - - size += scnprintf(buf+size, max_size-size, - "%04x %02x%02x%02x " - "%02x%02x%02x%02x%02x%02x%02x%02x " - "%c %s\n", - fcport->loop_id, - fcport->d_id.b.domain, - fcport->d_id.b.area, - fcport->d_id.b.al_pa, - fcport->port_name[0], fcport->port_name[1], - fcport->port_name[2], fcport->port_name[3], - fcport->port_name[4], fcport->port_name[5], - fcport->port_name[6], fcport->port_name[7], - port_type[fcport->port_type], state); + + size += scnprintf(buffer+size, max_size-size, + "%04x %02x%02x%02x " + "%02x%02x%02x%02x%02x%02x%02x%02x " + "%c %s\n", + fcport->loop_id, + fcport->d_id.b.domain, + fcport->d_id.b.area, + fcport->d_id.b.al_pa, + fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], + port_type[fcport->port_type], state); } } out: return size; } +extern int qla2x00_configure_loop(scsi_qla_host_t *); +extern int qla2x00_configure_local_loop(scsi_qla_host_t *); +extern int qla2x00_configure_fabric(scsi_qla_host_t *); + static ssize_t -qla2x00_update_portdb(struct device *dev, struct device_attribute *attr, - const char *buf, size_t size) +qla2x00_update_portdb(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t size) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - unsigned char reading = '0'; - switch (reading) { + if ((buffer == NULL) || (size == 0)) + goto out; + + switch (buffer[0]) { case '2': + qla_printk(KERN_INFO, ha, "Reconfiguring loop on %ld\n", + ha->host_no); qla2x00_configure_loop(ha); break; case 'l': case 'L': + qla_printk(KERN_INFO, ha, "Reconfiguring local loop on %ld\n", + ha->host_no); qla2x00_configure_local_loop(ha); break; case 'f': case 'F': + qla_printk(KERN_INFO, ha, "Reconfiguring fabric on %ld\n", + ha->host_no); qla2x00_configure_fabric(ha); default: + qla_printk(KERN_INFO, ha, "Resyncing loop on %ld\n", + ha->host_no); set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); break; } +out: return size; } + static DEVICE_ATTR(port_database, - S_IRUGO|S_IWUSR, - qla2x00_show_port_database, - qla2x00_update_portdb); + S_IRUGO|S_IWUSR, + qla2x00_show_port_database, + qla2x00_update_portdb); #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ @@ -336,12 +469,18 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj, { struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + char *rbuf = (char *)ha->fw_dump; if (ha->fw_dump_reading == 0) return 0; + if (off > ha->fw_dump_len) + return 0; + if (off + count > ha->fw_dump_len) + count = ha->fw_dump_len - off; - return memory_read_from_buffer(buf, count, &off, ha->fw_dump, - ha->fw_dump_len); + memcpy(buf, &rbuf[off], count); + + return (count); } static ssize_t @@ -404,13 +543,20 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, { struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + int size = ha->nvram_size; + char *nvram_cache = ha->nvram; - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) || off > size || count == 0) return 0; + if (off + count > size) { + size -= off; + count = size; + } /* Read NVRAM data from cache. */ - return memory_read_from_buffer(buf, count, &off, ha->nvram, - ha->nvram_size); + memcpy(buf, &nvram_cache[off], count); + + return count; } static ssize_t @@ -478,9 +624,14 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj, if (ha->optrom_state != QLA_SREADING) return 0; + if (off > ha->optrom_region_size) + return 0; + if (off + count > ha->optrom_region_size) + count = ha->optrom_region_size - off; - return memory_read_from_buffer(buf, count, &off, ha->optrom_buffer, - ha->optrom_region_size); + memcpy(buf, &ha->optrom_buffer[off], count); + + return count; } static ssize_t @@ -672,12 +823,20 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj, { struct scsi_qla_host *ha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); + int size = ha->vpd_size; + char *vpd_cache = ha->vpd; - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) || off > size || count == 0) return 0; + if (off + count > size) { + size -= off; + count = size; + } /* Read NVRAM data from cache. */ - return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size); + memcpy(buf, &vpd_cache[off], count); + + return count; } static ssize_t @@ -826,7 +985,7 @@ static ssize_t qla2x00_drvr_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str); + return scnprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str); } static ssize_t @@ -836,7 +995,7 @@ qla2x00_fw_version_show(struct device *dev, scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); char fw_str[30]; - return snprintf(buf, PAGE_SIZE, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", ha->isp_ops->fw_version_str(ha, fw_str)); } @@ -847,13 +1006,11 @@ qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); uint32_t sn; - if (IS_FWI2_CAPABLE(ha)) { - qla2xxx_get_vpd_field(ha, "SN", buf, PAGE_SIZE); - return snprintf(buf, PAGE_SIZE, "%s\n", buf); - } + if (IS_FWI2_CAPABLE(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1; - return snprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, + return scnprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, sn % 100000); } @@ -862,7 +1019,7 @@ qla2x00_isp_name_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "ISP%04X\n", ha->pdev->device); + return scnprintf(buf, PAGE_SIZE, "ISP%04X\n", ha->pdev->device); } static ssize_t @@ -870,7 +1027,7 @@ qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n", + return scnprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n", ha->product_id[0], ha->product_id[1], ha->product_id[2], ha->product_id[3]); } @@ -880,7 +1037,7 @@ qla2x00_model_name_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%s\n", ha->model_number); + return scnprintf(buf, PAGE_SIZE, "%s\n", ha->model_number); } static ssize_t @@ -888,7 +1045,7 @@ qla2x00_model_desc_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", ha->model_desc ? ha->model_desc: ""); } @@ -899,7 +1056,7 @@ qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); char pci_info[30]; - return snprintf(buf, PAGE_SIZE, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", ha->isp_ops->pci_info_str(ha, pci_info)); } @@ -912,30 +1069,30 @@ qla2x00_link_state_show(struct device *dev, struct device_attribute *attr, if (atomic_read(&ha->loop_state) == LOOP_DOWN || atomic_read(&ha->loop_state) == LOOP_DEAD) - len = snprintf(buf, PAGE_SIZE, "Link Down\n"); + len = scnprintf(buf, PAGE_SIZE, "Link Down\n"); else if (atomic_read(&ha->loop_state) != LOOP_READY || test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) - len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n"); + len = scnprintf(buf, PAGE_SIZE, "Unknown Link State\n"); else { - len = snprintf(buf, PAGE_SIZE, "Link Up - "); + len = scnprintf(buf, PAGE_SIZE, "Link Up - "); switch (ha->current_topology) { case ISP_CFG_NL: - len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Loop\n"); break; case ISP_CFG_FL: - len += snprintf(buf + len, PAGE_SIZE-len, "FL_Port\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "FL_Port\n"); break; case ISP_CFG_N: - len += snprintf(buf + len, PAGE_SIZE-len, + len += scnprintf(buf + len, PAGE_SIZE-len, "N_Port to N_Port\n"); break; case ISP_CFG_F: - len += snprintf(buf + len, PAGE_SIZE-len, "F_Port\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "F_Port\n"); break; default: - len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Loop\n"); break; } } @@ -951,10 +1108,10 @@ qla2x00_zio_show(struct device *dev, struct device_attribute *attr, switch (ha->zio_mode) { case QLA_ZIO_MODE_6: - len += snprintf(buf + len, PAGE_SIZE-len, "Mode 6\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Mode 6\n"); break; case QLA_ZIO_DISABLED: - len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); break; } return len; @@ -993,7 +1150,7 @@ qla2x00_zio_timer_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d us\n", ha->zio_timer * 100); + return scnprintf(buf, PAGE_SIZE, "%d us\n", ha->zio_timer * 100); } static ssize_t @@ -1023,9 +1180,9 @@ qla2x00_beacon_show(struct device *dev, struct device_attribute *attr, int len = 0; if (ha->beacon_blink_led) - len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Enabled\n"); else - len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); return len; } @@ -1066,7 +1223,7 @@ qla2x00_optrom_bios_version_show(struct device *dev, { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1], + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1], ha->bios_revision[0]); } @@ -1076,7 +1233,7 @@ qla2x00_optrom_efi_version_show(struct device *dev, { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1], + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1], ha->efi_revision[0]); } @@ -1086,7 +1243,7 @@ qla2x00_optrom_fcode_version_show(struct device *dev, { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1], + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1], ha->fcode_revision[0]); } @@ -1096,21 +1253,11 @@ qla2x00_optrom_fw_version_show(struct device *dev, { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n", + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n", ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2], ha->fw_revision[3]); } -static ssize_t -qla2x00_total_isp_aborts_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); - - return snprintf(buf, PAGE_SIZE, "%d\n", - ha->qla_stats.total_isp_aborts); -} - static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -1133,8 +1280,6 @@ static DEVICE_ATTR(optrom_fcode_version, S_IRUGO, qla2x00_optrom_fcode_version_show, NULL); static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show, NULL); -static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show, - NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -1153,11 +1298,12 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_optrom_efi_version, &dev_attr_optrom_fcode_version, &dev_attr_optrom_fw_version, - &dev_attr_total_isp_aborts, + &dev_attr_class2_enabled, #ifdef CONFIG_SCSI_QLA2XXX_TARGET - &dev_attr_target_mode_enabled, - &dev_attr_resource_counts, - &dev_attr_port_database, + &dev_attr_target_mode_enabled, + &dev_attr_explicit_conform_enabled, + &dev_attr_resource_counts, + &dev_attr_port_database, #endif NULL, }; @@ -1282,50 +1428,26 @@ qla2x00_get_starget_port_id(struct scsi_target *starget) } static void -qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) -{ - if (timeout) - rport->dev_loss_tmo = timeout; - else - rport->dev_loss_tmo = 1; -} - -static void -qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) +qla2x00_get_rport_loss_tmo(struct fc_rport *rport) { struct Scsi_Host *host = rport_to_shost(rport); - fc_port_t *fcport = *(fc_port_t **)rport->dd_data; + scsi_qla_host_t *ha = shost_priv(host); - qla2x00_abort_fcport_cmds(fcport); - - /* - * Transport has effectively 'deleted' the rport, clear - * all local references. - */ - spin_lock_irq(host->host_lock); - fcport->rport = NULL; - *((fc_port_t **)rport->dd_data) = NULL; - spin_unlock_irq(host->host_lock); + rport->dev_loss_tmo = ha->port_down_retry_count + 5; } static void -qla2x00_terminate_rport_io(struct fc_rport *rport) +qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) { - fc_port_t *fcport = *(fc_port_t **)rport->dd_data; + struct Scsi_Host *host = rport_to_shost(rport); + scsi_qla_host_t *ha = shost_priv(host); - /* - * At this point all fcport's software-states are cleared. Perform any - * final cleanup of firmware resources (PCBs and XCBs). - */ - if (fcport->loop_id != FC_NO_LOOP_ID) { - fcport->ha->isp_ops->fabric_logout(fcport->ha, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa); - fcport->loop_id = FC_NO_LOOP_ID; - } + if (timeout) + ha->port_down_retry_count = timeout; + else + ha->port_down_retry_count = 1; - qla2x00_abort_fcport_cmds(fcport); - scsi_target_unblock(&rport->dev); + rport->dev_loss_tmo = ha->port_down_retry_count + 5; } static int @@ -1379,7 +1501,6 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) pfc_host_stat->invalid_tx_word_count = stats->inval_xmit_word_cnt; pfc_host_stat->invalid_crc_count = stats->inval_crc_cnt; if (IS_FWI2_CAPABLE(ha)) { - pfc_host_stat->lip_count = stats->lip_cnt; pfc_host_stat->tx_frames = stats->tx_frames; pfc_host_stat->rx_frames = stats->rx_frames; pfc_host_stat->dumped_frames = stats->dumped_frames; @@ -1508,16 +1629,17 @@ vport_create_failed_2: static int qla24xx_vport_delete(struct fc_vport *fc_vport) { + scsi_qla_host_t *ha = shost_priv(fc_vport->shost); scsi_qla_host_t *vha = fc_vport->dd_data; - scsi_qla_host_t *pha = to_qla_parent(vha); - - while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) || - test_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags)) - msleep(1000); qla24xx_disable_vp(vha); qla24xx_deallocate_vp_id(vha); + mutex_lock(&ha->vport_lock); + ha->cur_vport_count--; + clear_bit(vha->vp_idx, ha->vp_idx_map); + mutex_unlock(&ha->vport_lock); + kfree(vha->node_name); kfree(vha->port_name); @@ -1582,12 +1704,11 @@ struct fc_function_template qla2xxx_transport_functions = { .get_starget_port_id = qla2x00_get_starget_port_id, .show_starget_port_id = 1, + .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo, .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo, .show_rport_dev_loss_tmo = 1, .issue_fc_host_lip = qla2x00_issue_lip, - .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk, - .terminate_rport_io = qla2x00_terminate_rport_io, .get_fc_host_stats = qla2x00_get_fc_host_stats, .vport_create = qla24xx_vport_create, @@ -1626,12 +1747,11 @@ struct fc_function_template qla2xxx_transport_vport_functions = { .get_starget_port_id = qla2x00_get_starget_port_id, .show_starget_port_id = 1, + .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo, .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo, .show_rport_dev_loss_tmo = 1, .issue_fc_host_lip = qla2x00_issue_lip, - .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk, - .terminate_rport_io = qla2x00_terminate_rport_io, .get_fc_host_stats = qla2x00_get_fc_host_stats, }; @@ -1642,7 +1762,8 @@ qla2x00_init_host_attr(scsi_qla_host_t *ha) fc_host_node_name(ha->host) = wwn_to_u64(ha->node_name); fc_host_port_name(ha->host) = wwn_to_u64(ha->port_name); - fc_host_supported_classes(ha->host) = FC_COS_CLASS3; + fc_host_supported_classes(ha->host) = ha->enable_class_2 ? + (FC_COS_CLASS2|FC_COS_CLASS3) : FC_COS_CLASS3; fc_host_max_npiv_vports(ha->host) = ha->max_npiv_vports;; fc_host_npiv_vports_inuse(ha->host) = ha->cur_vport_count; diff --git a/qla2x00t/qla_dbg.c b/qla2x00t/qla_dbg.c index 510ba64bc..cbef78576 100644 --- a/qla2x00t/qla_dbg.c +++ b/qla2x00t/qla_dbg.c @@ -216,7 +216,7 @@ qla24xx_soft_reset(scsi_qla_host_t *ha) static int qla2xxx_dump_ram(scsi_qla_host_t *ha, uint32_t addr, uint16_t *ram, - uint32_t ram_words, void **nxt) + uint16_t ram_words, void **nxt) { int rval; uint32_t cnt, stat, timer, words, idx; diff --git a/qla2x00t/qla_def.h b/qla2x00t/qla_def.h index b160f917f..c7d2a7ed4 100644 --- a/qla2x00t/qla_def.h +++ b/qla2x00t/qla_def.h @@ -94,7 +94,6 @@ #define LSD(x) ((uint32_t)((uint64_t)(x))) #define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16)) - /* * I/O register */ @@ -181,6 +180,7 @@ #define REQUEST_ENTRY_CNT_24XX 4096 /* Number of request entries. */ #define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/ #define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/ +#define ATIO_ENTRY_CNT_24XX 4096 /* Number of ATIO entries. */ /* * SCSI Request Block @@ -466,7 +466,7 @@ typedef struct { #define MBA_SYSTEM_ERR 0x8002 /* System Error. */ #define MBA_REQ_TRANSFER_ERR 0x8003 /* Request Transfer Error. */ #define MBA_RSP_TRANSFER_ERR 0x8004 /* Response Transfer Error. */ -#define MBA_WAKEUP_THRES 0x8005 /* Request Queue Wake-up. */ +#define MBA_ATIO_TRANSFER_ERR 0x8005 /* ATIO Queue Transfer Error. */ #define MBA_LIP_OCCURRED 0x8010 /* Loop Initialization Procedure */ /* occurred. */ #define MBA_LOOP_UP 0x8011 /* FC Loop UP. */ @@ -864,8 +864,7 @@ struct link_statistics { uint32_t prim_seq_err_cnt; uint32_t inval_xmit_word_cnt; uint32_t inval_crc_cnt; - uint32_t lip_cnt; - uint32_t unused1[0x1a]; + uint32_t unused1[0x1b]; uint32_t tx_frames; uint32_t rx_frames; uint32_t dumped_frames; @@ -1130,11 +1129,27 @@ typedef struct { * ISP queue - response queue entry definition. */ typedef struct { - uint8_t data[60]; + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System defined handle */ + uint8_t data[52]; uint32_t signature; #define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */ } response_t; +/* + * ISP queue - ATIO queue entry definition. + */ +typedef struct { + uint8_t entry_type; + uint8_t entry_count; + uint8_t data[58]; + uint32_t signature; +#define ATIO_PROCESSED 0xDEADDEAD /* Signature */ +} atio_t; + typedef union { uint16_t extended; struct { @@ -1529,6 +1544,8 @@ typedef struct fc_port { uint8_t node_name[WWN_SIZE]; uint8_t port_name[WWN_SIZE]; + /* True, if confirmed completion is supported */ + uint8_t conf_compl_supported:1; port_id_t d_id; uint16_t loop_id; uint16_t old_loop_id; @@ -1545,6 +1562,7 @@ typedef struct fc_port { int login_retry; atomic_t port_down_timer; + spinlock_t rport_lock; struct fc_rport *rport, *drport; u32 supported_classes; @@ -2155,10 +2173,6 @@ struct qla_chip_state_84xx { uint32_t gold_fw_version; }; -struct qla_statistics { - uint32_t total_isp_aborts; -}; - /* * Linux Host Adapter structure */ @@ -2170,6 +2184,7 @@ typedef struct scsi_qla_host { struct pci_dev *pdev; unsigned long host_no; + unsigned long instance; volatile struct { uint32_t init_done :1; @@ -2197,10 +2212,6 @@ typedef struct scsi_qla_host { uint32_t npiv_supported :1; uint32_t fce_enabled :1; uint32_t hw_event_marker_found :1; -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - uint32_t enable_target_mode :1; - uint32_t host_shutting_down :1; -#endif } flags; atomic_t loop_state; @@ -2241,7 +2252,6 @@ typedef struct scsi_qla_host { #define REGISTER_FDMI_NEEDED 26 #define FCPORT_UPDATE_NEEDED 27 #define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */ -#define UNLOADING 29 uint32_t device_flags; #define DFLG_LOCAL_DEVICES BIT_0 @@ -2338,6 +2348,28 @@ typedef struct scsi_qla_host { uint16_t rsp_ring_index; /* Current index. */ uint16_t response_q_length; + /* Protected by hw lock */ + unsigned int enable_class_2 :1; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + unsigned int enable_explicit_conf :1; + unsigned int host_shutting_down :1; + + dma_addr_t atio_dma; /* Physical address. */ + atio_t *atio_ring; /* Base virtual address */ + atio_t *atio_ring_ptr; /* Current address. */ + uint16_t atio_ring_index; /* Current index. */ + uint16_t atio_q_length; + + /* + * Protected by tgt_mutex AND hardware_lock for writing and + * tgt_mutex OR hardware_lock for reading. + */ + struct q2t_tgt *tgt; + + struct q2t_cmd *cmds[MAX_OUTSTANDING_COMMANDS]; + uint16_t current_handle; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + struct isp_operations *isp_ops; /* Outstandings ISP commands. */ @@ -2411,13 +2443,19 @@ typedef struct scsi_qla_host { struct list_head work_list; - /* - * Fibre Channel Device List. - * - * This list can be accessed from many contexts, including concurrently. - * So, although anything never deleted from this list, it needs the - * corresponding protection anyway. Let's do it by making access to - * it using RCU functions. + /* + * To surprise, fcports list isn't anyhow protected, although it can be + * accessed from many contexts, including concurrently. Seems, the + * original author of this code thought that if anything never deleted + * from this list, it is always safe to travel over it without any + * protection. Obviously, this isn't true. (The whole decision to keep + * stale entries forever is quite questionable as well.) We will fix + * that making access to fcports list using RCU functions. This isn't + * a complete solution, because it isn't clear if it's safe if some + * function going over this list misses just added entry, but definitely + * better, than currently. Also, most likely, fcports isn't the only + * variable in this structure wrongly accessed in an unprotected + * manner, so ToDo. VLNB. */ struct list_head fcports; @@ -2523,7 +2561,8 @@ typedef struct scsi_qla_host { uint32_t hw_event_ptr; uint32_t hw_event_pause_errors; - uint8_t host_str[16]; +#define HA_HOST_STR_SIZE 16 + uint8_t host_str[HA_HOST_STR_SIZE]; uint32_t pci_attr; uint16_t chip_revision; @@ -2531,7 +2570,7 @@ typedef struct scsi_qla_host { uint8_t model_number[16+1]; #define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - char model_desc[80]; + char *model_desc; uint8_t adapter_id[16+1]; uint8_t *node_name; @@ -2578,12 +2617,6 @@ typedef struct scsi_qla_host { uint16_t zio_timer; struct fc_host_statistics fc_host_stat; -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - struct q2t_cmd *cmds[MAX_OUTSTANDING_COMMANDS]; - uint16_t current_cmd; - struct q2t_tgt *tgt; -#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES]; struct list_head vp_list; /* list of VP */ @@ -2593,6 +2626,18 @@ typedef struct scsi_qla_host { uint16_t num_vsans; /* number of vsan created */ uint16_t vp_idx; /* vport ID */ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + struct mutex tgt_mutex; + + int saved_set; + uint16_t saved_exchange_count; + uint32_t saved_firmware_options_1; + uint32_t saved_firmware_options_2; + uint32_t saved_firmware_options_3; + uint8_t saved_firmware_options[2]; + uint8_t saved_add_firmware_options[2]; +#endif + struct scsi_qla_host *parent; /* holds pport */ unsigned long vp_flags; struct list_head vp_fcports; /* list of fcports */ @@ -2618,9 +2663,9 @@ typedef struct scsi_qla_host { int cur_vport_count; struct qla_chip_state_84xx *cs84xx; - struct qla_statistics qla_stats; } scsi_qla_host_t; + /* * Macros to help code, maintain, etc. */ @@ -2629,6 +2674,8 @@ typedef struct scsi_qla_host { test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \ atomic_read(&ha->loop_state) == LOOP_DOWN) +#define to_qla_host(x) ((scsi_qla_host_t *) (x)->hostdata) + #define qla_printk(level, ha, format, arg...) \ dev_printk(level , &((ha)->pdev->dev) , format , ## arg) @@ -2682,4 +2729,4 @@ typedef struct scsi_qla_host { #define CMD_ACTUAL_SNSLEN(Cmnd) ((Cmnd)->SCp.Message) #define CMD_ENTRY_STATUS(Cmnd) ((Cmnd)->SCp.have_data_in) -#endif +#endif /* __QLA_DEF_H */ diff --git a/qla2x00t/qla_gbl.h b/qla2x00t/qla_gbl.h index cd70f3965..4172c5a9b 100644 --- a/qla2x00t/qla_gbl.h +++ b/qla2x00t/qla_gbl.h @@ -49,10 +49,6 @@ extern void qla2x00_try_to_stop_firmware(scsi_qla_host_t *); extern void qla84xx_put_chip(struct scsi_qla_host *); -extern int qla2x00_configure_loop(scsi_qla_host_t *ha); -extern int qla2x00_configure_local_loop(scsi_qla_host_t *ha); -extern int qla2x00_configure_fabric(scsi_qla_host_t *ha); - /* * Global Data in qla_os.c source file. */ @@ -66,7 +62,7 @@ extern int ql2xfdmienable; extern int ql2xallocfwdump; extern int ql2xextended_error_logging; extern int ql2xqfullrampup; -extern int ql2xiidmaenable; +extern int num_hosts; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -75,8 +71,6 @@ extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t, uint16_t, uint16_t); -extern void qla2x00_abort_fcport_cmds(fc_port_t *); - /* * Global Functions in qla_mid.c source file. */ @@ -118,6 +112,7 @@ extern void qla2x00_vp_abort_isp(scsi_qla_host_t *); * Global Function Prototypes in qla_iocb.c source file. */ extern void qla2x00_isp_cmd(scsi_qla_host_t *); + extern uint16_t qla2x00_calc_iocbs_32(uint16_t); extern uint16_t qla2x00_calc_iocbs_64(uint16_t); extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t); @@ -130,9 +125,6 @@ int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); /* * Global Function Prototypes in qla_mbx.c source file. */ -extern int -qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp); - extern int qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); @@ -322,7 +314,8 @@ extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t, uint16_t, uint16_t); extern void qla2xxx_get_flash_info(scsi_qla_host_t *); -extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); + +extern int qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp); /* * Global Function Prototypes in qla_dbg.c source file. @@ -373,5 +366,4 @@ extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *); */ extern int qla2x00_dfs_setup(scsi_qla_host_t *); extern int qla2x00_dfs_remove(scsi_qla_host_t *); - #endif /* _QLA_GBL_H */ diff --git a/qla2x00t/qla_gs.c b/qla2x00t/qla_gs.c index 853f1d30d..db60475f3 100644 --- a/qla2x00t/qla_gs.c +++ b/qla2x00t/qla_gs.c @@ -5,6 +5,7 @@ * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla2x_tgt.h" static int qla2x00_sns_ga_nxt(scsi_qla_host_t *, fc_port_t *); static int qla2x00_sns_gid_pt(scsi_qla_host_t *, sw_info_t *); @@ -544,8 +545,12 @@ qla2x00_rff_id(scsi_qla_host_t *ha) /* * FC-4 Feature bit 0 indicates target functionality to the name server. */ - if (ha->flags.enable_target_mode) { + if (qla_tgt_mode_enabled(ha)) { +#ifdef CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE + ct_req->req.rff_id.fc4_feature = BIT_0; +#else ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; +#endif } else #endif ct_req->req.rff_id.fc4_feature = BIT_1; @@ -1670,12 +1675,6 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha) { int rval; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - DEBUG2(printk("scsi(%ld): FDMI unsupported on " - "ISP2100/ISP2200.\n", ha->host_no)); - return QLA_SUCCESS; - } - rval = qla2x00_mgmt_svr_login(ha); if (rval) return rval; diff --git a/qla2x00t/qla_init.c b/qla2x00t/qla_init.c index c4da19ac2..c2a6f5be9 100644 --- a/qla2x00t/qla_init.c +++ b/qla2x00t/qla_init.c @@ -15,8 +15,10 @@ #include #endif -#ifdef CONFIG_SCSI_QLA2XXX_TARGET #include "qla2x_tgt.h" + +#ifndef CONFIG_SCSI_QLA2XXX_TARGET +#warning "CONFIG_SCSI_QLA2XXX_TARGET is not set" #endif /* @@ -29,9 +31,13 @@ static void qla2x00_init_response_q_entries(scsi_qla_host_t *); static int qla2x00_init_rings(scsi_qla_host_t *); static int qla2x00_fw_ready(scsi_qla_host_t *); static int qla2x00_configure_hba(scsi_qla_host_t *); +int qla2x00_configure_loop(scsi_qla_host_t *); +int qla2x00_configure_local_loop(scsi_qla_host_t *); +int qla2x00_configure_fabric(scsi_qla_host_t *); static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *); static int qla2x00_device_resync(scsi_qla_host_t *); -static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *, uint16_t *); +static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *, + uint16_t *); static int qla2x00_restart_isp(scsi_qla_host_t *); @@ -124,10 +130,10 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (rval == QLA_SUCCESS) { /* Enable target response to SCSI bus. */ - if (ha->tgt != NULL) - qla2x00_enable_lun(ha); + if (qla_tgt_mode_enabled(ha)) + qla2x00_send_enable_lun(ha, true); else - qla2x00_disable_lun(ha); + qla2x00_send_enable_lun(ha, false); } #endif @@ -344,8 +350,6 @@ static int qla2x00_isp_firmware(scsi_qla_host_t *ha) { int rval; - uint16_t loop_id, topo, sw_cap; - uint8_t domain, area, al_pa; /* Assume loading risc code */ rval = QLA_FUNCTION_FAILED; @@ -357,11 +361,6 @@ qla2x00_isp_firmware(scsi_qla_host_t *ha) /* Verify checksum of loaded RISC code. */ rval = qla2x00_verify_checksum(ha, ha->fw_srisc_address); - if (rval == QLA_SUCCESS) { - /* And, verify we are not in ROM code. */ - rval = qla2x00_get_adapter_id(ha, &loop_id, &al_pa, - &area, &domain, &topo, &sw_cap); - } } if (rval) { @@ -618,8 +617,8 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) /* Assume a failed state */ rval = QLA_FUNCTION_FAILED; - DEBUG3(printk("scsi(%ld): Testing device at %lx.\n", - ha->host_no, (u_long)®->flash_address)); + DEBUG3(printk(KERN_WARNING "scsi(%ld): Testing device at %p\n", + ha->host_no, ®->flash_address)); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -634,23 +633,21 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) if (!IS_QLA23XX(ha)) { /* - * We need to have a delay here since the card will not respond - * while in reset causing an MCA on some architectures. + * We need to have a delay here since the card will not respond while + * in reset causing an MCA on some architectures. */ udelay(30); data = qla2x00_debounce_register(®->ctrl_status); - for (cnt = 6000000 ; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) { + for (cnt = 6000000; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) { udelay(5); data = RD_REG_WORD(®->ctrl_status); barrier(); } - if (!cnt) goto chip_diag_failed; - DEBUG3(printk(KERN_INFO "scsi(%ld): Reset register cleared by " - "chip reset\n", ha->host_no)); - + DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n", + ha->host_no)); } else { /* * Should be 16 PCI Clock cycles, but this is arbitrarily safer @@ -672,10 +669,8 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) data = RD_MAILBOX_REG(ha, reg, 0); barrier(); } - if (!cnt) goto chip_diag_failed; - } else udelay(10); @@ -733,7 +728,7 @@ qla2x00_chip_diag(scsi_qla_host_t *ha) spin_lock_irqsave(&ha->hardware_lock, flags); chip_diag_failed: - if (rval) + if (rval != QLA_SUCCESS) DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED " "****\n", ha->host_no)); @@ -756,7 +751,7 @@ qla24xx_chip_diag(scsi_qla_host_t *ha) /* Perform RISC reset. */ qla24xx_reset_risc(ha); - ha->fw_transfer_size = REQUEST_ENTRY_SIZE * ha->request_q_length; + ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024; rval = qla2x00_mbx_reg_test(ha); if (rval) { @@ -802,38 +797,6 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *ha) mem_size = (ha->fw_memory_size - 0x100000 + 1) * sizeof(uint32_t); - /* Allocate memory for Fibre Channel Event Buffer. */ - if (!IS_QLA25XX(ha)) - goto try_eft; - - tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, - GFP_KERNEL); - if (!tc) { - qla_printk(KERN_WARNING, ha, "Unable to allocate " - "(%d KB) for FCE.\n", FCE_SIZE / 1024); - goto try_eft; - } - - memset(tc, 0, FCE_SIZE); - rval = qla2x00_enable_fce_trace(ha, tc_dma, FCE_NUM_BUFFERS, - ha->fce_mb, &ha->fce_bufs); - if (rval) { - qla_printk(KERN_WARNING, ha, "Unable to initialize " - "FCE (%d).\n", rval); - dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc, - tc_dma); - ha->flags.fce_enabled = 0; - goto try_eft; - } - - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n", - FCE_SIZE / 1024); - - fce_size = sizeof(struct qla2xxx_fce_chain) + EFT_SIZE; - ha->flags.fce_enabled = 1; - ha->fce_dma = tc_dma; - ha->fce = tc; -try_eft: /* Allocate memory for Extended Trace Buffer. */ tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, GFP_KERNEL); @@ -859,6 +822,38 @@ try_eft: eft_size = EFT_SIZE; ha->eft_dma = tc_dma; ha->eft = tc; + + /* Allocate memory for Fibre Channel Event Buffer. */ + if (!IS_QLA25XX(ha)) + goto cont_alloc; + + tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, + GFP_KERNEL); + if (!tc) { + qla_printk(KERN_WARNING, ha, "Unable to allocate " + "(%d KB) for FCE.\n", FCE_SIZE / 1024); + goto cont_alloc; + } + + memset(tc, 0, FCE_SIZE); + rval = qla2x00_enable_fce_trace(ha, tc_dma, FCE_NUM_BUFFERS, + ha->fce_mb, &ha->fce_bufs); + if (rval) { + qla_printk(KERN_WARNING, ha, "Unable to initialize " + "FCE (%d).\n", rval); + dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc, + tc_dma); + ha->flags.fce_enabled = 0; + goto cont_alloc; + } + + qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n", + FCE_SIZE / 1024); + + fce_size = sizeof(struct qla2xxx_fce_chain) + EFT_SIZE; + ha->flags.fce_enabled = 1; + ha->fce_dma = tc_dma; + ha->fce = tc; } cont_alloc: req_q_size = ha->request_q_length * sizeof(request_t); @@ -1003,9 +998,8 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) &ha->fw_attributes, &ha->fw_memory_size); qla2x00_resize_request_q(ha); ha->flags.npiv_supported = 0; - if ((IS_QLA24XX(ha) || IS_QLA25XX(ha) || - IS_QLA84XX(ha)) && - (ha->fw_attributes & BIT_2)) { + if ((IS_QLA24XX(ha) || IS_QLA25XX(ha)) && + (ha->fw_attributes & BIT_2)) { ha->flags.npiv_supported = 1; if ((!ha->max_npiv_vports) || ((ha->max_npiv_vports + 1) % @@ -1037,7 +1031,7 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } - if (rval) { + if (rval != QLA_SUCCESS) { DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n", ha->host_no)); } @@ -1068,6 +1062,31 @@ qla2x00_init_response_q_entries(scsi_qla_host_t *ha) } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +/** + * qla2x00_init_atio_q_entries() - Initializes ATIO queue entries. + * @ha: HA context + * + * Beginning of ATIO ring has initialization control block already built + * by nvram config routine. + * + * Returns 0 on success. + */ +static void +qla2x00_init_atio_q_entries(scsi_qla_host_t *ha) +{ + uint16_t cnt; + atio_t *pkt; + + pkt = ha->atio_ring; + for (cnt = 0; cnt < ha->atio_q_length; cnt++) { + pkt->signature = ATIO_PROCESSED; + pkt++; + } + +} +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + /** * qla2x00_update_fw_options() - Read and process firmware options. * @ha: HA context @@ -1206,11 +1225,24 @@ qla24xx_config_rings(struct scsi_qla_host *ha) icb->response_q_address[0] = cpu_to_le32(LSD(ha->response_dma)); icb->response_q_address[1] = cpu_to_le32(MSD(ha->response_dma)); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + icb->atio_q_inpointer = __constant_cpu_to_le16(0); + icb->atio_q_length = cpu_to_le16(ha->atio_q_length); + icb->atio_q_address[0] = cpu_to_le32(LSD(ha->atio_dma)); + icb->atio_q_address[1] = cpu_to_le32(MSD(ha->atio_dma)); +#endif + WRT_REG_DWORD(®->req_q_in, 0); WRT_REG_DWORD(®->req_q_out, 0); WRT_REG_DWORD(®->rsp_q_in, 0); WRT_REG_DWORD(®->rsp_q_out, 0); RD_REG_DWORD(®->rsp_q_out); + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + WRT_REG_DWORD(®->atio_q_in, 0); + WRT_REG_DWORD(®->atio_q_out, 0); + RD_REG_DWORD(®->atio_q_out); +#endif } /** @@ -1253,6 +1285,12 @@ qla2x00_init_rings(scsi_qla_host_t *ha) /* Initialize response queue entries */ qla2x00_init_response_q_entries(ha); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->atio_ring_ptr = ha->atio_ring; + ha->atio_ring_index = 0; + qla2x00_init_atio_q_entries(ha); /* Initialize ATIO queue entries */ +#endif + ha->isp_ops->config_rings(ha); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -1268,7 +1306,7 @@ qla2x00_init_rings(scsi_qla_host_t *ha) mid_init_cb->options = __constant_cpu_to_le16(BIT_1); rval = qla2x00_init_firmware(ha, ha->init_cb_size); - if (rval) { + if (rval != QLA_SUCCESS) { DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n", ha->host_no)); } else { @@ -1393,9 +1431,9 @@ qla2x00_fw_ready(scsi_qla_host_t *ha) DEBUG(printk("scsi(%ld): fw_state=%x curr time=%lx.\n", ha->host_no, state[0], jiffies)); - if (rval) { - DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n", - ha->host_no)); + if (rval != QLA_SUCCESS) { + DEBUG2_3(printk("scsi(%ld): Firmware not ready (%x).\n", + ha->host_no, rval)); } return (rval); @@ -1536,25 +1574,18 @@ qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *de index = (ha->pdev->subsystem_device & 0xff); if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && index < QLA_MODEL_NAMES) - strncpy(ha->model_desc, - qla2x00_model_name[index * 2 + 1], - sizeof(ha->model_desc) - 1); + ha->model_desc = qla2x00_model_name[index * 2 + 1]; } else { index = (ha->pdev->subsystem_device & 0xff); if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && index < QLA_MODEL_NAMES) { strcpy(ha->model_number, qla2x00_model_name[index * 2]); - strncpy(ha->model_desc, - qla2x00_model_name[index * 2 + 1], - sizeof(ha->model_desc) - 1); + ha->model_desc = qla2x00_model_name[index * 2 + 1]; } else { strcpy(ha->model_number, def); } } - if (IS_FWI2_CAPABLE(ha)) - qla2xxx_get_vpd_field(ha, "\x82", ha->model_desc, - sizeof(ha->model_desc)); } /* On sparc systems, obtain port and node WWN from firmware @@ -1605,6 +1636,13 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) rval = QLA_SUCCESS; + if (unlikely(nv == NULL)) { + qla_printk(KERN_ERR, ha, "request_ring pointer is NULL\n"); + dump_stack(); + rval = 1; + goto out; + } + /* Determine NVRAM starting address. */ ha->nvram_size = sizeof(nvram_t); ha->nvram_base = 0; @@ -1704,21 +1742,76 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) /* * Setup driver NVRAM options. */ - nv->firmware_options[0] |= (BIT_6 | BIT_1); - nv->firmware_options[0] &= ~(BIT_5); #ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (ha->flags.enable_target_mode) - nv->firmware_options[0] |= BIT_4; - else + if (!IS_QLA2100(ha)) { + /* Check if target mode enabled */ + if (qla_tgt_mode_enabled(ha)) { + if (!ha->saved_set) { + /* We save only once */ + ha->saved_firmware_options[0] = nv->firmware_options[0]; + ha->saved_firmware_options[1] = nv->firmware_options[1]; + ha->saved_add_firmware_options[0] = nv->add_firmware_options[0]; + ha->saved_add_firmware_options[1] = nv->add_firmware_options[1]; + ha->saved_set = 1; + } + + /* Enable target mode */ + nv->firmware_options[0] |= BIT_4; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE + nv->firmware_options[0] |= BIT_5; +#endif + + /* Disable Full Login after LIP */ + nv->firmware_options[1] &= ~BIT_5; + /* Enable FC tapes support, just in case, it is ON by default */ + nv->add_firmware_options[1] |= BIT_4; + /* Enable Fibre Channel Confirm */ + nv->add_firmware_options[1] |= BIT_5; + /* Enable Command Queuing in Target Mode */ + nv->add_firmware_options[1] |= BIT_6; + } else { + if (ha->saved_set) { + nv->firmware_options[0] = ha->saved_firmware_options[0]; + nv->firmware_options[1] = ha->saved_firmware_options[1]; + nv->add_firmware_options[0] = ha->saved_add_firmware_options[0]; + nv->add_firmware_options[1] = ha->saved_add_firmware_options[1]; + } + } + } #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - nv->firmware_options[0] &= ~BIT_4; - nv->firmware_options[1] |= (BIT_5 | BIT_0); + + if (!IS_QLA2100(ha)) { + if (ha->enable_class_2) { + if (ha->flags.init_done) { + fc_host_supported_classes(ha->host) = + FC_COS_CLASS2 | FC_COS_CLASS3; + } + nv->add_firmware_options[1] |= BIT_0; + } else { + if (ha->flags.init_done) { + fc_host_supported_classes(ha->host) = + FC_COS_CLASS3; + } + nv->add_firmware_options[1] &= BIT_0; + } + } + + /* Enable ADISC and fairness */ + nv->firmware_options[0] |= (BIT_6 | BIT_1); + /* Enable PDB changed AE */ + nv->firmware_options[1] |= BIT_0; + /* Stop Port Queue on Full Status */ nv->firmware_options[1] &= ~BIT_4; if (IS_QLA23XX(ha)) { + /* Enable full duplex */ nv->firmware_options[0] |= BIT_2; + /* Disable Fast Sataus Posting */ nv->firmware_options[0] &= ~BIT_3; + /* P2P preferred, otherwise loop */ nv->add_firmware_options[1] |= BIT_5 | BIT_4; + /* out-of-order frames rassembly */ + nv->special_options[0] |= BIT_6; if (IS_QLA2300(ha)) { if (ha->fb_rev == FPM_2310) { @@ -1731,6 +1824,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) sizeof(nv->model_number), "QLA23xx"); } } else if (IS_QLA2200(ha)) { + /* Enable full duplex */ nv->firmware_options[0] |= BIT_2; /* * 'Point-to-point preferred, else loop' is not a safe @@ -1899,7 +1993,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) ha->flags.process_response_queue = 1; } } - +out: if (rval) { DEBUG2_3(printk(KERN_WARNING "scsi(%ld): NVRAM configuration failed!\n", ha->host_no)); @@ -1912,13 +2006,19 @@ qla2x00_rport_del(void *data) { fc_port_t *fcport = data; struct fc_rport *rport; + unsigned long flags; - spin_lock_irq(fcport->ha->host->host_lock); + spin_lock_irqsave(&fcport->rport_lock, flags); rport = fcport->drport; fcport->drport = NULL; - spin_unlock_irq(fcport->ha->host->host_lock); - if (rport) + spin_unlock_irqrestore(&fcport->rport_lock, flags); + if (rport) { fc_remote_port_delete(rport); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_fc_port_deleted) + qla_target.tgt_fc_port_deleted(fcport->ha, fcport); +#endif + } } /** @@ -1945,6 +2045,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) atomic_set(&fcport->state, FCS_UNCONFIGURED); fcport->flags = FCF_RLC_SUPPORT; fcport->supported_classes = FC_COS_UNSPECIFIED; + spin_lock_init(&fcport->rport_lock); return fcport; } @@ -1961,7 +2062,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) * 1 = error. * 2 = database was full and device was not configured. */ -int + int qla2x00_configure_loop(scsi_qla_host_t *ha) { int rval; @@ -2053,10 +2154,8 @@ qla2x00_configure_loop(scsi_qla_host_t *ha) if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { if (test_bit(LOCAL_LOOP_UPDATE, &save_flags)) set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); - if (test_bit(RSCN_UPDATE, &save_flags)) { - ha->flags.rscn_queue_overflow = 1; + if (test_bit(RSCN_UPDATE, &save_flags)) set_bit(RSCN_UPDATE, &ha->dpc_flags); - } } return (rval); @@ -2074,7 +2173,7 @@ qla2x00_configure_loop(scsi_qla_host_t *ha) * Returns: * 0 = success. */ -int + int qla2x00_configure_local_loop(scsi_qla_host_t *ha) { int rval, rval2; @@ -2291,24 +2390,33 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) { struct fc_rport_identifiers rport_ids; struct fc_rport *rport; + unsigned long flags; if (fcport->drport) qla2x00_rport_del(fcport); + if (fcport->rport) + return; + rport_ids.node_name = wwn_to_u64(fcport->node_name); rport_ids.port_name = wwn_to_u64(fcport->port_name); rport_ids.port_id = fcport->d_id.b.domain << 16 | fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; - fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids); + rport = fc_remote_port_add(ha->host, 0, &rport_ids); if (!rport) { qla_printk(KERN_WARNING, ha, "Unable to allocate fc remote port!\n"); return; } - spin_lock_irq(fcport->ha->host->host_lock); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_fc_port_added) + qla_target.tgt_fc_port_added(ha, fcport); +#endif + spin_lock_irqsave(&fcport->rport_lock, flags); + fcport->rport = rport; *((fc_port_t **)rport->dd_data) = fcport; - spin_unlock_irq(fcport->ha->host->host_lock); + spin_unlock_irqrestore(&fcport->rport_lock, flags); rport->supported_classes = fcport->supported_classes; @@ -2366,7 +2474,7 @@ qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) * 0 = success. * BIT_0 = error */ -int + int qla2x00_configure_fabric(scsi_qla_host_t *ha) { int rval, rval2; @@ -2442,8 +2550,9 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) } rval = qla2x00_find_all_fabric_devs(ha, &new_fcports); - if (rval != QLA_SUCCESS) + if (rval != QLA_SUCCESS) { break; + } /* * Logout all previous fabric devices marked lost, except @@ -2612,8 +2721,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) { kfree(swl); swl = NULL; - } else if (ql2xiidmaenable && - qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) { + } else if (qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) { qla2x00_gpsc(ha, swl); } } @@ -2787,7 +2895,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) continue; /* If device was not in our fcports list, then add it. */ - list_add_tail(&new_fcport->list, new_fcports); + list_add_tail_rcu(&new_fcport->list, new_fcports); /* Allocate a new replacement fcport. */ nxt_d_id.b24 = new_fcport->d_id.b24; @@ -2982,11 +3090,12 @@ qla2x00_device_resync(scsi_qla_host_t *ha) continue; if (atomic_read(&fcport->state) == FCS_ONLINE) { +#ifndef CONFIG_SCSI_QLA2XXX_TARGET if (format != 3 || - fcport->port_type != FCT_INITIATOR) { + fcport->port_type != FCT_INITIATOR) +#endif qla2x00_mark_device_lost(ha, fcport, 0, 0); - } } fcport->flags &= ~FCF_FARP_DONE; } @@ -3123,6 +3232,13 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, if (mb[10] & BIT_1) fcport->supported_classes |= FC_COS_CLASS3; + if (IS_FWI2_CAPABLE(ha)) { + if (mb[10] & BIT_7) + fcport->conf_compl_supported = 1; + } else { + /* mb[10] bits are undocumented, ToDo */ + } + rval = QLA_SUCCESS; break; } else if (mb[0] == MBS_LOOP_ID_USED) { @@ -3254,7 +3370,7 @@ qla2x00_loop_resync(scsi_qla_host_t *ha) return (QLA_FUNCTION_FAILED); } - if (rval) { + if (rval != QLA_SUCCESS) { DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__)); } @@ -3267,10 +3383,10 @@ qla2x00_update_fcports(scsi_qla_host_t *ha) fc_port_t *fcport; /* Go with deferred removal of rport references. */ - list_for_each_entry_rcu(fcport, &ha->fcports, list) - if (fcport->drport && - atomic_read(&fcport->state) != FCS_UNCONFIGURED) + list_for_each_entry_rcu(fcport, &ha->fcports, list) { + if (fcport->drport) qla2x00_rport_del(fcport); + } } /* @@ -3288,23 +3404,19 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) { int rval; uint8_t status = 0; - scsi_qla_host_t *vha; if (ha->flags.online) { ha->flags.online = 0; clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - ha->qla_stats.total_isp_aborts++; qla_printk(KERN_INFO, ha, - "Performing ISP error recovery - ha= %p.\n", ha); + "Performing ISP abort - ha= %p.\n", ha); ha->isp_ops->reset_chip(ha); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); qla2x00_mark_all_devices_lost(ha, 0); - list_for_each_entry(vha, &ha->vp_list, vp_list) - qla2x00_mark_all_devices_lost(vha, 0); } else { if (!atomic_read(&ha->loop_down_timer)) atomic_set(&ha->loop_down_timer, @@ -3332,9 +3444,9 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) ha->flags.online = 1; #ifdef CONFIG_SCSI_QLA2XXX_TARGET - /* Enable target response to SCSI bus */ - if (ha->flags.enable_target_mode) - qla2x00_enable_lun(ha); + /* Enable target response to SCSI bus. */ + if (qla_tgt_mode_enabled(ha)) + qla2x00_send_enable_lun(ha, true); #endif ha->isp_ops->enable_intrs(ha); @@ -3342,6 +3454,17 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) ha->isp_abort_cnt = 0; clear_bit(ISP_ABORT_RETRY, &ha->dpc_flags); + if (ha->eft) { + memset(ha->eft, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(ha, + ha->eft_dma, EFT_NUM_BUFFERS); + if (rval) { + qla_printk(KERN_WARNING, ha, + "Unable to reinitialize EFT " + "(%d).\n", rval); + } + } + if (ha->fce) { ha->flags.fce_enabled = 1; memset(ha->fce, 0, @@ -3356,17 +3479,6 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) ha->flags.fce_enabled = 0; } } - - if (ha->eft) { - memset(ha->eft, 0, EFT_SIZE); - rval = qla2x00_enable_eft_trace(ha, - ha->eft_dma, EFT_NUM_BUFFERS); - if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to reinitialize EFT " - "(%d).\n", rval); - } - } } else { /* failed the ISP abort */ ha->flags.online = 1; if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) { @@ -3628,6 +3740,62 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) rval = 1; } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + /* Check if target mode enabled */ + if (qla_tgt_mode_enabled(ha)) { + if (!ha->saved_set) { + /* We save only once */ + ha->saved_exchange_count = nv->exchange_count; + ha->saved_firmware_options_1 = nv->firmware_options_1; + ha->saved_firmware_options_2 = nv->firmware_options_2; + ha->saved_firmware_options_3 = nv->firmware_options_3; + ha->saved_set = 1; + } + + nv->exchange_count = __constant_cpu_to_le16(0xFFFF); + + /* Enable target mode */ + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET_DISABLE_INI_MODE + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5); +#endif + + /* Disable Full Login after LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + /* Enable FC tapes support, just in case, it is ON by default */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + } else { + if (ha->saved_set) { + nv->exchange_count = ha->saved_exchange_count; + nv->firmware_options_1 = ha->saved_firmware_options_1; + nv->firmware_options_2 = ha->saved_firmware_options_2; + nv->firmware_options_3 = ha->saved_firmware_options_3; + } + } +#endif + /* out-of-order frames reassembly */ + nv->firmware_options_3 |= BIT_6|BIT_9; + + if (ha->enable_class_2) { + if (ha->flags.init_done) { + fc_host_supported_classes(ha->host) = + FC_COS_CLASS2 | FC_COS_CLASS3; + } + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); + } else { + if (ha->flags.init_done) + fc_host_supported_classes(ha->host) = FC_COS_CLASS3; + nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); + } + +#if 0 + /* + * firmware_options_3 bits 3-2 are reserved, but for some reason + * sometimes set by BIOS. Let's explicitly reset them. + */ + nv->firmware_options_3 &= ~__constant_cpu_to_le32(BIT_3|BIT_2); +#endif + /* Reset Initialization control block */ memset(icb, 0, sizeof(struct init_cb_24xx)); @@ -3963,6 +4131,110 @@ fail_fw_integrity: return QLA_FUNCTION_FAILED; } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + +/* + * qla2x00_enable_tgt_mode - NO LOCK HELD + * + * host_reset, bring up w/ Target Mode Enabled + */ +void +qla2x00_enable_tgt_mode(scsi_qla_host_t *ha) +{ + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + qla_set_tgt_mode(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + qla2xxx_wake_dpc(ha); + qla2x00_wait_for_hba_online(ha); +} + +/* + * qla2x00_disable_tgt_mode - NO LOCK HELD + * + * Disable Target Mode and reset the adapter + */ +void +qla2x00_disable_tgt_mode(scsi_qla_host_t *ha) +{ + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + qla_clear_tgt_mode(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + qla2xxx_wake_dpc(ha); + qla2x00_wait_for_hba_online(ha); +} + +/* + * qla2x00_issue_marker + * Issue marker + * Caller CAN have hardware lock held as specified by ha_locked parameter. + * Might release it, then reaquire. + */ +int qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked) +{ + if (ha_locked) { + if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) + return (QLA_FUNCTION_FAILED); + } else { + if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) + return (QLA_FUNCTION_FAILED); + } + ha->marker_needed = 0; + return (QLA_SUCCESS); +} + +int qla2xxx_tgt_register_driver(struct qla_tgt_initiator *tgt_data, + struct qla_target *init_data) +{ + int res = 0; + + ENTER(__func__); + + if ((tgt_data == NULL) || (tgt_data->magic != QLA2X_TARGET_MAGIC)) { + printk("***ERROR*** Wrong version of the target mode add-on: " + "%d\n", tgt_data->magic); + res = -EINVAL; + goto out; + } + + memcpy(&qla_target, tgt_data, sizeof(qla_target)); + + init_data->magic = QLA2X_INITIATOR_MAGIC; + init_data->req_pkt = qla2x00_req_pkt; + init_data->isp_cmd = qla2x00_isp_cmd; + init_data->enable_tgt_mode = qla2x00_enable_tgt_mode; + init_data->disable_tgt_mode = qla2x00_disable_tgt_mode; + init_data->issue_marker = qla2x00_issue_marker; + init_data->mark_all_devices_lost = qla2x00_mark_all_devices_lost; + init_data->get_port_database = qla2x00_get_port_database; + init_data->get_id_list = qla2x00_get_id_list; + +out: + LEAVE(__func__); + return res; +} +EXPORT_SYMBOL_GPL(qla2xxx_tgt_register_driver); + +void qla2xxx_tgt_unregister_driver(void) +{ + ENTER(__func__); + memset(&qla_target, 0, sizeof(qla_target)); + LEAVE(__func__); + return; +} +EXPORT_SYMBOL_GPL(qla2xxx_tgt_unregister_driver); + +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + int qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) { @@ -4085,8 +4357,8 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) ret = qla2x00_stop_firmware(ha); for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT && retries ; retries--) { - ha->isp_ops->reset_chip(ha); - if (ha->isp_ops->chip_diag(ha) != QLA_SUCCESS) + qla2x00_reset_chip(ha); + if (qla2x00_chip_diag(ha) != QLA_SUCCESS) continue; if (qla2x00_setup_chip(ha) != QLA_SUCCESS) continue; @@ -4108,7 +4380,7 @@ qla24xx_configure_vhba(scsi_qla_host_t *ha) rval = qla2x00_fw_ready(ha->parent); if (rval == QLA_SUCCESS) { clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); - qla2x00_marker(ha, 0, 0, MK_SYNC_ALL); + qla2x00_marker(ha->parent, 0, 0, MK_SYNC_ALL); } ha->flags.management_server_logged_in = 0; @@ -4133,150 +4405,6 @@ qla24xx_configure_vhba(scsi_qla_host_t *ha) return rval; } -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - -/* - * __qla2x00_enable_lun - NO LOCK HELD - * - * host_reset, bring up w/ Target Mode Enabled - */ -static void -__qla2x00_enable_lun(scsi_qla_host_t *ha) -{ - unsigned long flags = 0; - - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->flags.enable_target_mode = 1; - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); -} - -/* - * __qla2x00_disable_lun - NO LOCK HELD - * - * Disable Target Mode and reset the adapter - */ -static void -__qla2x00_disable_lun(scsi_qla_host_t *ha) -{ - unsigned long flags = 0; - - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->flags.enable_target_mode = 0; - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); - qla2xxx_wake_dpc(ha); - qla2x00_wait_for_hba_online(ha); -} - -/* - * __qla2x00_issue_marker - * Issue marker - * Caller MUST have hardware lock held - */ - -static int -__qla2x00_issue_marker(scsi_qla_host_t *ha) -{ - /* Send marker if required */ - if (ha->marker_needed != 0) { - if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) - return QLA_FUNCTION_FAILED; - ha->marker_needed = 0; - } - return QLA_SUCCESS; -} - -static int -qla2x00_get_resource_counts(scsi_qla_host_t *ha, uint8_t *cmd, uint8_t *imm) -{ - mbx_cmd_t mc; - int rval; - - /* Ask the card */ - mc.mb[0] = MBC_GET_RESOURCE_COUNTS; - mc.out_mb = MBX_0; - mc.in_mb = MBX_0|MBX_1|MBX_2; - mc.tov = 30; - mc.flags = 0; - - rval = qla2x00_mailbox_command(ha, &mc); - - *cmd = mc.mb[1]; - *imm = mc.mb[2]; - - return rval; -} - -/* - * qla2x00_req_cont_pkt - * Retrieve a continue packet from request que - * Caller MUST have hardware lock held - */ -static cont_entry_t * -qla2x00_req_cont_pkt(scsi_qla_host_t *ha) -{ - /* Adjust ring index. */ - ha->req_ring_index++; - if (ha->req_ring_index == ha->request_q_length) { - ha->req_ring_index = 0; - ha->request_ring_ptr = ha->request_ring; - } else { - ha->request_ring_ptr++; - } - - return (cont_entry_t *)ha->request_ring_ptr; -} - -int -qla2xxx_tgt_register_driver(struct qla2x_tgt_initiator *tgt_data, - struct qla2x_tgt_target *init_data) -{ - int res = 0; - - ENTER(__func__); - - if ((tgt_data == NULL) || (tgt_data->magic != QLA2X_TARGET_MAGIC)) { - printk(KERN_INFO "***ERROR*** Wrong version of the target " - "mode add-on: %d\n", tgt_data->magic); - res = -EINVAL; - goto out; - } - - memcpy(&qla_target, tgt_data, sizeof(qla_target)); - - init_data->magic = QLA2X_INITIATOR_MAGIC; - init_data->req_pkt = qla2x00_req_pkt; - init_data->isp_cmd = qla2x00_isp_cmd; - init_data->enable_lun = __qla2x00_enable_lun; - init_data->disable_lun = __qla2x00_disable_lun; - init_data->issue_marker = __qla2x00_issue_marker; - init_data->req_cont_pkt = qla2x00_req_cont_pkt; - init_data->get_counts = qla2x00_get_resource_counts; - /* ... */ - -out: - LEAVE(__func__); - return res; -} -EXPORT_SYMBOL_GPL(qla2xxx_tgt_register_driver); - -void -qla2xxx_tgt_unregister_driver(void) -{ - ENTER(__func__); - memset(&qla_target, 0, sizeof(qla_target)); - LEAVE(__func__); - return; -} -EXPORT_SYMBOL_GPL(qla2xxx_tgt_unregister_driver); - -#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - /* 84XX Support **************************************************************/ static LIST_HEAD(qla_cs84xx_list); diff --git a/qla2x00t/qla_inline.h b/qla2x00t/qla_inline.h index 92fafbdbb..19820a804 100644 --- a/qla2x00t/qla_inline.h +++ b/qla2x00t/qla_inline.h @@ -47,32 +47,6 @@ to_qla_parent(scsi_qla_host_t *ha) return ha->parent ? ha->parent : ha; } -/** - * qla2x00_issue_marker() - Issue a Marker IOCB if necessary. - * @ha: HA context - * @ha_locked: is function called with the hardware lock - * - * Returns non-zero if a failure occured, else zero. - */ -static inline int -qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked) -{ - /* Send marker if required */ - if (ha->marker_needed != 0) { - if (ha_locked) { - if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != - QLA_SUCCESS) - return (QLA_FUNCTION_FAILED); - } else { - if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != - QLA_SUCCESS) - return (QLA_FUNCTION_FAILED); - } - ha->marker_needed = 0; - } - return (QLA_SUCCESS); -} - static inline uint8_t * host_to_fcp_swap(uint8_t *fcp, uint32_t bsize) { diff --git a/qla2x00t/qla_iocb.c b/qla2x00t/qla_iocb.c index 2609b26d2..05801ff17 100644 --- a/qla2x00t/qla_iocb.c +++ b/qla2x00t/qla_iocb.c @@ -11,7 +11,7 @@ #include -request_t *qla2x00_req_pkt(scsi_qla_host_t *ha); +#include "qla2x_tgt.h" /** * qla2x00_get_cmd_direction() - Determine control_flag data direction. @@ -407,6 +407,8 @@ queuing_error: * Can be called from both normal and interrupt context. * * Returns non-zero if a failure occured, else zero. + * + * Hardware lock must be held on entrance. Might release it, then reaquire. */ int __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, @@ -453,11 +455,10 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, { int ret; unsigned long flags = 0; - scsi_qla_host_t *pha = to_qla_parent(ha); - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); ret = __qla2x00_marker(ha, loop_id, lun, type); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return (ret); } @@ -467,6 +468,7 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, * @ha: HA context * * Note: The caller must hold the hardware lock before calling this routine. + * Might release it, then reaquire. * * Returns NULL if function failed, else, a pointer to the request packet. */ @@ -565,7 +567,6 @@ qla2x00_isp_cmd(scsi_qla_host_t *ha) WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), ha->req_ring_index); RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); } - } /** @@ -672,7 +673,7 @@ qla24xx_start_scsi(srb_t *sp) { int ret, nseg; unsigned long flags; - scsi_qla_host_t *ha, *pha; + scsi_qla_host_t *ha; struct scsi_cmnd *cmd; uint32_t *clr_ptr; uint32_t index; @@ -686,7 +687,6 @@ qla24xx_start_scsi(srb_t *sp) /* Setup device pointers. */ ret = 0; ha = sp->ha; - pha = to_qla_parent(ha); reg = &ha->iobase->isp24; cmd = sp->cmd; /* So we know we haven't pci_map'ed anything yet */ @@ -701,7 +701,7 @@ qla24xx_start_scsi(srb_t *sp) } /* Acquire ring specific lock */ - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); /* Check for room in outstanding command list. */ handle = ha->current_outstanding_cmd; @@ -796,14 +796,14 @@ qla24xx_start_scsi(srb_t *sp) ha->response_ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(ha); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_SUCCESS; queuing_error: if (tot_dsds) scsi_dma_unmap(cmd); - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return QLA_FUNCTION_FAILED; } diff --git a/qla2x00t/qla_isr.c b/qla2x00t/qla_isr.c index 9856a7078..3eb2e3b9d 100644 --- a/qla2x00t/qla_isr.c +++ b/qla2x00t/qla_isr.c @@ -3,17 +3,18 @@ * Copyright (c) 2003-2008 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. + * */ #include "qla_def.h" +#include "qla2x_tgt.h" + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +struct qla_tgt_initiator qla_target; +#endif #include #include -#ifdef CONFIG_SCSI_QLA2XXX_TARGET -#include "qla2x_tgt.h" -struct qla2x_tgt_initiator qla_target; -#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ - static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t); static void qla2x00_process_completed_request(struct scsi_qla_host *, uint32_t); static void qla2x00_status_entry(scsi_qla_host_t *, void *); @@ -354,8 +355,8 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) #ifdef CONFIG_SCSI_QLA2XXX_TARGET case MBA_CTIO_COMPLETION: - if (qla_target.tgt_ctio_completion) - qla_target.tgt_ctio_completion(ha, handles[0]); + if (qla_target.tgt2x_ctio_completion) + qla_target.tgt2x_ctio_completion(ha, handles[0]); break; #endif @@ -363,10 +364,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", ha->host_no)); set_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_SYSTEM_ERR: /* System Error */ @@ -392,10 +389,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->flags.online = 0; } else set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ @@ -405,10 +398,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ @@ -418,19 +407,14 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) qla2x00_post_hwe_work(ha, mb[0], mb[1], mb[2], mb[3]); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; - case MBA_WAKEUP_THRES: /* Request Queue Wake-up */ - DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n", + case MBA_ATIO_TRANSFER_ERR: /* ATIO Queue Transfer Error */ + DEBUG2(printk("scsi(%ld): ATIO Transfer Error.\n", ha->host_no)); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif + qla_printk(KERN_WARNING, ha, "ATIO Transfer Error.\n"); + + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); break; case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ @@ -454,10 +438,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->flags.management_server_logged_in = 0; qla2x00_post_aen_work(ha, FCH_EVT_LIP, mb[1]); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_LOOP_UP: /* Loop Up Event */ @@ -478,10 +458,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->flags.management_server_logged_in = 0; qla2x00_post_aen_work(ha, FCH_EVT_LINKUP, ha->link_data_rate); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_LOOP_DOWN: /* Loop Down Event */ @@ -505,10 +481,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->flags.management_server_logged_in = 0; ha->link_data_rate = PORT_SPEED_UNKNOWN; qla2x00_post_aen_work(ha, FCH_EVT_LINKDOWN, 0); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_LIP_RESET: /* LIP reset occurred */ @@ -533,10 +505,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->operating_mode = LOOP; ha->flags.management_server_logged_in = 0; qla2x00_post_aen_work(ha, FCH_EVT_LIPRESET, mb[1]); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_POINT_TO_POINT: /* Point-to-Point */ @@ -571,10 +539,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->flags.gpsc_supported = 1; ha->flags.management_server_logged_in = 0; -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_CHG_IN_CONNECTION: /* Change in connection mode */ @@ -602,13 +566,13 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; case MBA_PORT_UPDATE: /* Port database update */ + /* Only handle SCNs for our Vport index. */ + if (ha->parent && ha->vp_idx != (mb[3] & 0xff)) + break; + /* * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET * event etc. earlier indicating loop is down) then process @@ -621,8 +585,15 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) "ignored %04x/%04x/%04x.\n", ha->host_no, mb[1], mb[2], mb[3])); #ifdef CONFIG_SCSI_QLA2XXX_TARGET + DEBUG2(printk("scsi(%ld): ha state %d init_done %d " + "oper_mode %d topo %d\n", + ha->host_no, + atomic_read(&ha->loop_state), + ha->flags.init_done, + ha->operating_mode, + ha->current_topology)); if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); + qla_target.tgt_async_event(mb[0], ha, mb); #endif break; } @@ -646,7 +617,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); + qla_target.tgt_async_event(mb[0], ha, mb); #endif break; @@ -692,10 +663,6 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(RSCN_UPDATE, &ha->dpc_flags); qla2x00_post_aen_work(ha, FCH_EVT_RSCN, rscn_entry); -#ifdef CONFIG_SCSI_QLA2XXX_TARGET - if (qla_target.tgt_async_event) - qla_target.tgt_async_event(mb[0], ha, mb); -#endif break; /* case MBA_RIO_RESPONSE: */ @@ -757,6 +724,40 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) } spin_unlock_irqrestore(&ha->cs84xx->access_lock, flags); break; + + case MBA_LOOP_INIT_ERR: + printk("scsi(%ld): Loop init error received -- " + "%04x %04x %04x.\n", ha->host_no, mb[1], mb[2], + mb[3]); + break; + + default: + printk("scsi(%ld): Unhandled async event %d received -- " + "%04x %04x %04x.\n", ha->host_no, mb[0], mb[1], mb[2], + mb[3]); + break; + } + + switch (mb[0]) { + case MBA_POINT_TO_POINT: /* Point-to-Point */ + case MBA_CHG_IN_CONNECTION: /* Change in connection mode */ + if (IS_QLA2100(ha)) + break; + /* else go through */ + case MBA_RESET: /* Reset */ + case MBA_SYSTEM_ERR: /* System Error */ + case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ + case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ + case MBA_ATIO_TRANSFER_ERR: /* ATIO Queue Transfer Error */ + case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ + case MBA_LOOP_UP: /* Loop Up Event */ + case MBA_LOOP_DOWN: /* Loop Down Event */ + case MBA_LIP_RESET: /* LIP reset occurred */ +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_async_event) + qla_target.tgt_async_event(mb[0], ha, mb); +#endif + break; } if (!ha->parent && ha->num_vhosts) @@ -834,8 +835,8 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index) #ifdef CONFIG_SCSI_QLA2XXX_TARGET if (HANDLE_IS_CTIO_COMP(index)) { - if (qla_target.tgt_ctio_completion) - qla_target.tgt_ctio_completion(ha, index); + if (qla_target.tgt2x_ctio_completion) + qla_target.tgt2x_ctio_completion(ha, index); return; } #endif @@ -846,7 +847,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index) ha->host_no, index)); qla_printk(KERN_WARNING, ha, "Invalid SCSI completion handle %d.\n", index); - + DEBUG2(dump_stack()); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); return; } @@ -869,6 +870,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *ha, uint32_t index) ha->host_no)); qla_printk(KERN_WARNING, ha, "Invalid ISP SCSI completion handle\n"); + DEBUG2(dump_stack()); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); } @@ -892,6 +894,9 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) { pkt = (sts_entry_t *)ha->response_ring_ptr; + DEBUG5(printk("%s(): IOCB data:\n", __func__)); + DEBUG5(qla2x00_dump_buffer((uint8_t *)pkt, RESPONSE_ENTRY_SIZE)); + ha->rsp_ring_index++; if (ha->rsp_ring_index == ha->response_q_length) { ha->rsp_ring_index = 0; @@ -905,29 +910,43 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) "scsi(%ld): Process error entry.\n", ha->host_no)); qla2x00_error_entry(ha, pkt); - ((response_t *)pkt)->signature = RESPONSE_PROCESSED; - wmb(); - continue; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + switch (pkt->entry_type) { + case ACCEPT_TGT_IO_TYPE: + case CONTINUE_TGT_IO_TYPE: + case CTIO_A64_TYPE: + case IMMED_NOTIFY_TYPE: + case NOTIFY_ACK_TYPE: + case ENABLE_LUN_TYPE: + case MODIFY_LUN_TYPE: + break; + default: + ((response_t *)pkt)->signature = RESPONSE_PROCESSED; + wmb(); + continue; + } +#endif } switch (pkt->entry_type) { #ifdef CONFIG_SCSI_QLA2XXX_TARGET - case ACCEPT_TGT_IO_TYPE: - case CONTINUE_TGT_IO_TYPE: - case CTIO_A64_TYPE: - case IMMED_NOTIFY_TYPE: - case NOTIFY_ACK_TYPE: - case ENABLE_LUN_TYPE: - case MODIFY_LUN_TYPE: - DEBUG4(printk(KERN_WARNING + case ACCEPT_TGT_IO_TYPE: + case CONTINUE_TGT_IO_TYPE: + case CTIO_A64_TYPE: + case IMMED_NOTIFY_TYPE: + case NOTIFY_ACK_TYPE: + case ENABLE_LUN_TYPE: + case MODIFY_LUN_TYPE: + DEBUG5(printk(KERN_WARNING "qla2x00_response_pkt: " "calling tgt_response_pkt %p " "(type %02X)\n", qla_target.tgt_response_pkt, pkt->entry_type);); if (qla_target.tgt_response_pkt) - qla_target.tgt_response_pkt(ha, pkt); - break; + qla_target.tgt_response_pkt(ha, + (response_t*)pkt); + break; #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ case STATUS_TYPE: qla2x00_status_entry(ha, pkt); @@ -949,6 +968,8 @@ qla2x00_process_response_queue(struct scsi_qla_host *ha) case STATUS_CONT_TYPE: qla2x00_status_cont_entry(ha, (sts_cont_entry_t *)pkt); break; + case MARKER_TYPE: + break; default: /* Type Not Supported. */ DEBUG4(printk(KERN_WARNING @@ -984,12 +1005,11 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len) sp->request_sense_ptr += sense_len; sp->request_sense_length -= sense_len; if (sp->request_sense_length != 0) - sp->fcport->ha->status_srb = sp; + sp->ha->status_srb = sp; DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) " - "cmd=%p pid=%ld\n", __func__, sp->fcport->ha->host_no, - cp->device->channel, cp->device->id, cp->device->lun, cp, - cp->serial_number)); + "cmd=%p pid=%ld\n", __func__, sp->ha->host_no, cp->device->channel, + cp->device->id, cp->device->lun, cp, cp->serial_number)); if (sense_len) DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, CMD_ACTUAL_SNSLEN(cp))); @@ -1308,8 +1328,9 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) atomic_read(&fcport->state))); cp->result = DID_BUS_BUSY << 16; - if (atomic_read(&fcport->state) == FCS_ONLINE) - qla2x00_mark_device_lost(fcport->ha, fcport, 1, 1); + if (atomic_read(&fcport->state) == FCS_ONLINE) { + qla2x00_mark_device_lost(ha, fcport, 1, 1); + } break; case CS_RESET: @@ -1352,7 +1373,7 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) /* Check to see if logout occurred. */ if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT)) - qla2x00_mark_device_lost(fcport->ha, fcport, 1, 1); + qla2x00_mark_device_lost(ha, fcport, 1, 1); break; default: @@ -1432,9 +1453,12 @@ qla2x00_error_entry(scsi_qla_host_t *ha, sts_entry_t *pkt) srb_t *sp; #if defined(QL_DEBUG_LEVEL_2) - if (pkt->entry_status & RF_INV_E_ORDER) - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__); - else if (pkt->entry_status & RF_INV_E_COUNT) + qla_printk(KERN_ERR, ha, "%s: Error entry with type %x:\n", __func__, + pkt->entry_type); + if (pkt->entry_status & RF_INV_E_ORDER) { + qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order:\n", __func__); + qla2x00_dump_buffer((void*)pkt, sizeof(*pkt)); + } else if (pkt->entry_status & RF_INV_E_COUNT) qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__); else if (pkt->entry_status & RF_INV_E_PARAM) qla_printk(KERN_ERR, ha, @@ -1510,8 +1534,67 @@ qla24xx_mbx_completion(scsi_qla_host_t *ha, uint16_t mb0) DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n", __func__, ha->host_no)); } + +#if defined(QL_DEBUG_LEVEL_1) + printk("scsi(%ld): Mailbox registers:", ha->host_no); + for (cnt = 0; cnt < ha->mbx_count; cnt++) { + if ((cnt % 4) == 0) + printk("\n"); + printk("mbox %02d: 0x%04x ", cnt, ha->mailbox_out[cnt]); + } + printk("\n"); +#endif } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +/** + * qla24xx_process_atio_queue() - Process ATIO queue entries. + * @ha: SCSI driver HA context + */ +static void +qla24xx_process_atio_queue(struct scsi_qla_host *ha) +{ + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + atio_t *pkt; + int cnt, i; + + if (!ha->flags.online) + return; + + while (ha->atio_ring_ptr->signature != ATIO_PROCESSED) { + pkt = ha->atio_ring_ptr; + cnt = pkt->entry_count; + + DEBUG5(printk("%s(): IOCB data:\n", __func__)); + DEBUG5(qla2x00_dump_buffer((uint8_t *)pkt, RESPONSE_ENTRY_SIZE)); + + DEBUG5(printk(KERN_WARNING + "%s: calling tgt24_atio_pkt %p (type %02X, " + "count %d)\n", __func__, qla_target.tgt24_atio_pkt, + pkt->entry_type, cnt);); + + if (likely(qla_target.tgt24_atio_pkt)) + qla_target.tgt24_atio_pkt(ha, (atio7_entry_t*)pkt); + + for(i = 0; i < cnt; i++) { + ha->atio_ring_index++; + if (ha->atio_ring_index == ha->atio_q_length) { + ha->atio_ring_index = 0; + ha->atio_ring_ptr = ha->atio_ring; + } else + ha->atio_ring_ptr++; + + pkt->signature = ATIO_PROCESSED; + pkt = ha->atio_ring_ptr; + } + wmb(); + } + + /* Adjust ring index */ + WRT_REG_DWORD(®->atio_q_out, ha->atio_ring_index); +} +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + /** * qla24xx_process_response_queue() - Process response queue entries. * @ha: SCSI driver HA context @@ -1528,6 +1611,9 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) while (ha->response_ring_ptr->signature != RESPONSE_PROCESSED) { pkt = (struct sts_entry_24xx *)ha->response_ring_ptr; + DEBUG5(printk("%s(): IOCB data:\n", __func__)); + DEBUG5(qla2x00_dump_buffer((uint8_t *)pkt, RESPONSE_ENTRY_SIZE)); + ha->rsp_ring_index++; if (ha->rsp_ring_index == ha->response_q_length) { ha->rsp_ring_index = 0; @@ -1541,9 +1627,19 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) "scsi(%ld): Process error entry.\n", ha->host_no)); qla2x00_error_entry(ha, (sts_entry_t *) pkt); - ((response_t *)pkt)->signature = RESPONSE_PROCESSED; - wmb(); - continue; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + switch (pkt->entry_type) { + case ABTS_RECV_24XX: + case ABTS_RESP_24XX: + case CTIO_TYPE7: + case NOTIFY_ACK_TYPE: + break; + default: + ((response_t *)pkt)->signature = RESPONSE_PROCESSED; + wmb(); + continue; + } +#endif } switch (pkt->entry_type) { @@ -1557,6 +1653,27 @@ qla24xx_process_response_queue(struct scsi_qla_host *ha) qla24xx_report_id_acquisition(ha, (struct vp_rpt_id_entry_24xx *)pkt); break; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + case ABTS_RECV_24XX: + /* ensure that the ATIO queue is empty */ + qla24xx_process_atio_queue(ha); + /* go through */ + case ABTS_RESP_24XX: + case CTIO_TYPE7: + case NOTIFY_ACK_TYPE: + DEBUG5(printk(KERN_WARNING + "qla2x00_response_pkt: " + "calling tgt_response_pkt %p " + "(type %02X)\n", + qla_target.tgt_response_pkt, + pkt->entry_type);); + if (qla_target.tgt_response_pkt) + qla_target.tgt_response_pkt(ha, + (response_t*)pkt); + break; +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + case MARKER_TYPE: + break; default: /* Type Not Supported. */ DEBUG4(printk(KERN_WARNING @@ -1694,6 +1811,15 @@ qla24xx_intr_handler(int irq, void *dev_id) case 0x13: qla24xx_process_response_queue(ha); break; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + case 0x1C: /* ATIO queue updated */ + qla24xx_process_atio_queue(ha); + break; + case 0x1D: /* ATIO and response queues updated */ + qla24xx_process_atio_queue(ha); + qla24xx_process_response_queue(ha); + break; +#endif default: DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " "(%d).\n", @@ -1792,6 +1918,15 @@ qla24xx_msix_default(int irq, void *dev_id) case 0x13: qla24xx_process_response_queue(ha); break; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + case 0x1C: /* ATIO queue updated */ + qla24xx_process_atio_queue(ha); + break; + case 0x1D: /* ATIO and response queues updated */ + qla24xx_process_atio_queue(ha); + qla24xx_process_response_queue(ha); + break; +#endif default: DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " "(%d).\n", @@ -1957,6 +2092,7 @@ clear_risc_ints: WRT_REG_WORD(®->isp.hccr, HCCR_CLR_HOST_INT); } spin_unlock_irq(&ha->hardware_lock); + ha->isp_ops->enable_intrs(ha); fail: return ret; diff --git a/qla2x00t/qla_mbx.c b/qla2x00t/qla_mbx.c index 7b96e77f5..c558c561f 100644 --- a/qla2x00t/qla_mbx.c +++ b/qla2x00t/qla_mbx.c @@ -265,7 +265,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) if (!abort_active) complete(&ha->mbx_cmd_comp); - if (rval) { + if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, " "mbx2=%x, cmd=%x ****\n", __func__, ha->host_no, mcp->mb[0], mcp->mb[1], mcp->mb[2], command)); @@ -918,8 +918,6 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, rval = qla2x00_mailbox_command(ha, mcp); if (mcp->mb[0] == MBS_COMMAND_ERROR) rval = QLA_COMMAND_ERROR; - else if (mcp->mb[0] == MBS_INVALID_COMMAND) - rval = QLA_INVALID_COMMAND; /* Return data. */ *id = mcp->mb[1]; @@ -1024,6 +1022,28 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", ha->host_no)); +#ifdef QL_DEBUG_LEVEL_5 + if (IS_FWI2_CAPABLE(ha)) { + struct init_cb_24xx *icb = (struct init_cb_24xx *)ha->init_cb; + DEBUG5(printk("%s(): firmware_options_1 %x, " + "firmware_options_2 %x, firmware_options_3 %x\n", + __func__, icb->firmware_options_1, + icb->firmware_options_2, icb->firmware_options_3)); + DEBUG5(printk("%s(): Control Block:\n", __func__)); + DEBUG5(qla2x00_dump_buffer((uint8_t *)icb, sizeof(*icb))); + } else { + init_cb_t *icb = (init_cb_t *)ha->init_cb; + DEBUG5(printk("%s(): firmware_options[0] %x, " + "firmware_options[1] %x, add_firmware_options[0] %x, " + "add_firmware_options[1] %x\n", __func__, + icb->firmware_options[0], icb->firmware_options[1], + icb->add_firmware_options[0], + icb->add_firmware_options[1])); + DEBUG5(printk("%s(): Control Block:\n", __func__)); + DEBUG5(qla2x00_dump_buffer((uint8_t *)icb, sizeof(*icb))); + } +#endif + if (ha->flags.npiv_supported) mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE; else @@ -1156,6 +1176,13 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) fcport->port_type = FCT_INITIATOR; else fcport->port_type = FCT_UNKNOWN; + + /* Passback COS information. */ + fcport->supported_classes = (pd24->flags & PDF_CLASS_2) ? + FC_COS_CLASS2: FC_COS_CLASS3; + + if (pd24->prli_svc_param_word_3[0] & BIT_7) + fcport->conf_compl_supported = 1; } else { /* Check for logged in state. */ if (pd->master_state != PD_STATE_PORT_LOGGED_IN && @@ -1189,6 +1216,9 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) /* Passback COS information. */ fcport->supported_classes = (pd->options & BIT_4) ? FC_COS_CLASS2: FC_COS_CLASS3; + + if (pd->prli_svc_param_word_3[0] & BIT_7) + fcport->conf_compl_supported = 1; } gpd_error_out: @@ -1537,6 +1567,8 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, mb[10] |= BIT_0; /* Class 2. */ if (lg->io_parameter[9] || lg->io_parameter[10]) mb[10] |= BIT_1; /* Class 3. */ + if (lg->io_parameter[0] & __constant_cpu_to_le32(BIT_7)) + mb[10] |= BIT_7; /* Confirmed Completion Allowed */ } dma_pool_free(ha->s_dma_pool, lg, lg_dma); @@ -2167,18 +2199,17 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp) struct abort_entry_24xx *abt; dma_addr_t abt_dma; uint32_t handle; - scsi_qla_host_t *pha = to_qla_parent(ha); DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); fcport = sp->fcport; - spin_lock_irqsave(&pha->hardware_lock, flags); + spin_lock_irqsave(&ha->hardware_lock, flags); for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) { - if (pha->outstanding_cmds[handle] == sp) + if (ha->outstanding_cmds[handle] == sp) break; } - spin_unlock_irqrestore(&pha->hardware_lock, flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); if (handle == MAX_OUTSTANDING_COMMANDS) { /* Command not found. */ return QLA_FUNCTION_FAILED; diff --git a/qla2x00t/qla_mid.c b/qla2x00t/qla_mid.c index 893fc51dc..0fd28ac3a 100644 --- a/qla2x00t/qla_mid.c +++ b/qla2x00t/qla_mid.c @@ -6,6 +6,7 @@ */ #include "qla_def.h" +#include #include #include #include @@ -42,7 +43,6 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha) set_bit(vp_id, ha->vp_idx_map); ha->num_vhosts++; - ha->cur_vport_count++; vha->vp_idx = vp_id; list_add_tail(&vha->vp_list, &ha->vp_list); mutex_unlock(&ha->vport_lock); @@ -58,7 +58,6 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) mutex_lock(&ha->vport_lock); vp_id = vha->vp_idx; ha->num_vhosts--; - ha->cur_vport_count--; clear_bit(vp_id, ha->vp_idx_map); list_del(&vha->vp_list); mutex_unlock(&ha->vport_lock); @@ -104,8 +103,8 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) "loop_id=0x%04x :%x\n", vha->host_no, fcport->loop_id, fcport->vp_idx)); + atomic_set(&fcport->state, FCS_DEVICE_DEAD); qla2x00_mark_device_lost(vha, fcport, 0, 0); - atomic_set(&fcport->state, FCS_UNCONFIGURED); } } @@ -277,8 +276,7 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha) clear_bit(RESET_ACTIVE, &vha->dpc_flags); } - if (atomic_read(&vha->vp_state) == VP_ACTIVE && - test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { + if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) { qla2x00_loop_resync(vha); clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags); @@ -392,6 +390,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->parent = ha; vha->fc_vport = fc_vport; vha->device_flags = 0; + vha->instance = num_hosts; vha->vp_idx = qla24xx_allocate_vp_id(vha); if (vha->vp_idx > ha->max_npiv_vports) { DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", @@ -429,7 +428,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) host->max_cmd_len = MAX_CMDSZ; host->max_channel = MAX_BUSES - 1; host->max_lun = MAX_LUNS; - host->unique_id = host->host_no; + host->unique_id = vha->instance; host->max_id = MAX_TARGETS_2200; host->transportt = qla2xxx_transport_vport_template; @@ -437,6 +436,12 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->host_no, vha)); vha->flags.init_done = 1; + num_hosts++; + + mutex_lock(&ha->vport_lock); + set_bit(vha->vp_idx, ha->vp_idx_map); + ha->cur_vport_count++; + mutex_unlock(&ha->vport_lock); return vha; diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c index 81c57b516..b008a6744 100644 --- a/qla2x00t/qla_os.c +++ b/qla2x00t/qla_os.c @@ -18,9 +18,7 @@ #include #include -#if defined(CONFIG_SCSI_QLA2XXX_TARGET) #include "qla2x_tgt.h" -#endif /* * Driver version @@ -32,6 +30,7 @@ char qla2x00_version_str[40]; */ static struct kmem_cache *srb_cachep; +int num_hosts; int ql2xlogintimeout = 20; module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xlogintimeout, @@ -55,12 +54,18 @@ module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xloginretrycount, "Specify an alternate value for the NVRAM login retry count."); -int ql2xallocfwdump = 1; +int ql2xenableclass2 = 0; +module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xenableclass2, + "Specify if Class 2 operations are supported from the very " + "beginning."); + +int ql2xallocfwdump = 0; module_param(ql2xallocfwdump, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xallocfwdump, "Option to enable allocation of memory for a firmware dump " "during HBA initialization. Memory allocation requirements " - "vary by ISP type. Default is 1 - allocate memory."); + "vary by ISP type. Default is 0 - don't allocate memory."); int ql2xextended_error_logging; module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR); @@ -91,13 +96,6 @@ MODULE_PARM_DESC(ql2xqfullrampup, "depth for a device after a queue-full condition has been " "detected. Default is 120 seconds."); -int ql2xiidmaenable=1; -module_param(ql2xiidmaenable, int, S_IRUGO|S_IRUSR); -MODULE_PARM_DESC(ql2xiidmaenable, - "Enables iIDMA settings " - "Default is 1 - perform iIDMA. 0 - no iIDMA."); - - /* * SCSI host template entry points */ @@ -149,6 +147,10 @@ static struct scsi_host_template qla2x00_driver_template = { */ .max_sectors = 0xFFFF, .shost_attrs = qla2x00_host_attrs, + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + .supported_mode = MODE_INITIATOR | MODE_TARGET, +#endif }; struct scsi_host_template qla24xx_driver_template = { @@ -177,6 +179,10 @@ struct scsi_host_template qla24xx_driver_template = { .max_sectors = 0xFFFF, .shost_attrs = qla2x00_host_attrs, + +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + .supported_mode = MODE_INITIATOR | MODE_TARGET, +#endif }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; @@ -399,7 +405,7 @@ qla2x00_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) } /* Close window on fcport/rport state-transitioning. */ - if (fcport->drport) { + if (!*(fc_port_t **)rport->dd_data) { cmd->result = DID_IMM_RETRY << 16; goto qc_fail_command; } @@ -454,7 +460,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) int rval; scsi_qla_host_t *pha = to_qla_parent(ha); - if (unlikely(pci_channel_offline(pha->pdev))) { + if (unlikely(pci_channel_offline(ha->pdev))) { cmd->result = DID_REQUEUE << 16; goto qc24_fail_command; } @@ -466,7 +472,7 @@ qla24xx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) } /* Close window on fcport/rport state-transitioning. */ - if (fcport->drport) { + if (!*(fc_port_t **)rport->dd_data) { cmd->result = DID_IMM_RETRY << 16; goto qc24_fail_command; } @@ -602,7 +608,7 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha) * Success (LOOP_READY) : 0 * Failed (LOOP_NOT_READY) : 1 */ -static inline int +int qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) { int return_status = QLA_SUCCESS; @@ -628,40 +634,6 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) return (return_status); } -void -qla2x00_abort_fcport_cmds(fc_port_t *fcport) -{ - int cnt; - unsigned long flags; - srb_t *sp; - scsi_qla_host_t *ha = fcport->ha; - scsi_qla_host_t *pha = to_qla_parent(ha); - - spin_lock_irqsave(&pha->hardware_lock, flags); - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { - sp = pha->outstanding_cmds[cnt]; - if (!sp) - continue; - if (sp->fcport != fcport) - continue; - - spin_unlock_irqrestore(&pha->hardware_lock, flags); - if (ha->isp_ops->abort_command(ha, sp)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Abort failed -- %lx\n", sp->cmd->serial_number)); - } else { - if (qla2x00_eh_wait_on_command(ha, sp->cmd) != - QLA_SUCCESS) - DEBUG2(qla_printk(KERN_WARNING, ha, - "Abort failed while waiting -- %lx\n", - sp->cmd->serial_number)); - - } - spin_lock_irqsave(&pha->hardware_lock, flags); - } - spin_unlock_irqrestore(&pha->hardware_lock, flags); -} - static void qla2x00_block_error_handler(struct scsi_cmnd *cmnd) { @@ -769,7 +741,7 @@ enum nexus_wait_type { WAIT_LUN, }; -static int +int qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t, unsigned int l, enum nexus_wait_type type) { @@ -785,8 +757,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t, sp = pha->outstanding_cmds[cnt]; if (!sp) continue; - - if (ha->vp_idx != sp->fcport->ha->vp_idx) + if (ha->vp_idx != sp->ha->vp_idx) continue; match = 0; switch (type) { @@ -1086,7 +1057,9 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *ha, int res) sp = ha->outstanding_cmds[cnt]; if (sp) { ha->outstanding_cmds[cnt] = NULL; + sp->flags = 0; sp->cmd->result = res; + sp->cmd->host_scribble = (unsigned char *)NULL; qla2x00_sp_compl(ha, sp); } } @@ -1117,7 +1090,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev) else scsi_deactivate_tcq(sdev, ha->max_q_depth); - rport->dev_loss_tmo = ha->port_down_retry_count; + rport->dev_loss_tmo = ha->port_down_retry_count + 5; return 0; } @@ -1592,13 +1565,19 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha = shost_priv(host); memset(ha, 0, sizeof(scsi_qla_host_t)); + ha->enable_class_2 = ql2xenableclass2; ha->pdev = pdev; ha->host = host; ha->host_no = host->host_no; - sprintf(ha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, ha->host_no); + snprintf(ha->host_str, HA_HOST_STR_SIZE, "%s_%ld", QLA2XXX_DRIVER_NAME, + ha->host_no); ha->parent = NULL; ha->bars = bars; ha->mem_only = mem_only; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + mutex_init(&ha->tgt_mutex); + qla_clear_tgt_mode(ha); +#endif spin_lock_init(&ha->hardware_lock); /* Set ISP-type information. */ @@ -1656,6 +1635,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->mbx_count = MAILBOX_REGISTER_COUNT; ha->request_q_length = REQUEST_ENTRY_CNT_24XX; ha->response_q_length = RESPONSE_ENTRY_CNT_2300; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->atio_q_length = ATIO_ENTRY_CNT_24XX; +#endif ha->last_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); ha->mgmt_svr_loop_id = 10 + ha->vp_idx; @@ -1667,6 +1649,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->mbx_count = MAILBOX_REGISTER_COUNT; ha->request_q_length = REQUEST_ENTRY_CNT_24XX; ha->response_q_length = RESPONSE_ENTRY_CNT_2300; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->atio_q_length = ATIO_ENTRY_CNT_24XX; +#endif ha->last_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_24xx); ha->mgmt_svr_loop_id = 10 + ha->vp_idx; @@ -1678,6 +1663,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) } host->can_queue = ha->request_q_length + 128; + /* load the F/W, read paramaters, and init the H/W */ + ha->instance = num_hosts; + spin_lock_init(&ha->dpc_lock); mutex_init(&ha->vport_lock); @@ -1727,7 +1715,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host->this_id = 255; host->cmd_per_lun = 3; - host->unique_id = host->host_no; + host->unique_id = ha->instance; host->max_cmd_len = MAX_CMDSZ; host->max_channel = MAX_BUSES - 1; host->max_lun = MAX_LUNS; @@ -1748,12 +1736,12 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flags.init_done = 1; ha->flags.online = 1; + num_hosts++; + ret = scsi_add_host(host, &pdev->dev); if (ret) goto probe_failed; - ha->isp_ops->enable_intrs(ha); - scsi_scan_host(host); qla2x00_alloc_sysfs_attr(ha); @@ -1786,24 +1774,47 @@ probe_out: return ret; } +static void +qla2x00_stop_dpc_thread(scsi_qla_host_t *ha) +{ + struct task_struct *t = NULL; + + spin_lock_irq(&ha->dpc_lock); + if (ha->dpc_thread != NULL) { + t = ha->dpc_thread; + /* + * qla2xxx_wake_dpc checks for ->dpc_thread + * so we need to zero it out. + */ + ha->dpc_thread = NULL; + } + spin_unlock_irq(&ha->dpc_lock); + + if (t != NULL) + kthread_stop(t); +} + static void qla2x00_remove_one(struct pci_dev *pdev) { - scsi_qla_host_t *ha, *vha, *temp; + scsi_qla_host_t *ha; ha = pci_get_drvdata(pdev); - list_for_each_entry_safe(vha, temp, &ha->vp_list, vp_list) - fc_vport_terminate(vha->fc_vport); - - set_bit(UNLOADING, &ha->dpc_flags); - -#if defined(CONFIG_SCSI_QLA2XXX_TARGET) - ha->flags.host_shutting_down = 1; - if (qla_target.tgt_host_action != NULL) +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + { + unsigned long flags; + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->host_shutting_down = 1; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } + if (qla_target.tgt_host_action != NULL) qla_target.tgt_host_action(ha, DISABLE_TARGET_MODE); #endif + /* Necessary to prevent races with it */ + qla2x00_stop_dpc_thread(ha); + qla2x00_dfs_remove(ha); qla84xx_put_chip(ha); @@ -1822,26 +1833,6 @@ qla2x00_remove_one(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } -static void -qla2x00_stop_dpc_thread(scsi_qla_host_t *ha) -{ - struct task_struct *t = NULL; - - spin_lock_irq(&ha->dpc_lock); - if (ha->dpc_thread != NULL) { - t = ha->dpc_thread; - /* - * qla2xxx_wake_dpc checks for ->dpc_thread - * so we need to zero it out. - */ - ha->dpc_thread = NULL; - } - spin_unlock_irq(&ha->dpc_lock); - - if (t != NULL) - kthread_stop(t); -} - static void qla2x00_free_device(scsi_qla_host_t *ha) { @@ -1883,22 +1874,31 @@ static inline void qla2x00_schedule_rport_del(struct scsi_qla_host *ha, fc_port_t *fcport, int defer) { + unsigned long flags; struct fc_rport *rport; - scsi_qla_host_t *pha = to_qla_parent(ha); if (!fcport->rport) return; rport = fcport->rport; if (defer) { - unsigned long flags; - spin_lock_irqsave(ha->host->host_lock, flags); + spin_lock_irqsave(&fcport->rport_lock, flags); fcport->drport = rport; - spin_unlock_irqrestore(ha->host->host_lock, flags); - set_bit(FCPORT_UPDATE_NEEDED, &pha->dpc_flags); - qla2xxx_wake_dpc(pha); - } else + fcport->rport = NULL; + *(fc_port_t **)rport->dd_data = NULL; + spin_unlock_irqrestore(&fcport->rport_lock, flags); + set_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags); + } else { + spin_lock_irqsave(&fcport->rport_lock, flags); + fcport->rport = NULL; + *(fc_port_t **)rport->dd_data = NULL; + spin_unlock_irqrestore(&fcport->rport_lock, flags); fc_remote_port_delete(rport); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_fc_port_deleted) + qla_target.tgt_fc_port_deleted(ha, fcport); +#endif + } } /* @@ -1968,18 +1968,27 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *ha, int defer) scsi_qla_host_t *pha = to_qla_parent(ha); list_for_each_entry_rcu(fcport, &pha->fcports, list) { - if (ha->vp_idx != fcport->vp_idx) +#ifndef CONFIG_SCSI_QLA2XXX_TARGET + if (ha->vp_idx != 0 && ha->vp_idx != fcport->vp_idx) continue; +#endif /* * No point in marking the device as lost, if the device is * already DEAD. */ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD) continue; - if (atomic_read(&fcport->state) == FCS_ONLINE) - qla2x00_schedule_rport_del(ha, fcport, defer); + if (atomic_read(&fcport->state) == FCS_ONLINE) { + if (defer) + qla2x00_schedule_rport_del(ha, fcport, defer); + else if (ha->vp_idx == fcport->vp_idx) + qla2x00_schedule_rport_del(ha, fcport, defer); + } atomic_set(&fcport->state, FCS_DEVICE_LOST); } + + if (defer) + qla2xxx_wake_dpc(ha); } /* @@ -2007,10 +2016,20 @@ qla2x00_mem_alloc(scsi_qla_host_t *ha) if (!ha->response_ring) goto fail_free_request_ring; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (IS_FWI2_CAPABLE(ha)) { + ha->atio_ring = dma_alloc_coherent(&ha->pdev->dev, + (ha->atio_q_length + 1) * sizeof(atio_t), + &ha->atio_dma, GFP_KERNEL); + if (ha->atio_ring == NULL) + goto fail_free_response_ring; + } +#endif + ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, &ha->gid_list_dma, GFP_KERNEL); if (!ha->gid_list) - goto fail_free_response_ring; + goto fail_free_atio_ring; ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size, &ha->init_cb_dma, GFP_KERNEL); @@ -2079,7 +2098,14 @@ fail_free_gid_list: ha->gid_list_dma); ha->gid_list = NULL; ha->gid_list_dma = 0; +fail_free_atio_ring: +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + dma_free_coherent(&ha->pdev->dev, (ha->atio_q_length + 1) * + sizeof(response_t), ha->atio_ring, ha->atio_dma); + ha->atio_ring = NULL; + ha->atio_dma = 0; fail_free_response_ring: +#endif dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) * sizeof(response_t), ha->response_ring, ha->response_dma); ha->response_ring = NULL; @@ -2145,6 +2171,13 @@ qla2x00_mem_free(scsi_qla_host_t *ha) dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, ha->gid_list_dma); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (ha->atio_ring) + dma_free_coherent(&ha->pdev->dev, + (ha->atio_q_length + 1) * sizeof(atio_t), + ha->atio_ring, ha->atio_dma); +#endif + if (ha->response_ring) dma_free_coherent(&ha->pdev->dev, (ha->response_q_length + 1) * sizeof(response_t), @@ -2176,6 +2209,10 @@ qla2x00_mem_free(scsi_qla_host_t *ha) ha->response_dma = 0; ha->request_ring = NULL; ha->request_dma = 0; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + ha->atio_ring = NULL; + ha->atio_dma = 0; +#endif list_for_each_safe(fcpl, fcptemp, &ha->fcports) { fcport = list_entry(fcpl, fc_port_t, list); @@ -2214,7 +2251,7 @@ qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type, static int qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked) { - unsigned long uninitialized_var(flags); + unsigned long flags = 0; scsi_qla_host_t *pha = to_qla_parent(ha); if (!locked) @@ -2314,7 +2351,7 @@ qla2x00_do_dpc(void *data) ha = (scsi_qla_host_t *)data; - set_user_nice(current, -20); + set_user_nice(current, -10); while (!kthread_should_stop()) { DEBUG3(printk("qla2x00: DPC handler sleeping\n")); @@ -2371,10 +2408,8 @@ qla2x00_do_dpc(void *data) ha->host_no)); } - if (test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) { + if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags)) qla2x00_update_fcports(ha); - clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags); - } if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) && (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) { @@ -2487,13 +2522,10 @@ qla2x00_do_dpc(void *data) void qla2xxx_wake_dpc(scsi_qla_host_t *ha) { - struct task_struct *t; unsigned long flags; - spin_lock_irqsave(&ha->dpc_lock, flags); - t = ha->dpc_thread; - if (!test_bit(UNLOADING, &ha->dpc_flags) && t) - wake_up_process(t); + if (ha->dpc_thread) + wake_up_process(ha->dpc_thread); spin_unlock_irqrestore(&ha->dpc_lock, flags); } @@ -2575,11 +2607,11 @@ qla2x00_timer(scsi_qla_host_t *ha) */ t = 0; list_for_each_entry_rcu(fcport, &ha->fcports, list) { +#ifndef CONFIG_SCSI_QLA2XXX_TARGET if (fcport->port_type != FCT_TARGET) continue; - +#endif if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) { - if (atomic_read(&fcport->port_down_timer) == 0) continue; @@ -2594,7 +2626,6 @@ qla2x00_timer(scsi_qla_host_t *ha) t++; } /* End of for fcport */ - /* Loop down handler. */ if (atomic_read(&ha->loop_down_timer) > 0 && !(test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) && ha->flags.online) { @@ -2905,6 +2936,25 @@ qla2x00_module_init(void) { int ret = 0; +#if defined(QL_DEBUG_LEVEL_1) || \ + defined(QL_DEBUG_LEVEL_2) || \ + defined(QL_DEBUG_LEVEL_3) || \ + defined(QL_DEBUG_LEVEL_4) || \ + defined(QL_DEBUG_LEVEL_5) || \ + defined(QL_DEBUG_LEVEL_6) || \ + defined(QL_DEBUG_LEVEL_7) || \ + defined(QL_DEBUG_LEVEL_8) || \ + defined(QL_DEBUG_LEVEL_9) || \ + defined(QL_DEBUG_LEVEL_10) || \ + defined(QL_DEBUG_LEVEL_11) || \ + defined(QL_DEBUG_LEVEL_12) || \ + defined(QL_DEBUG_LEVEL_13) || \ + defined(QL_DEBUG_LEVEL_14) || \ + defined(QL_DEBUG_LEVEL_15) || \ + defined(QL_DEBUG_LEVEL_16) + ql2xextended_error_logging = 1; +#endif + /* Allocate cache for SRBs. */ srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0, SLAB_HWCACHE_ALIGN, NULL); @@ -2961,8 +3011,8 @@ module_init(qla2x00_module_init); module_exit(qla2x00_module_exit); MODULE_AUTHOR("QLogic Corporation"); -#if defined(CONFIG_SCSI_QLA2XXX_TARGET) -MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver (Target Support)"); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver (Target Mode Support, including 24xx+ ISP)"); #else MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver"); #endif diff --git a/qla2x00t/qla_sup.c b/qla2x00t/qla_sup.c index 1bca74474..1728ab3cc 100644 --- a/qla2x00t/qla_sup.c +++ b/qla2x00t/qla_sup.c @@ -869,9 +869,11 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, uint32_t i; uint32_t *dwptr; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + unsigned long flags; ret = QLA_SUCCESS; + spin_lock_irqsave(&ha->hardware_lock, flags); /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); @@ -905,6 +907,7 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ + spin_unlock_irqrestore(&ha->hardware_lock, flags); return ret; } @@ -2302,51 +2305,6 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) return ret; } -static int -qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end) -{ - if (pos >= end || *pos != 0x82) - return 0; - - pos += 3 + pos[1]; - if (pos >= end || *pos != 0x90) - return 0; - - pos += 3 + pos[1]; - if (pos >= end || *pos != 0x78) - return 0; - - return 1; -} - -int -qla2xxx_get_vpd_field(scsi_qla_host_t *ha, char *key, char *str, size_t size) -{ - uint8_t *pos = ha->vpd; - uint8_t *end = pos + ha->vpd_size; - int len = 0; - - if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end)) - return 0; - - while (pos < end && *pos != 0x78) { - len = (*pos == 0x82) ? pos[1] : pos[2]; - - if (!strncmp(pos, key, strlen(key))) - break; - - if (*pos != 0x90 && *pos != 0x91) - pos += len; - - pos += 3; - } - - if (pos < end - len && *pos != 0x78) - return snprintf(str, size, "%.*s", len, pos + 3); - - return 0; -} - static int qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata) { diff --git a/qla2x00t/qla_version.h b/qla2x00t/qla_version.h index 4160e4caa..d058c8862 100644 --- a/qla2x00t/qla_version.h +++ b/qla2x00t/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.01-k7" +#define QLA2XXX_VERSION "8.02.01-k4" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 diff --git a/www/handler_fileio_tgt.html b/www/handler_fileio_tgt.html index e5b746d7b..8dcc16ca8 100644 --- a/www/handler_fileio_tgt.html +++ b/www/handler_fileio_tgt.html @@ -35,7 +35,7 @@

Target Drivers

  • iSCSI-SCST
  • -
  • QLogic FC 22xx/23xx
  • +
  • QLogic FC QLA2X00T
  • QLogic FC ISP
  • SCSI RDMA Protocol (SRP)
  • Marvell SAS adapters
  • diff --git a/www/index.html b/www/index.html index 2bdbf0ac1..f1706d188 100644 --- a/www/index.html +++ b/www/index.html @@ -59,8 +59,8 @@ SCST core performs all required pre- and post- processing of incoming requests as well as necessary error recovery.
  • SCST core undertakes most problems, related to execution contexts, thus practically eliminating one of the most - complicated problem in the kernel drivers development. For example, target drivers for QLogic - 22xx/23xx adapters and for InfiniBand SRP are only about 2300 lines of code long.
  • + complicated problem in the kernel drivers development. For example, target drivers for Marvell + SAS adapters or for InfiniBand SRP are less 3000 lines of code long.
  • Very low overhead, fine-grained locks and simplest commands processing path allow to reach maximum possible performance and scalability. Particularly, incoming requests can be processed in the caller's context or in one of the internal SCST core's tasklets without any diff --git a/www/scst_admin.html b/www/scst_admin.html index 7c6187c82..1115ffaed 100644 --- a/www/scst_admin.html +++ b/www/scst_admin.html @@ -35,7 +35,7 @@

    Target Drivers

    • iSCSI-SCST
    • -
    • QLogic FC 22xx/23xx
    • +
    • QLogic FC QLA2X00T
    • QLogic FC ISP
    • SCSI RDMA Protocol (SRP)
    • Marvell SAS adapters
    • diff --git a/www/scst_pg.html b/www/scst_pg.html index ded28c9f1..3998bb646 100644 --- a/www/scst_pg.html +++ b/www/scst_pg.html @@ -1,1128 +1,2208 @@ - - - - - - - - -SCST: Generic SCSI Target Subsystem for Linux - - - - -
      - - -
      -
      -

      Generic SCSI Target Subsystem for Linux

      -

      Vladislav Bolkhovitin

      -

      Version 0.9.3-pre4 2006/02/07, actual for SCST 0.9.3-pre4 and later

      -

      This document describes SCSI target mid-level for Linux (SCST), its architecture and drivers from - the driver writer's point of view.

      - -

      1. Introduction

      -

      SCST is a SCSI target mid-level subsystem for Linux. It is designed to provide unified, consistent interface - between SCSI target drivers and Linux kernel and simplify target drivers development as much as possible.
      - It has the following features: -

        -
      • Very low overhead, fine-grained locks and simplest commands processing path, which allow to reach - maximum possible performance and scalability that close to theoretical limit.
      • -
      • Incoming requests can be processed in the caller's context or in one of the internal SCST's tasklets, - therefore no extra context switches required.
      • -
      • Complete SMP support.
      • -
      • Undertakes most problems, related to execution contexts, thus practically eliminating one of the most - complicated problem in the kernel drivers development. For example, a target driver for QLogic 22xx/23xx adapters, - which has all necessary features, is about 2000 lines of code long, that is at least in several times less, than - the initiator one.
      • -
      • Performs all required pre- and post- processing of incoming requests and all necessary error recovery - functionality.
      • -
      • Emulates necessary functionality of SCSI host adapter, because from a remote initiator's point of view - SCST acts as a SCSI host with its own devices.
        Some of the emulated functions are the following: -
          -
        • Generation of necessary UNIT ATTENTIONs, their storage and delivery to all connected remote - initiators (sessions).
        • -
        • RESERVE/RELEASE functionality.
        • -
        • CA/ACA conditions.
        • -
        • All types of RESETs and other task management functions.
        • -
        • REPORT LUNS command as well as SCSI address space management in order to have consistent address - space on all remote initiators, since local SCSI devices could not know about each other to report via - REPORT LUNS command. Additionally, SCST responds with error on all commands to non-existing devices and - provides access control (not implemented yet), so different remote initiators could see different set of - devices.
        • -
        • Other necessary functionality (task attributes, etc.) as specified in SAM-2, SPC-2, - SAM-3, SPC-3 and other SCSI standards.
        • -
      • -
      • Device handlers architecture provides extra reliability and security via verifying all incoming - requests and allows to make any additional requests processing, which is completely independent from target - drivers, for example, data caching or device dependent exceptional conditions treatment.
      • -

      -

      Interoperability between SCST and local SCSI initiators (like sd, st) is the additional issue that SCST is going - to address (it is not implemented yet). It is necessary, because local SCSI initiators can change the state of the - device, for example RESERVE the device, or some of its parameters and that would be done behind SCST, which could - lead to various problems. Thus, RESERVE/RELEASE commands, locally generated UNIT ATTENTIONs, etc. should be - intercepted and processed as if local SCSI initiators act as remote SCSI initiators connected to SCST. - This feature requires some the kernel modification. Since in the current version it is not implemented, SCST and - the target drivers are able to work with any unpatched 2.4 kernel version.

      - -

      Interface between SCST and the target drivers is based on work, done by University of New Hampshire - Interoperability Labs (UNH IOL).

      - -

      All described below data structures and function could be found in scsi_tgt.h.
      - The SCST's Internet page is http://scst.sourceforge.net.

      - -

      2. Terms and Definitions

      -

      SCSI initiator device

      - A SCSI device that originates service and task management requests to be processed by a SCSI target device and - receives device service and task management responses from SCSI target devices.

      + + + + + Generic SCSI Target Middle Level for Linux + + +

      Generic SCSI Target Middle Level for Linux

      -

      Think of the 'SCSI LLDD' as a BE (Back End) driver.

      +

      Vladislav Bolkhovitin

      Version 0.9.5 2006/12/01, actual for SCST 0.9.5 and later +
      +This document describes SCSI target mid-level for Linux (SCST), its +architecture and drivers from the driver writer's point of view. +
      +

      1. Introduction

      -

      SCSI target device

      - A SCSI device that receives device service and task management requests for processing and sends device service - and task management responses to SCSI initiator devices or drivers.

      +

      SCST is a SCSI target mid-level subsystem for Linux. It is designed to +provide unified, consistent interface between SCSI target drivers and +Linux kernel and simplify target drivers development as much as +possible. It has the following features:

      +

      +

        +
      • Very low overhead, fine-grained locks and simplest commands +processing path, which allow to reach maximum possible performance and +scalability that close to theoretical limit. +
      • +
      • Incoming requests can be processed in the caller's context or in +one of the internal SCST's tasklets, therefore no extra context switches +required. +
      • +
      • Complete SMP support. +
      • +
      • Undertakes most problems, related to execution contexts, thus +practically eliminating one of the most complicated problem in the +kernel drivers development. For example, target drivers for Marvell SAS +adapters or for InfiniBand SRP are less 3000 lines of code long. +
      • +
      • Performs all required pre- and post- processing of incoming +requests and all necessary error recovery functionality. +
      • +
      • Emulates necessary functionality of SCSI host adapter, because +from a remote initiator's point of view SCST acts as a SCSI host with +its own devices. Some of the emulated functions are the following: -

        Think of the 'Target Driver' as an FE (Front End) driver.

        +
          +
        • Generation of necessary UNIT ATTENTIONs, their storage and +delivery to all connected remote initiators (sessions). +
        • +
        • RESERVE/RELEASE functionality. +
        • +
        • CA/ACA conditions. +
        • +
        • All types of RESETs and other task management functions. +
        • +
        • REPORT LUNS command as well as SCSI address space management +in order to have consistent address space on all remote initiators, +since local SCSI devices could not know about each other to report +via REPORT LUNS command. Additionally, SCST responds with error on +all commands to non-existing devices and provides access control +(not implemented yet), so different remote initiators could see +different set of devices. +
        • +
        • Other necessary functionality (task attributes, etc.) as +specified in SAM-2, SPC-2, SAM-3, SPC-3 and other SCSI standards. +
        • +
        -

        The FE driver interfaces to the initiators (via the - storage-fabric-cloud) and also to the upper edge of the SCST. Whereas - the BE driver interfaces to the targets, i.e. disk-enclosures/JBODs/tapes etc. - and also to the bottom edge of the SCST.

        +
      • +
      • Device handlers architecture provides extra reliability and +security via verifying all incoming requests and allows to make any +additional requests processing, which is completely independent from +target drivers, for example, data caching or device dependent +exceptional conditions treatment. +
      • +
      +

      +

      Interoperability between SCST and local SCSI initiators (like sd, st) is +the additional issue that SCST is going to address (it is not +implemented yet). It is necessary, because local SCSI initiators can +change the state of the device, for example RESERVE the device, or some +of its parameters and that would be done behind SCST, which could lead +to various problems. Thus, RESERVE/RELEASE commands, locally generated +UNIT ATTENTIONs, etc. should be intercepted and processed as if local +SCSI initiators act as remote SCSI initiators connected to SCST. This +feature requires some the kernel modification. Since in the current +version it is not implemented, SCST and the target drivers are able to +work with any unpatched 2.4 kernel version.

      +

      Interface between SCST and the target drivers is based on work, done by +University of New Hampshire Interoperability Labs (UNH IOL).

      +

      All described below data structures and function could be found in +scst.h. The SCST's Internet page is +http://scst.sourceforge.net.

      -

      SCST session

      - SCST session is the object that describes relationship between a remote initiator and SCST via a target driver. - All the commands from the remote initiator is passed to SCST in the session. For example, for connection oriented - protocols, like iSCSI, SCST session could be mapped to the TCP connection. SCST session is the close equivalent of - I_T nexus object.

      -

      Local SCSI initiator

      - A SCSI initiator that is located on the same host as SCST subsystem. Examples are sg and st drivers.

      -

      Remote SCSI initiator

      - A SCSI initiator that is located on the remote host for SCST subsystem and makes client connections to SCST via - SCSI target drivers.

      -

      Device handler driver

      - Also known as "device type specific driver" or "dev handler", is plugin for SCST, which helps SCST - to analyze incoming requests and determine parameters, specific to various types of devices as well as perform - some processing. See appropriate section for details.

      - -

      3. SCST Architecture

      -

      SCST accepts commands and passes them to SCSI mid-level at the same way as SCSI high-level drivers (sg, sd, st) - do. Figure 1 shows interaction between SCST, its drivers and Linux SCSI subsystem.
      -

      -

      Interaction between SCST, its drivers and Linux SCSI subsystem.

      - -

      4. Target driver registration

      -

      To work with SCST a target driver must register its template in SCST by calling scst_register_target_template(). - The template lets SCST know the target driver's entry points. It is defined as the following:

      -

      4.1 Structure scst_tgt_template

      -

      struct scst_tgt_template
      - {
      - int sg_tablesize;
      - const char name[15];
      -
      - unsigned unchecked_isa_dma:1;
      - unsigned use_clustering:1;
      -
      - unsigned xmit_response_atomic:1;
      - unsigned rdy_to_xfer_atomic:1;
      - unsigned report_aen_atomic:1;
      -
      - int (* detect) (struct scst_tgt_template *tgt_template);
      - int (* release)(struct scst_tgt *tgt);
      -
      - int (* xmit_response)(struct scst_cmd *cmd);
      - int (* rdy_to_xfer)(struct scst_cmd *cmd);
      -
      - void (*on_free_cmd) (struct scst_cmd *cmd);
      -
      - void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd);
      - void (* report_aen)(int mgmt_fn, const uint8_t *lun, int lun_len);
      -
      - int (*proc_info) (char *buffer, char **start, off_t offset,
      - int length, int *eof, struct scst_tgt *tgt, int inout);
      - }
      -
      - Where: -

        -
      • sg_tablesize - allows checking whether scatter/gather can be used or not and, if yes, sets the - maximum supported count of scatter/gather entries
      • -
      • name - the name of the template. Must be unique to identify the template. Must be - defined.
      • -
      • unchecked_isa_dma - true, if this target adapter uses unchecked DMA onto an ISA bus. -
      • -
      • use_clustering - true, if this target adapter wants to use clustering (i.e. smaller - number of segments).
      • -
      • xmit_response_atomic, rdy_to_xfer_atomic - true, if the corresponding function supports execution in the atomic (non-sleeping) context.
      • -
      • int (* detect) (struct scst_tgt_template *tgt_template) - this function is intended - to detect the target adapters that are present in the system. Each found adapter should be registered by calling - scst_register(). The function should return a value >= 0 to signify the number of detected - target adapters. A negative value should be returned whenever there is an error. Must be defined.
      • -
      • int (* release)(struct scst_tgt *tgt) - this function is intended to free up the resources allocated to the device. The function should return 0 to indicate successful release or a negative value if there are some issues with the release. In the current version of SCST the return value is ignored. Must be defined.
      • -
      • int (* xmit_response)(struct scst_cmd *cmd) - this function is equivalent to the SCSI queuecommand(). The target should transmit the response data and the status in the struct scst_cmd. See below for details. Must be defined.
      • -
      • int (* rdy_to_xfer)(struct scst_cmd *cmd) - this function informs the driver that - data buffer corresponding to the said command have now been allocated and it is OK to receive data for this - command. This function is necessary because a SCSI target does not have any control over the commands it - receives. Most lower-level protocols have a corresponding function which informs the initiator that buffers have - been allocated e.g., XFER_RDY in Fibre Channel. After the data is actually received the low-level driver should - call scst_rx_data() in order to continue processing this command. Returns one of the - SCST_TGT_RES_* constants, described below. Pay attention to "atomic" attribute of the command, - which can be get via scst_cmd_atomic(): it is true if the function called in the atomic - (non-sleeping) context. Must be defined.
      • -
      • void (*on_free_cmd)(struct scst_cmd *cmd) - this function called to notify the driver - that the command is about to be freed. Necessary, because for aborted commands xmit_response() - could not be called. Could be used on IRQ context. Must be defined.
      • -
      • void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd) - this function informs - the driver that a received task management function has been completed. Completion status could be get via - scst_mgmt_cmd_get_status(). No return value expected. Must be defined, if the target supports - task management functionality.
      • -
      • int (* report_aen)(int mgmt_fn, const uint8_t *lun, int lun_len) - this function is - used for Asynchronous Event Notification. It is the responsibility of the driver to notify any/all initiators - about the Asynchronous Event reported. Returns one of the SCST_TGT_RES_* constants, described - below. Must be defined, if low-level protocol supports AEN. This feature is not implemented yet.
      • -
      • int (*proc_info) (char *buffer, char **start, off_t offset, int length, int *eof, struct scst_tgt *tgt, int inout) - statistics and other information to the world outside the kernel.
        Parameters: -
          -
        1. buffer, start, offset, length, eof - have the same meaning as for read_proc_t function of the kernel
        2. -
        3. tgt - pointer to the target, for which the function is called
        4. -
        5. inout - read/write direction flag, 0 - for reads, other value - for writes
        6. -
        - If the driver needs to create additional files in its /proc subdirectory, it can use scst_proc_get_tgt_root() - function to get the root proc_dir_entry.
      • -
      -

      -

      Functions xmit_response(), rdy_to_xfer() are expected to be non-blocking, i.e. return - immediately and don't wait for actual data transfer to finish. Blocking in such command could negatively impact on - overall system performance. If blocking is necessary, it is worth to consider creating dedicated thread(s) in - target driver, to which the commands would be passed and which would perform blocking operations instead of SCST. - If the function allowed to sleep or not is defined by "atomic" attribute of the cmd that can be get via - scst_cmd_atomic(), which is true, if sleeping is not allowed. In this case, if the function - requires sleeping, it can return SCST_TGT_RES_NEED_THREAD_CTX in order to be recalled in the - thread context, where sleeping is allowed.

      -

      Functions xmit_response(), rdy_to_xfer() and report_aen() can return the - following error codes: -

        -
      • SCST_TGT_RES_SUCCESS - success.
      • -
      • SCST_TGT_RES_QUEUE_FULL - internal device queue is full, retry again later.
      • -
      • SCST_TGT_RES_NEED_THREAD_CTX - it is impossible to complete requested task in atomic context. - The command should be restarted in the thread context as described above.
      • -
      • SCST_TGT_RES_FATAL_ERROR - fatal error, i.e. it is unable to perform requested - operation. If returned by xmit_response() the command will be destroyed, if by rdy_to_xfer(), - xmit_response() will be called with HARDWARE ERROR sense data.
      • -

      - -

      More about xmit_response()
      - As already written above, function xmit_response() should transmit the response data and the - status from the cmd parameter. Either it should transmit the data or the status is defined by bits of the value, - returned by scst_cmd_get_tgt_resp_flags().
      They are: -

        -
      • SCST_TSC_FLAG_DATA - set if there are data to be sent
      • -
      • SCST_TSC_FLAG_STATUS - set if the command is finished and there is status/sense to be sent -

      -

      If SCST_TSC_FLAG_DATA is set, the data contained in the buffer, returned by - scst_cmd_get_buffer() (pay attention to scst_cmd_get_use_sg() for scatter/gather) - with length, returned by scst_cmd_get_resp_data_len(). It is recommended to use - scst_get_buf_*()scst_put_buf()/ family of function instead of direct access to the data buffers, - because they hide all HIGHMEM and SG/plain buffer issues.

      -

      If SCST_TSC_FLAG_STATUS is set the status could be received by the appropriate - scst_cmd_get_*_status() functions (see below).

      -

      The sense, if any, is contained in the buffer, returned by scst_cmd_get_sense_buffer(), with - length, returned by scst_cmd_get_sense_buffer_len(). SCST always works in autosense mode. - If a low-level SCSI driver/device doesn't support autosense mode, SCST will issue REQUEST SENSE command, if - necessary. Thus, if CHECK CONDITION established, target driver will always see sense in the sense buffer and - isn't required to request the sense manually.

      -

      It is possible, that SCST_TSC_FLAG_DATA is set, but SCST_TSC_FLAG_STATUS is - not set. In this case the driver should only transmit the data, but not finish the command and transmit the status.
      - Function xmit_response() will be called again either to transmit the status or data once more.

      -

      After the response is completely sent, the target should call scst_tgt_cmd_done() function in - order to allow SCST to free the command.

      -

      Function xmit_response() returns one of the SCST_TGT_RES_* constants, - described above. Pay attention to "atomic" attribute of the cmd, which can be get via - scst_cmd_atomic(): it is true if the function called in the atomic (non-sleeping) context.

      - -

      4.2 Target driver registration functions

      -

      scst_register_target_template()
      Function scst_register_target_template() is - defined as the following:

      -

      int scst_register_target_template(
      - struct scst_tgt_template *vtt)
      -

      -

      Where:

      • vtt - pointer to the target driver template

      -

      Returns 0 on success or appropriate error code otherwise.

      -

      scst_register()
      Function scst_register() is defined as the following:

      -

      struct scst_tgt *scst_register(
      - struct scst_tgt_template *vtt)
      -

      -

      Where:

      • vtt - pointer to the target driver template

      -

      Returns target structure based on template vtt or NULL in case of error.

      - -

      5. Target driver unregistration

      -

      In order to unregister itself target driver should at first call scst_unregister() for all its - adapters and then call scst_unregister_target_template() for its template.

      - -

      5.1 scst_unregister()

      -

      Function scst_unregister() is defined as the following:

      -

      void scst_unregister(
      - struct scst_tgt *tgt)
      -

      -

      Where:

      • tgt - pointer to the target driver structure

      +

      2. Terms and Definitions

      -

      5.2 scst_unregister_target_template()

      -

      Function scst_unregister_target_template() is defined as the following:

      -

      void scst_unregister_target_template(
      - struct scst_tgt_template *vtt)
      -

      -

      Where:

      • vtt - pointer to the target driver template

      - -

      6. SCST session registration

      -

      When target driver determines that it needs to create new SCST session (for example, by receiving new TCP - connection), it should call scst_register_session(), that is defined as the following:

      -

      struct scst_session *scst_register_session(
      - struct scst_tgt *tgt,
      - int atomic,
      - const char *initiator_name,
      - void *data,
      - void (*result_fn) (
      - struct scst_session *sess,
      - void *data,
      - int result));
      -

      -

      Where:

        -
      • tgt - target
      • -
      • atomic - true, if the function called in the atomic context
      • -
      • initiator_name - remote initiator's name, any NULL-terminated string, e.g. iSCSI name, - which used as the key to found appropriate access control group. Could be NULL, then "default" group is used. - The groups are set up via /proc interface.
      • -
      • data - data that will be used as the second parameter for bfresult_fn/()/ function
      • -
      • result_fn - pointer to the function that will be asynchronously called when session - initialization finishes. Can be NULL. Parameters:
          -
        • sess - session
        • -
        • data - target driver supplied to scst_register_session() data
        • -
        • result - session initialization result, 0 on success or appropriate error code otherwise
        • -

      -

      A session creation and initialization is a complex task, which requires sleeping state, so it can't be fully - done in interrupt context. Therefore the "bottom half" of it, if scst_register_session() is - called from atomic context, will be done in SCST thread context. In this case scst_register_session() - will return not completely initialized session, but the target driver can supply commands to this session via - scst_rx_cmd(). Those commands processing will be delayed inside SCST until the session - initialization is finished, then their processing will be restarted. The target driver will be notified about - finish of the session initialization by function result_fn(). On success the target driver could - do nothing, but if the initialization fails, the target driver must ensure that no more new commands being sent or - will be sent to SCST after result_fn() returns.
      All already sent to SCST commands for failed - session will be returned in xmit_response() with BUSY status. In case of failure the driver - shall call scst_unregister_session() inside result_fn(), it will NOT be called - automatically. Thus, scst_register_session() can be called even on IRQ context.

      -

      Session registration is illustrated on Figure 2 and Figure 3.

      -


      Session registration when atomic parameter is false
      -
      Session registration when atomic parameter is true

      - -

      7. SCST session unregistration

      -

      SCST session unregistration basically is the same, except that instead of atomic parameter there is wait one.

      -

      void scst_unregister_session(
      - struct scst_session *sess,
      - int wait,
      - void (* unreg_done_fn)(
      - struct scst_session *sess))
      -

      -

      Where:

        -
      • sess - session to be unregistered
      • -
      • wait - if true, instructs to wait until all commands, which currently executing and belonged - to the session, finished. Otherwise, target driver should be prepared to receive xmit_response() - for the session after scst_unregister_session() returns.
      • -
      • unreg_done_fn - pointer to the function that will be asynchronously called when the last session's command finishes and the session is about to be completely freed. Can be NULL. Parameter:
      • -
        • sess - session
        • -

      -

      All outstanding commands will be finished regularly. After scst_unregister_session() returned - no new commands must be sent to SCST via scst_rx_cmd(). Also, the caller must ensure that no - scst_rx_cmd() or scst_rx_mgmt_fn_*() is called in parallel with - scst_unregister_session().

      -

      Function scst_unregister_session() can be called before result_fn() of - scst_register_session() called, i.e. during the session registration/initialization.

      - -

      8. The commands processing and interaction between SCST and its drivers

      -

      The commands processing by SCST started when target driver calls scst_rx_cmd(). This function - returns SCST's command. Then the target driver finishes the command's initialization, if necessary, for example, - storing necessary target driver specific data there, and calls scst_cmd_init_done() telling SCST - that it can start the processing. Then SCST translates the command's LUN to local device, determines the command's - data direction and required data buffer size by calling appropriate device handler's parse() - function.
      Then:

        -
      • If the command required no data transfer, it will be passed to SCSI mid-level directly or - via device handler's exec() call.
      • -
      • If the command is a READ command (data to the remote/local initiator), necessary space will be - allocated and then the command will be passed to SCSI mid-level directly or via device handler's - exec() call.
      • -
      • If the command is a WRITE command (data from the remote/local initiator), necessary space will be - allocated, then the target's rdy_to_xfer() function will be called, telling the target that - the space is ready and it can start data transferring. When all the data are read from the target, it will call - scst_rx_data(), and the command will be passed to SCSI mid-level directly or via device - handler's exec() call.
      • -

      -

      When the command is finished by SCSI mid-level, device handler's dev_done() is called to - notify it about the command's completion. Then in order to send the response the target's xmit_response() - is called. When the response, including data, if any, is transmitted, the target will call - scst_tgt_cmd_done() telling SCST that it can free the command and its data buffer.

      -

      Then during the command's deallocation device handler's and the target's on_free_cmd() will be - called in this order, if set.

      -

      This sequence is illustrated on Figure 4. To simplify the picture, sign "..." means SCST's waiting state for - the corresponding command to complete. During this state SCST and its drivers continue processing of other - commands, if there are any. One way arrow, for example to xmit_response(), means that after this - function returns, nothing valuable for the current command will be done and SCST goes sleeping or to the next - command processing until corresponding event happens.

      -


      The commands processing flow

      -

      Additionally, before calling scst_cmd_init_done() the target driver can set the following the - command's flags or parameters:

        -
      • DATA_BUF_ALLOCED - set if the data buffer is already allocated. The flag is set via - scst_cmd_set_data_buff_alloced() and get via scst_cmd_get_data_buff_alloced(). - Useful, for instance, for iSCSI unsolicited data.
      • -
      • Expected transfer length and direction via scst_cmd_set_expected() as supplied by - remote initiator, if any. This values will be used only if the command's opcode is unknown for SCST, for example - for vendor-specific commands. If these values not set and opcode isn't known, the command will be completed by - SCST in preprocessing phase with INVALID OPCODE sense.
      • -

      +

      SCSI initiator device

      +

      A SCSI device that originates service and task management requests to be +processed by a SCSI target device and receives device service and task +management responses from SCSI target devices.

      +

      Think of the 'SCSI LLDD' as a BE (Back End) driver.

      +

      SCSI target device

      +

      A SCSI device that receives device service and task management requests +for processing and sends device service and task management responses +to SCSI initiator devices or drivers.

      +

      Think of the 'Target Driver' as an FE (Front End) driver.

      +

      The FE driver interfaces to the initiators (via the +storage-fabric-cloud) and also to the upper edge of the SCST. Whereas +the BE driver interfaces to the targets, i.e. disk-enclosures/JBODs/tapes etc. +and also to the bottom edge of the SCST.

      +

      SCST session

      +

      SCST session is the object that describes relationship between a remote +initiator and SCST via a target driver. All the commands from the remote +initiator is passed to SCST in the session. For example, for connection +oriented protocols, like iSCSI, SCST session could be mapped to the TCP +connection (as well as iSCSI session). SCST session is the close +equivalent of I_T nexus object.

      +

      Local SCSI initiator

      +

      A SCSI initiator that is located on the same host as SCST subsystem. +Examples are sg and st drivers.

      +

      Remote SCSI initiator

      +

      A SCSI initiator that is located on the remote host for SCST subsystem +and makes client connections to SCST via SCSI target drivers.

      +

      SCSI target driver

      +

      A Linux hardware or logical driver that acts as a SCSI target for remote +SCSI initiators, i.e. accepts remote connections, passes incoming SCSI +requests to SCST and sends SCSI responses from SCST back to their +originators.

      +

      Device handler driver

      +

      Also known as "device type specific driver" or "dev handler", is plugin +for SCST, which helps SCST to analyze incoming requests and determine +parameters, specific to various types of devices as well as perform some +processing. See appropriate section for details.

      -

      8.1 The commands processing functions

      -

      scst_rx_cmd()

      -

      Function scst_rx_cmd() creates and sends new command to SCST. Returns the command on success - or NULL otherwise. It is defined as the following:

      -

      struct scst_cmd *scst_rx_cmd(
      - struct scst_session *sess,
      - const uint8_t *lun,
      - int lun_len,
      - const uint8_t *cdb,
      - int cdb_len,
      - int atomic)
      -

      -

      Where:

        -
      • sess - SCST's session
      • -
      • lun - pointer to device's LUN as specified in SCSI Architecture Model 2/3 without - any byte order translation. Extended addressing method is not supported.
      • -
      • lun_len - LUN's length
      • -
      • cdb - SCSI CDB
      • -
      • cdb_len - CDB's length
      • -
      • atomic - if true, the command will be allocated with GFP_ATOMIC flag, otherwise GFP_KERNEL will be used
      • -

      +

      3. SCST Architecture

      -

      scst_cmd_init_done()

      -

      Function scst_cmd_init_done() notifies SCST that the driver finished its part of the command - initialization, and the command is ready for execution. It is defined as the following:

      -

      void scst_cmd_init_done(
      - struct scst_cmd *cmd,
      - int pref_context)
      -

      -

      Where:

        -
      • cmd - the command
      • -
      • pref_context - preferred command execution context. See SCST_CONTEXT_* - constants below for details.
      • -

      -

      scst_rx_data()

      -

      Function scst_rx_data() notifies SCST that the driver received all the necessary data and the - command is ready for further processing. It is defined as the following:

      -

      void scst_rx_data(
      - struct scst_cmd *cmd,
      - int status,
      - int pref_context)
      -

      -

      Where:

        -
      • cmd - the command
      • -
      • status - completion status, see below.
      • -
      • pref_context - preferred command execution context. See SCST_CONTEXT_* constants below for details.
      • -

      -

      Parameter status can have one of the following values:

        -
      • SCST_RX_STATUS_SUCCESS - success
      • -
      • SCST_RX_STATUS_ERROR - data receiving finished with error, so SCST should set the sense and finish the command by calling xmit_response()
      • -
      • SCST_RX_STATUS_ERROR_SENSE_SET - data receiving finished with error and the sense is set, so SCST should finish the command by calling xmit_response()
      • -
      • SCST_RX_STATUS_ERROR_FATAL - data receiving finished with fatal error, so SCST should finish the command, but don't call xmit_response(). In this case the driver must free all associated with the command data before calling scst_rx_data().
      • -

      - -

      scst_tgt_cmd_done()

      -

      Function scst_tgt_cmd_done() notifies SCST that the driver sent the data and/or response. It - must not been called if there are an error and xmit_response() returned something other, than - SCST_TGT_RES_SUCCESS. It is defined as the following:

      -

      void scst_tgt_cmd_done(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - the command

      +

      +SCST accepts commands and passes them to SCSI mid-level at the same +way as SCSI high-level drivers (sg, sd, st) do. Figure 1 shows +interaction between SCST, its drivers and Linux SCSI subsystem.

      +

      +

      + + +
      Interaction between SCST, its drivers and Linux SCSI subsystem. +
      +

      -

      8.2 The commands processing context

      -

      Execution context often is a major problem in the kernel drivers development, because many contexts, like IRQ - one, greatly limit available functionality, therefore require additional complex code in order to pass processing - to more simple context. SCST does its best to undertake most of the context handling.

      -

      On the initialization time SCST creates for internal command processing as many threads as there are processors - in the system or specified by user via scst_threads module parameter. Similarly, as many tasklets - created as there are processors in the system.

      -

      Each command can be processed in one of four contexts:

      -

        -
      1. Directly, i.e. in the caller's context, without limitations
      2. -
      3. Directly atomically, i.e. with sleeping forbidden
      4. -
      5. In the SCST's internal per processor or per session thread
      6. -
      7. In the SCST's per processor tasklet
      8. -

      -

      The target driver sets this context as pref_context parameter for scst_cmd_init_done() and - scst_rx_data(). Additionally, target's template's xmit_response_atomic and - rdy_to_xfer_atomic flags have direct influence on the context. If one of them is false, the - corresponding function will never be called in the atomic context and, if necessary, the command will be - rescheduled to one of the SCST's threads.

      -

      SCST in some circumstances can change preferred context to less restrictive one, for example, for large data - buffer allocation, if there is not enough GFP_ATOMIC memory.

      -

      Preferred context constants

      -

      There are the following preferred context constants:

      -

        -
      • SCST_CONTEXT_DIRECT - sets direct command processing (i.e. regular function calls in the current context) sleeping is allowed, no context restrictions. Supposed to be used when calling from thread context where no locks are held and the driver's architecture allows sleeping without performance degradation or anything like that.
      • -
      • SCST_CONTEXT_DIRECT_ATOMIC - sets direct command processing (i.e. regular function calls in the current context), sleeping is not allowed. Supposed to be used when calling on thread context where there are locks held, when calling on softirq context or the driver's architecture does not allow sleeping without performance degradation or anything like that.
      • -
      • SCST_CONTEXT_TASKLET - tasklet or thread context required for the command processing. Supposed to be used when calling from IRQ context.
      • -
      • SCST_CONTEXT_THREAD - thread context required for the command processing. Supposed to be used if the driver's architecture does not allow using any of above.
      • -

      +

      4. Target driver registration

      -

      9. Task management functions

      -

      There are the following task management functions supported:

      -

        -
      • SCST_ABORT_TASK - ABORT_TASK task management function, aborts the specified task (command). Returns completion status via task_mgmt_fn_done() when the command (task) is actually aborted.
      • -
      • SCST_ABORT_TASK_SET - ABORT_TASK_SET task management function, aborts all tasks (commands) on the specified device. Returns the success via task_mgmt_fn_done() immediately, not waiting for the commands being actually aborted.
      • -
      • SCST_CLEAR_ACA - CLEAR_ACA task management function, currently does nothing.
      • -
      • SCST_CLEAR_TASK_SET - CLEAR_TASK_SET task management function, the same as SCST_ABORT_TASK_SET.
      • -
      • SCST_LUN_RESET - LUN_RESET task management function, implemented via scsi_reset_provider() call for the specified device with SCSI_TRY_RESET_DEVICE parameter.
      • -
      • SCST_TARGET_RESET - TARGET_RESET task management function, implemented via scsi_reset_provider() call for all the hosts in the system (one device per each host) with SCSI_TRY_RESET_BUS parameter at first and then, if failed, with SCSI_TRY_RESET_HOST.
      • -

      +

      To work with SCST a target driver must register its template in SCST by +calling scst_register_target_template(). The template lets SCST know the +target driver's entry points. It is defined as the following:

      -

      9.1 scst_rx_mgmt_fn_tag()

      -

      Function scst_rx_mgmt_fn_tag() tells SCST to perform the specified task management function, - based on the command's tag. Can be used only for SCST_ABORT_TASK.

      -

      It is defined as the following:

      -

      int scst_rx_mgmt_fn_tag(
      struct scst_session *sess,
      int fn,
      uint32_t tag,
      - int atomic,
      void *tgt_specific)

      -

      Where:

        -
      • sess - the session, on which the command should be performed.
      • -
      • fn - task management function, one of the constants above.
      • -
      • tag - the command's tag.
      • -
      • atomic - true, if the function called in the atomic context.
      • -
      • tgt_specific - pointer to the target driver specific data, can be retrieved in - task_mgmt_fn_done() via scst_mgmt_cmd_get_status() function.
      • -
      - Returns 0 if the command was successfully created and scheduled for execution, error code otherwise. On success, - the completion status of the command will be reported asynchronously via task_mgmt_fn_done() - driver's callback.

      - -

      9.2 scst_rx_mgmt_fn_lun()

      -

      Function scst_rx_mgmt_fn_lun() tells SCST to perform the specified task management function, - based on the LUN. Currently it can be used for any function, except SCST_ABORT_TASK.

      -

      It is defined as the following:

      -

      int scst_rx_mgmt_fn_lun(
      struct scst_session *sess,
      int fn,
      const uint8_t *lun,
      - int lun_len,
      int atomic,
      void *tgt_specific);

      +

      4.1 Structure scst_tgt_template +

      -

      Where:

      • sess - the session, on which the command should be performed.
      • -
      • fn - task management function, one of the constants above.
      • -
      • lun - LUN, the format is the same as for scst_rx_cmd().
      • -
      • lun_len - LUN's length.
      • -
      • atomic - true, if the function called in the atomic context.
      • -
      • tgt_specific - pointer to the target driver specific data, can be retrieved in task_mgmt_fn_done() via scst_mgmt_cmd_get_status() function.
      • -
      - Returns 0 if the command was successfully created and scheduled for execution, error code otherwise. On success, - the completion status of the command will be reported asynchronously via task_mgmt_fn_done() - driver's callback.

      - -

      10. Device specific drivers (device handlers)

      -

      Device specific drivers are plugins for SCST, which help SCST to analyze incoming requests and determine - parameters, specific to various types of devices. Device handlers are intended for the following:

      -

        -
      • To get data transfer length and direction directly from CDB and current device's configuration exactly as an end-target SCSI device does. This serves two purposes:
          -
        • Improves security and reliability by not trusting the data supplied by remote initiator via SCSI low-level protocol.
        • -
        • Some low-level SCSI protocols don't provide data transfer length and direction, so that information can be get only directly from CDB and current device's configuration. For example, for tape devices to get data transfer size it might be necessary to know block size setting.
        • -
      • -
      • To process some exceptional conditions, like ILI on tape devices.
      • -
      • To initialize incoming commands with some device-specific parameters, like timeout value.
      • -
      • To allow some additional device-specific commands pre-, post- processing or alternative execution, like copying data from system cache, and do that completely independently from target drivers.
      • -

      -

      Device handlers performs very lightweight processing and therefore should not considerably affect performance - or CPU load. They are considered to be part of SCST, so they could directly access any fields in SCST's structures - as well as use the corresponding functions.

      -

      Without appropriate device handler SCST hides devices of this type from remote initiators and returns -
      HARDWARE ERROR sense data to any requests to them.

      - -

      10.1 Device specific driver registration

      -

      scst_register_dev_driver()

      -

      To work with SCST a device specific driver must register itself in SCST by calling - scst_register_dev_driver(). It is defined as the following:

      -

      int scst_register_dev_driver(
      struct scst_dev_type *dev_type)

      +

      +

      +struct scst_tgt_template 
      +{
      +        int sg_tablesize;
      +        const char name[15];
       
      -				

      Where:

      • dev_type - device specific driver's description structure
      - The function returns 0 on success or appropriate error code otherwise.

      - -

      Structure scst_dev_type

      -

      Structure scst_dev_type is defined as the following:

      -

      struct scst_dev_type
      {
      char name[15];
      int type;

      unsigned parse_atomic:1;
      - unsigned exec_atomic:1;
      unsigned dev_done_atomic:1;

      int (*init) (struct scst_dev_type *dev_type);
      - void (*release) (struct scst_dev_type *dev_type);

      int (*attach) (struct scst_device *dev);
      - void (*detach) (struct scst_device *dev);

      int (*attach_tgt) (struct scst_tgt_device *tgt_dev);
      - void (*detach_tgt) (struct scst_tgt_device *tgt_dev);

      int (*parse) (struct scst_cmd *cmd);
      - int (*exec) (struct scst_cmd *cmd,
      void (*scst_cmd_done)(struct scsi_cmnd *cmd, int next_state));
      - int (*dev_done) (struct scst_cmd *cmd);
      int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd,
      - struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort);
      int (*on_free_cmd) (struct scst_cmd *cmd);

      -
      int (*proc_info) (char *buffer, char **start, off_t offset,
      - int length, int *eof, struct scst_dev_type *dev_type,
      int inout)

      struct module *module;
      - }
      -

      + unsigned unchecked_isa_dma:1; + unsigned use_clustering:1; -

      Where:

        -
      • name - the name of the device handler. Must be defined and unique
      • -
      • type - SCSI type of the supported device. Must be defined.
      • -
      • parse_atomic, exec_atomic, dev_done_atomic - true, if corresponding function supports execution in the atomic (non-sleeping) context
      • -
      • int (*init) (struct scst_dev_type *dev_type) - called on the device handler load, before the first attach(). Returns 0 on success, error code otherwise.
      • -
      • void (*release) (struct scst_dev_type *dev_type) - called on the device handler unload, after final detach()
      • -
      • int (*attach) (struct scst_device *dev) - called when new device is attaching to the device handler
      • -
      • void (*detach) (struct scst_device *dev) - called when new device is detaching from the device handler
      • -
      • int (*attach_tgt) (struct scst_tgt_device *tgt_dev) - called when new tgt_device (session) is attaching to the device handler
      • -
      • void (*detach_tgt) (struct scst_tgt_device *tgt_dev) - called when tgt_device (session) is detaching from the device handler
      • -
      • int (*parse) (struct scst_cmd *cmd, const struct scst_info_cdb *cdb_info) - called to parse CDB from the command. It should initialize cmd->bufflen and cmd->data_direction (see below SCST_DATA_* constants) if necessary, otherwise defaults based on cdb_info will be used. Parameter cdb_info provides some info about the CDB (see below). Pay attention to "atomic" attribute of the cmd, which can be via by scst_cmd_atomic(): it is true if the function called in the atomic (non-sleeping) context. Returns the command's next state or SCST_CMD_STATE_DEFAULT, if the next default state should be used, or SCST_CMD_STATE_NEED_THREAD_CTX if the function called in atomic context, but requires sleeping. In the last case, the function will be recalled in the thread context, where sleeping is allowed. Additionally, SCST_CMD_DATA_BUF_ALLOCED flag can be set by parse() (see above). Must be defined.
      • -
      • int (*exec) (struct scst_cmd *cmd, void (*scst_cmd_done)( struct scst_cmd *cmd, int next_state)) - called to execute CDB. The result of the CDB execution is reported via scst_cmd_done() callback. Pay attention to "atomic" attribute of the command, which can be get via scst_cmd_atomic(): it is true if the function called in the atomic (non-sleeping) context. For scst_cmd_done() parameter next_state is the command's next state or SCST_CMD_STATE_DEFAULT, if the next default state should be used. Using this function modules devdisk_perf and devtape_perf were implemented. These modules in their exec() method skip (pretend to execute) all READ and WRITE operations and thus allow direct link performance measurements without overhead of actual data transferring from/to underlying SCSI device. See also scst_is_cmd_local() below. Returns: -
          -
        • SCST_EXEC_COMPLETED - the command is done, go to other ones
        • -
        • SCST_EXEC_NEED_THREAD - thread context is required to execute the command. Exec() will be called again in the thread context.
        • -
        • SCST_EXEC_NOT_COMPLETED - the command should be sent to SCSI mid-level.
        • -
      • -
      • int (*dev_done) (struct scst_cmd *cmd) - called to notify device handler about the result of the command's execution and perform some post processing. If parse() function is called, dev_done() is guaranteed to be called as well. The command's fields tgt_resp_flags and resp_data_len should be set by this function, but SCST offers good defaults. Pay attention to "atomic" attribute of the command, which can be get via scst_cmd_atomic(): it is true if the function called in the atomic (non-sleeping) context. Returns the command's next state or SCST_CMD_STATE_DEFAULT, if the next default state should be used, or SCST_CMD_STATE_NEED_THREAD_CTX if the function called in atomic context, but requires sleeping. In the last case, the function will be recalled in the thread context, where sleeping is allowed.
      • -
      • int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort) - called to execute a task management command. Returns: -
          -
        • SCST_DEV_TM_COMPLETED_SUCCESS - the command is done with success, no further actions required
        • -
        • SCST_DEV_TM_COMPLETED_FAILED - the command is failed, no further actions required
        • -
        • SCST_DEV_TM_NOT_COMPLETED - regular standard actions for the command should be done
        • -
        NOTE: for SCST_ABORT_TASK called under spinlock
      • -
      • void (*on_free_cmd) (struct scst_cmd *cmd) - called to notify device handler that the command is about to be freed. Could be called on IRQ context.
      • -
      • int (*proc_info) (char *buffer, char **start, off_t offset, int length, int *eof, struct scst_dev_type *dev_type, int inout) - this function can be used to export the handler's statistics and other information to the world outside the kernel. Parameters: -
          -
        1. buffer, start, offset, length, eof - have the same meaning as for read_proc_t function of the kernel
        2. -
        3. dev_type - pointer to the device handler, for which the function is called
        4. -
        5. inout - read/write direction flag, 0 - for reads, other value - for writes
        6. -
        - If the driver needs to create additional files in its /proc subdirectory, it can use scst_proc_get_dev_type_root() function to get the root proc_dir_entry.
      • -
      • struct module *module - pointer to device handler's module
      • -

      - -

      Structure scst_info_cdb is defined as the following:

      -

      struct scst_info_cdb
      {
      enum scst_cdb_flags flags;
      scst_data_direction direction;
      unsigned int transfer_len;
      - unsigned short cdb_len;
      const char *op_name;
      }

      - -

      Where:

      • flags - CDB's flags can be (OR'ed):
          -
        • SCST_TRANSFER_LEN_TYPE_FIXED - set if data length in CDB set in blocks
        • -
        • SCST_SMALL_TIMEOUT - set if CDB requires small timeout
        • -
        • SCST_LONG_TIMEOUT - set if CDB requires long timeout
        • -
      • -
      • direction - one of the SCST_DATA_* constants (see below)
      • -
      • transfer_len - CDB's data length as set in CDB
      • -
      • cdb_len - CDB's length
      • -
      • op_name - the name of the command
      • -

      -

      Field cmd->data_direction, set by parse(), can have one of the following values:

      -

        -
      • SCST_DATA_UNKNOWN - data flow direction is unknown
      • -
      • SCST_DATA_WRITE - data flow direction is WRITE (from target to initiator)
      • -
      • SCST_DATA_READ - data flow direction is READ (from initiator to target)
      • -
      • SCST_DATA_NONE - there is no data transfer
      • -

      + unsigned xmit_response_atomic:1; + unsigned rdy_to_xfer_atomic:1; + unsigned report_aen_atomic:1; + + int (* detect) (struct scst_tgt_template *tgt_template); + int (* release)(struct scst_tgt *tgt); + + int (* xmit_response)(struct scst_cmd *cmd); + int (* rdy_to_xfer)(struct scst_cmd *cmd); + + void (*on_free_cmd) (struct scst_cmd *cmd); + + void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd); + void (* report_aen)(int mgmt_fn, const uint8_t *lun, int lun_len); + + int (*proc_info) (char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_tgt *tgt, int inout); +} +
      +

      +

      Where:

      +

      +

        +
      • sg_tablesize - allows checking whether scatter/gather can be +used or not and, if yes, sets the maximum supported count of +scatter/gather entries +
      • +
      • name - the name of the template. Must be unique to identify +the template. Must be defined. +
      • +
      • unchecked_isa_dma - true, if this target adapter uses +unchecked DMA onto an ISA bus. +
      • +
      • use_clustering - true, if this target adapter wants to use +clustering (i.e. smaller number of segments). +
      • +
      • xmit_response_atomic, rdy_to_xfer_atomic - true, if the +corresponding function supports execution in the atomic (non-sleeping) +context. +
      • +
      • int (* detect) (struct scst_tgt_template *tgt_template) - this +function is intended to detect the target adapters that are present in +the system. Each found adapter should be registered by calling +scst_register(). The function should return a value >= 0 to signify +the number of detected target adapters. A negative value should be +returned whenever there is an error. Must be defined. +
      • +
      • int (* release)(struct scst_tgt *tgt) - this function is +intended to free up the resources allocated to the device. The function +should return 0 to indicate successful release or a negative value if +there are some issues with the release. In the current version of SCST +the return value is ignored. Must be defined. +
      • +
      • int (* xmit_response)(struct scst_cmd *cmd) - this +function is equivalent to the SCSI queuecommand(). The target should +transmit the response data and the status in the struct scst_cmd. See +below for details. Must be defined. +
      • +
      • int (* rdy_to_xfer)(struct scst_cmd *cmd) - this function +informs the driver that data buffer corresponding to the said command +have now been allocated and it is OK to receive data for this command. +This function is necessary because a SCSI target does not have any +control over the commands it receives. Most lower-level protocols have a +corresponding function which informs the initiator that buffers have +been allocated e.g., XFER_RDY in Fibre Channel. After the data is +actually received the low-level driver should call scst_rx_data() +in order to continue processing this command. Returns one of the +SCST_TGT_RES_* constants, described below. Pay attention to +"atomic" attribute of the command, which can be get via +scst_cmd_atomic(): it is true if the function called in the atomic +(non-sleeping) context. Must be defined. +
      • +
      • void (*on_free_cmd)(struct scst_cmd *cmd) - this function +called to notify the driver that the command is about to be freed. +Necessary, because for aborted commands xmit_response() could not be +called. Could be used on IRQ context. Must be defined. +
      • +
      • void (* task_mgmt_fn_done)(struct scst_mgmt_cmd *mgmt_cmd) - +this function informs the driver that a received task management +function has been completed. Completion status could be get via +scst_mgmt_cmd_get_status(). No return value expected. Must be +defined, if the target supports task management functionality. +
      • +
      • int (* report_aen)(int mgmt_fn, const uint8_t *lun, int +lun_len) - this function is used for Asynchronous Event Notification. +It is the responsibility of the driver to notify any/all initiators +about the Asynchronous Event reported. Returns one of the +SCST_TGT_RES_* constants, described below. Must be defined, if +low-level protocol supports AEN. This feature is not implemented yet. +
      • +
      • int (*proc_info) (char *buffer, char **start, off_t offset, +int length, int *eof, struct scst_tgt *tgt, int inout) - this function +can be used to export the driver's statistics and other information to +the world outside the kernel. Parameters: + +
          +
        1. buffer, start, offset, length, eof - have the same +meaning as for read_proc_t function of the kernel +
        2. +
        3. tgt - pointer to the target, for which the function +is called +
        4. +
        5. inout - read/write direction flag, 0 - for reads, other +value - for writes +
        6. +
        -

        10.2 Device specific driver unregistration

        -

        Device specific driver is unregistered by calling scst_unregister_dev_driver(). It is defined - as the following:

        -

        void scst_unregister_dev_driver(
        struct scst_dev_type *dev_type)

        -

        Where:

        • dev_type - device specific driver's description structure

        +If the driver needs to create additional files in its /proc +subdirectory, it can use scst_proc_get_tgt_root() function to get +the root proc_dir_entry. +
      • +
      +

      +

      Functions xmit_response(), rdy_to_xfer() are expected to be +non-blocking, i.e. return immediately and don't wait for actual data +transfer to finish. Blocking in such command could negatively impact on +overall system performance. If blocking is necessary, it is worth to +consider creating dedicated thread(s) in target driver, to which the +commands would be passed and which would perform blocking operations +instead of SCST. If the function allowed to sleep or not is defined by +"atomic" attribute of the cmd that can be get via +scst_cmd_atomic(), which is true, if sleeping is not allowed. In +this case, if the function requires sleeping, it can return +SCST_TGT_RES_NEED_THREAD_CTX in order to be recalled in the thread +context, where sleeping is allowed.

      +

      Functions task_mgmt_fn_done() and report_aen() are recommended +to be non-blocking as well. Blocking there will stop all management +processing for all target drivers in the system (there is only one +management thread in the system).

      +

      Functions xmit_response(), rdy_to_xfer() and report_aen() +can return the following error codes:

      +

      +

        +
      • SCST_TGT_RES_SUCCESS - success. +
      • +
      • SCST_TGT_RES_QUEUE_FULL - internal device queue is full, retry +again later. +
      • +
      • SCST_TGT_RES_NEED_THREAD_CTX - it is impossible to complete +requested task in atomic context. The command should be restarted in the +thread context as described above. +
      • +
      • SCST_TGT_RES_FATAL_ERROR - fatal error, i.e. it is unable to +perform requested operation. If returned by xmit_response() the +command will be destroyed, if by rdy_to_xfer(), +xmit_response() will be called with HARDWARE ERROR sense data. +
      • +
      +

      -

      11. SCST commands' states

      -

      There are the following states, which a SCST command passes through during execution and which could be - returned by device handler's parse() and dev_done() (but not all states are - allowed to be returned):

      -

        -
      • SCST_CMD_STATE_INIT_WAIT - the command is created, but scst_cmd_init_done() not called
      • -
      • SCST_CMD_STATE_INIT - LUN translation (i.e. cmd->tgt_dev assignment) state
      • -
      • SCST_CMD_STATE_REINIT - again LUN translation, used if device handler wants to restart the command on another LUN
      • -
      • SCST_CMD_STATE_DEV_PARSE - device handler's parse() is going to be called
      • -
      • SCST_CMD_STATE_PREPARE_SPACE - allocation of the command's data buffer
      • -
      • SCST_CMD_STATE_RDY_TO_XFER - target driver's rdy_to_xfer() is going to be called
      • -
      • SCST_CMD_STATE_DATA_WAIT - waiting for data from the initiator (until scst_rx_data() called)
      • -
      • SCST_CMD_STATE_SEND_TO_MIDLEV - the command is going to be sent to SCSI mid-level for execution
      • -
      • SCST_CMD_STATE_EXECUTING - waiting for the command's execution finish
      • -
      • SCST_CMD_STATE_DEV_DONE - device handler's dev_done() is going to be called
      • -
      • SCST_CMD_STATE_XMIT_RESP - target driver's xmit_response() is going to be called
      • -
      • SCST_CMD_STATE_XMIT_WAIT - waiting for data/response's transmission finish (until scst_tgt_cmd_done() called)
      • -
      • SCST_CMD_STATE_FINISHED - the command finished and going to be freed
      • -

      - -

      12. SCST's structures manipulation functions

      -

      Target drivers must not directly access any fields in SCST's structures, they must use only described below - functions.

      - -

      12.1 SCST target driver manipulation functions

      -

      scst_tgt_get_tgt_specific() and scst_tgt_set_tgt_specific()

      -

      Function scst_tgt_get_tgt_specific() returns pointer to the target driver specific data, set - by scst_tgt_set_tgt_specific(). It is defined as the following:

      -

      void *scst_tgt_get_tgt_specific(
      struct scst_tgt *tgt)

      -

      Function scst_tgt_set_tgt_specific() stores the target driver specific data that could be - retrieved later by scst_tgt_get_tgt_specific(). It is defined as the following:

      -

      void scst_tgt_set_tgt_specific(
      struct scst_tgt *tgt,
      void *val)

      -

      Where:

      • tgt - pointer to the SCST target structure
      • -
      • val - pointer to the target driver specific data

      +

      More about xmit_response()

      -

      12.2 SCST session manipulation functions

      -

      scst_sess_get_tgt_specific() and scst_sess_set_tgt_specific()

      -

      Function scst_sess_get_tgt_specific() returns pointer to the target driver specific data, set - by scst_sess_set_tgt_specific(). It is defined as the following:

      -

      void *scst_sess_get_tgt_specific(
      struct scst_session *sess)

      -

      Function scst_sess_set_tgt_specific() stores the target driver specific data that could be - retrieved later by scst_sess_set_tgt_specific(). It is defined as the following:

      -

      void scst_sess_set_tgt_specific(
      struct scst_session *sess,
      void *val)

      -

      Where:

      • sess - pointer to the SCST session structure
      • -
      • val - pointer to the target driver specific data

      +

      +As already written above, function xmit_response() should transmit +the response data and the status from the cmd parameter. Either it +should transmit the data or the status is defined by bits of the value, +returned by scst_cmd_get_tgt_resp_flags(). They are:

      +

      +

        +
      • SCST_TSC_FLAG_DATA - set if there are data to be sent +
      • +
      • SCST_TSC_FLAG_STATUS - set if the command is finished and +there is status/sense to be sent +
      • +
      +

      +

      If SCST_TSC_FLAG_DATA is set, the data contained in the buffer, +returned by scst_cmd_get_buffer() (pay attention to +scst_cmd_get_use_sg() for scatter/gather) with length, returned by +scst_cmd_get_resp_data_len(). It is recommended to use +scst_get_buf_*()scst_put_buf()/ family of function instead of +direct access to the data buffers, because they hide all HIGHMEM and +SG/plain buffer issues.

      +

      If SCST_TSC_FLAG_STATUS is set the status could be received by the +appropriate scst_cmd_get_*_status() functions (see below).

      +

      The sense, if any, is contained in the buffer, returned by +scst_cmd_get_sense_buffer(), with length, returned by +scst_cmd_get_sense_buffer_len(). SCST always works in +autosense mode. If a low-level SCSI driver/device doesn't support +autosense mode, SCST will issue REQUEST SENSE command, if necessary. +Thus, if CHECK CONDITION established, target driver will always see +sense in the sense buffer and isn't required to request the sense +manually.

      +

      It is possible, that SCST_TSC_FLAG_DATA is set, but +SCST_TSC_FLAG_STATUS is not set. In this case the driver should +only transmit the data, but not finish the command and transmit the +status. Function xmit_response() will be called again either to +transmit the status or data once more.

      +

      After the response is completely sent, the target should call +scst_tgt_cmd_done() function in order to allow SCST to free the +command.

      +

      Function xmit_response() returns one of the SCST_TGT_RES_* +constants, described above. Pay attention to "atomic" attribute of the +cmd, which can be get via scst_cmd_atomic(): it is true if the +function called in the atomic (non-sleeping) context.

      -

      12.3 SCST command manipulation functions

      -

      scst_cmd_atomic()

      -

      Function scst_cmd_atomic() returns true if the command is being executed in the atomic context - or false otherwise. It is defined as the following:

      -

      int scst_cmd_atomic(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_get_session()

      -

      Function scst_cmd_get_session() returns the command's session. It is defined as the following:

      -

      struct scst_session *scst_cmd_get_session(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_get_resp_data_len()

      -

      Function scst_cmd_get_resp_data_len() returns the command's response data length. It is - defined as the following:

      -

      unsigned int scst_cmd_get_resp_data_len(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_get_tgt_resp_flags()

      -

      Function scst_cmd_get_tgt_resp_flags() returns the command's response data response flags - (SCST_TSC_FLAG_* constants). It is defined as the following:

      - -

      int scst_cmd_get_tgt_resp_flags(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      4.2 Target driver registration functions +

      -

      scst_cmd_get_buffer()

      -

      Function scst_cmd_get_buffer() returns the command's data buffer. It is defined as the following:

      -

      void *scst_cmd_get_buffer(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      scst_register_target_template()

      -

      It is recommended to use scst_get_buf_*()scst_put_buf()/ family of function instead of direct access to the data buffers, because they hide all HIGHMEM and SG/plain buffer issues.

      -

      scst_cmd_get_bufflen()

      -

      Function scst_cmd_get_bufflen() returns the command's data buffer length. It is defined as the following:

      -

      unsigned int scst_cmd_get_bufflen(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      Function scst_register_target_template() is defined as the following:

      +

      +

      +int scst_register_target_template(
      +        struct scst_tgt_template *vtt)
      +
      +

      +

      Where:

      +

      +

        +
      • vtt - pointer to the target driver template
      • +
      +

      +

      Returns 0 on success or appropriate error code otherwise.

      -

      It is recommended to use scst_get_buf_*()scst_put_buf()/ family of function instead of direct - access to the data buffers, because they hide all HIGHMEM and SG/plain buffer issues.

      +

      scst_register()

      -

      scst_cmd_get_use_sg()

      -

      Function scst_cmd_get_use_sg() returns the command's use_sg value. Its meaning is the same as for scsi_cmnd. The function is defined as the following:

      -

      unsigned short scst_cmd_get_use_sg(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      It is recommended to use scst_get_buf_*()scst_put_buf()/ family of function instead of direct access to the data buffers, because they hide all HIGHMEM and SG/plain buffer issues.

      -

      scst_cmd_get_data_direction()

      -

      Function scst_cmd_get_data_direction() returns the command's data direction (SCST_DATA_* - constants). It is defined as the following:

      -

      scst_data_direction scst_cmd_get_data_direction(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      Function scst_register() is defined as the following:

      +

      +

      +struct scst_tgt *scst_register(
      +        struct scst_tgt_template *vtt)
      +
      +

      +

      Where:

      +

      +

        +
      • vtt - pointer to the target driver template
      • +
      +

      +

      Returns target structure based on template vtt or NULL in case of error.

      -

      scst_cmd_get_status()

      -

      Functions scst_cmd_get_status() returns the status byte from host device. It is defined as the following:

      -

      uint8_t scst_cmd_get_status(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      5. Target driver unregistration

      -

      scst_cmd_get_masked_status()

      -

      Functions scst_cmd_get_masked_status() returns the status byte set from host device by status_byte(). It is defined as the following:

      -

      uint8_t scst_cmd_get_masked_status(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_get_msg_status()

      -

      Functions scst_cmd_get_msg_status() returns the status from host adapter itself. It is defined as the following:

      -

      uint8_t scst_cmd_get_msg_status(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      In order to unregister itself target driver should at first call +scst_unregister() for all its adapters and then call +scst_unregister_target_template() for its template.

      -

      scst_cmd_get_host_status()

      -

      Functions scst_cmd_get_host_status() returns the status set by low-level driver to indicate - its status. It is defined as the following:

      -

      uint16_t scst_cmd_get_host_status(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_get_driver_status()

      -

      Functions scst_cmd_get_driver_status() returns the status set by SCSI mid-level. It is defined as - the following:

      -

      uint16_t scst_cmd_get_driver_status(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_get_sense_buffer()

      -

      Functions scst_cmd_get_sense_buffer() returns pointer to the sense buffer. It is defined as the following:

      -

      uint8_t *scst_cmd_get_sense_buffer(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      5.1 scst_unregister() +

      -

      scst_cmd_get_sense_buffer_len()

      -

      Functions scst_cmd_get_sense_buffer_len() returns the sense buffer length. It is defined as - the following:

      -

      int scst_cmd_get_sense_buffer_len(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      +

      Function scst_unregister() is defined as the following:

      +

      +

      +void scst_unregister(
      +        struct scst_tgt *tgt)
      +
      +

      +

      Where:

      +

      +

        +
      • tgt - pointer to the target driver structure
      • +
      +

      -

      scst_cmd_get_tag() and scst_cmd_set_tag()

      -

      Function scst_cmd_get_tag() returns the command's tag, set by scst_cmd_set_tag() - . It is defined as the following:

      -

      uint32_t scst_cmd_get_tag(
      - struct scst_cmd *cmd)
      -

      -

      Function scst_cmd_set_tag() sets command's tag that could be retrieved later by - scst_cmd_get_tag(). It is defined as the following:

      -

      void scst_cmd_set_tag(
      - struct scst_cmd *cmd,
      - uint32_t tag)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      • tag - the tag
      • -
      -

      scst_cmd_get_tgt_specific() and scst_cmd_get_tgt_specific_lock()

      -

      Functions scst_cmd_get_tgt_specific() and scst_cmd_get_tgt_specific_lock() - return pointer to the target driver specific data, set by scst_cmd_set_tgt_specific() or - scst_cmd_set_tgt_specific_lock(). Both function are basically the same, but the later one - additionally takes lock, which helps to prevent some races. See scst_find_cmd() below for details.

      -

      They are defined as the following:

      -

      void *scst_cmd_get_tgt_specific(
      - struct scst_cmd *cmd)
      -
      - void *scst_cmd_get_tgt_specific_lock(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      - -

      scst_cmd_set_tgt_specific() and scst_cmd_set_tgt_specific_lock()

      -

      Functions scst_cmd_set_tgt_specific() and scst_cmd_set_tgt_specific_lock() - store the target driver specific data, that could be retrieved later by scst_cmd_get_tgt_specific() - or scst_cmd_get_tgt_specific_lock(). Both function are basically the same, but the later one - additionally takes lock, which helps to prevent some races. See scst_find_cmd() below for details.

      -

      They are defined as the following:

      -

      void *scst_cmd_set_tgt_specific(
      - struct scst_cmd *cmd,
      - void *val)
      -
      - void *scst_cmd_set_tgt_specific_lock(
      - struct scst_cmd *cmd,
      - void *val)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      • val - pointer to the target driver specific data -
      -

      scst_cmd_get_data_buff_alloced() and scst_cmd_set_data_buff_alloced()

      -

      Function scst_cmd_get_data_buff_alloced() returns the state of the - SCST_CMD_DATA_BUF_ALLOCED flag. It is defined as the following:

      -

      int scst_cmd_get_data_buff_alloced(
      - struct scst_cmd *cmd)
      -

      -

      Function scst_cmd_set_data_buff_alloced() tells SCST that the data buffer is alloced by target - driver or device handler by setting the SCST_CMD_DATA_BUF_ALLOCED flag on. Could be useful, for - instance, for iSCSI unsolicited data. It is defined as the following:

      -

      void scst_cmd_set_data_buff_alloced(
      - struct scst_cmd *cmd)
      -

      -

      Where:

      • cmd - pointer to the command
      -

      scst_cmd_set_expected(), scst_cmd_is_expected_set(),scst_cmd_get_expected_data_direction() and scst_cmd_get_expected_transfer_len()

      -

      Function scst_cmd_set_expected() tells SCST expected data transfer direction and its length, - as supplied by remote initiator. It is defined as the following:

      -

      void scst_cmd_set_expected(
      - struct scst_cmd *cmd,
      - scst_data_direction expected_data_direction,
      - unsigned int expected_transfer_len)
      -

      -

      Function scst_cmd_is_expected_set() returns true, if the expected values were set by target - driver and false otherwise. It is defined as the following:

      -

      int scst_cmd_is_expected_set(
      - struct scst_cmd *cmd)
      -

      -

      Function scst_cmd_get_expected_data_direction() returns expected data direction set by target - driver, if any. If this value was not set, the return value is undefined. It is defined as the following:

      -

      scst_data_direction scst_cmd_get_expected_data_direction(
      - struct scst_cmd *cmd)
      -

      -

      Function scst_cmd_get_expected_transfer_len() returns expected transfer length set by target - driver, if any. If this value was not set, the return value is undefined. It is defined as the following:

      -

      unsigned int scst_cmd_get_expected_transfer_len(
      - struct scst_cmd *cmd)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      • expected_data_direction - expected data direction
      • -
      • expected_transfer_len - expected transfer length
      • -
      -

      scst_get_buf_first(), scst_get_buf_next(),scst_put_buf() and scst_get_buf_count()

      -

      These functions are designed to simplify and unify access to the commands data (SG vector or plain data buffer) - in all possible conditions, including HIGHMEM environment, and should be used instead of direct access.

      -

      Function scst_get_buf_first() starts access to the data. It is defined as the following:

      -

      int scst_get_buf_first(
      - struct scst_cmd *cmd,
      - uint8_t **buf)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      • buf - pointer, where pointer to the first data chunk will be put
      • -
      -

      Returns the length of the chunk of data for success, 0 for the end of data, negative error code otherwise.

      -

      Function scst_get_buf_next() continues access to the data. It is defined as the following:

      -

      int scst_get_buf_next(
      - struct scst_cmd *cmd,
      - uint8_t **buf)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      • buf - pointer, where pointer to the next data chunk will be put
      • -
      -

      Returns the length of the chunk of data for success, 0 for the end of data, negative error code otherwise.

      -

      Function scst_put_buf() tells SCST that the user of the chunk of data, returned by - scst_get_buf_first() or scst_get_buf_next(), finished accessing the data. This - function must be called for all chunks of data, returned by scst_get_buf_first() or - scst_get_buf_next(). It is defined as the following:

      -

      void scst_put_buf(
      - struct scst_cmd *cmd,
      - uint8_t *buf)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      • buf - pointer to the data chunk
      • -
      -

      Function scst_get_buf_count() returns the approximate higher rounded count of data chunks - that scst_get_buf_[first|next]() will return. It is defined as the following:

      -

      int scst_get_buf_count(
      - struct scst_cmd *cmd)
      -

      -

      Where:

        -
      • cmd - pointer to the command
      • -
      +

      5.2 scst_unregister_target_template() +

      -

      12.4 SCST task management commands manipulation functions

      -

      scst_mgmt_cmd_get_tgt_specific()

      -

      Function scst_mgmt_cmd_get_tgt_specific() returns pointer to the target driver specific data, - set on call of scst_rx_mgmt_fn_tag() or scst_rx_mgmt_fn_lun(). It is defined as - the following:

      -

      void *scst_mgmt_cmd_get_tgt_specific(
      - struct scst_mgmt_cmd *mcmd)
      -

      -

      Where:

        -
      • mcmd - pointer to the task management command
      • -
      - -

      scst_mgmt_cmd_get_status()

      -

      Functions scst_mgmt_cmd_get_status() returns task management command's completion status. It - is defined as the following:

      -

      void *scst_mgmt_cmd_get_status(
      - struct scst_mgmt_cmd *mcmd)
      -

      -

      Where:

        -
      • mcmd - pointer to the task management command
      • -
      -

      The following status values are possible:

      -
        -
      • SCST_MGMT_STATUS_SUCCESS - the task management command completed successfully
      • -
      • SCST_MGMT_STATUS_FAILED - the task management command failed.
      • -
      +

      Function scst_unregister_target_template() is defined as the following:

      +

      +

      +void scst_unregister_target_template(
      +        struct scst_tgt_template *vtt)
      +
      +

      +

      Where:

      +

      +

        +
      • vtt - pointer to the target driver template
      • +
      +

      -

      13. Miscellaneous functions

      -

      13.1 scst_find_cmd_by_tag()

      -

      Function scst_find_cmd_by_tag() is designed to find SCST's command based on the supplied tag - comparing it with one that previously set by scst_cmd_set_tag(). This value should be set by the - target driver on the command's initialization time.

      +

      6. SCST session registration

      -

      It is defined as the following:

      -

      struct scst_cmd *scst_find_cmd_by_tag(
      - struct scst_session *sess,
      - uint32_t tag)
      -

      -

      Where:

        -
      • sess - session to which the command belongs
      • -
      • tag - the tag

      -

      Returns found command or NULL otherwise.

      +

      When target driver determines that it needs to create new SCST session +(for example, by receiving new TCP connection), it should call +scst_register_session(), that is defined as the following:

      +

      +

      +struct scst_session *scst_register_session(
      +        struct scst_tgt *tgt,
      +        int atomic,
      +        const char *initiator_name,
      +        void *data,
      +        void (*result_fn) (
      +                struct scst_session *sess,
      +                void *data, 
      +                int result));
      +
      +

      +

      Where:

      +

      +

        +
      • tgt - target +
      • +
      • atomic - true, if the function called in the atomic context +
      • +
      • initiator_name - remote initiator's name, any NULL-terminated +string, e.g. iSCSI name, which used as the key to found appropriate +access control group. Could be NULL, then "default" group is used. The +groups are set up via /proc interface. +
      • +
      • data - data that will be used as the second +parameter for bfresult_fn/()/ function +
      • +
      • result_fn - pointer to the function that will be +asynchronously called when session initialization finishes. Can be NULL. +Parameters: -

        13.2 scst_find_cmd()

        -

        Function scst_find_cmd() is designed to find SCST's command. For example, it can be used to find - the command by internal serial number that was supplied by a remote target's response.

        -

        It is defined as the following:

        -

        struct scst_cmd *scst_find_cmd(
        - struct scst_session *sess,
        - void *data,
        - int (*cmp_fn)(struct scst_cmd *cmd, void *data))
        -

        -

        Where:

          -
        • sess - session to which the command belongs
        • -
        • data - comparison data that will be passed to cmp_fn() as is
        • -
        • cmp_fn - comparison callback function that will be called for each the session's command. Should return true if the command is found, false otherwise. Parameters: -
          • cmd - the command to compare
          • -
          • data - comparison data.
          -

        -

        Returns found command or NULL otherwise.

        -

        IMPORTANT

        -

        SCST is designed in a such way that any command is always processed only by one thread at any time, so no locking - is necessary. But there is one exception from that rule, it is scst_find_cmd() function. Since it - calls the callback over all commands of the session in the internal lists, despite of the command's current state, - there is a race possibility accessing to target specific data pointer between scst_cmd_set_tgt_specific() - caller and cmp_fn(), which usually calls scst_cmd_get_tgt_specific() from the - different context. The only place, where it is safe to call scst_cmd_set_tgt_specific() without the - race probability, is between scst_rx_cmd() and scst_cmd_init_done(). Thus, if you - call scst_cmd_set_tgt_specific() only there, there is nothing to worry, always use the functions - without "lock" suffix. Otherwise, be careful and, if necessary, use "lock" functions. In addition, - cmp_fn() is allowed to use only target specific data and forbidden to call any SCST's functions.

        - -

        13.3 scst_get_cdb_info()

        -

        Function scst_get_cdb_info() provides various CDB info. It is defined as the following:

        -

        int scst_get_cdb_info(
        - const uint8_t *cdb_p,
        - int dev_type,
        - struct scst_info_cdb *info_p)
        -

        -

        Where:

          -
        • cdb_p - pointer to CDB
        • -
        • dev_type - SCSI device type
        • -
        • info_p - the result structure, see description in device handler's parse() chapter
        • -

        -

        Returns 0 on success, -1 otherwise.

        - -

        13.4 scst_to_dma_dir()

        -

        Function scst_to_dma_dir() translates SCST's data direction to DMA one. It is defined as the - following:

        -

        int scst_to_dma_dir(
        - int scst_dir)
        -

        -

        Where:

        • scst_dir - one of the SCST_DATA_* constants

        -

        Returns the corresponding PCI_DMA_* constant.

        - -

        13.5 scst_is_cmd_local()

        -

        Function scst_is_cmd_local() checks if the command is handled by SCST (i.e. locally, as, - e.g., REPORT LUNS command). Intended to be used in device handler's exec(), when the device - handler wants to perform all the commands, except ones that should be done by SCST itself.

        -

        It is defined as the following:

        -

        int scst_is_cmd_local(
        - struct scst_cmd *cmd)
        -

        -

        Where:

        • cmd - the command, which CDB should be checked

        -

        Returns 1, if the command's CDB is locally handled by SCST or 0 otherwise

        - -

        13.6 scst_register_virtual_device() and scst_unregister_virtual_device()

        -

        These functions provide a way for device handlers to register a virtual (emulated) device, which will be - visible only by remote initiators. For example, FILEIO device handler uses files on file system to makes from them - virtual remotely available SCSI disks.

        -

        Function scst_register_virtual_device() registers a virtual device. During the registration - the device handlers functions init() and attach() will be called, if defined. - The function is defined as the following:

        -

        int scst_register_virtual_device(
        - struct scst_dev_type *dev_handler)
        -

        -

        Where:

        • dev_handler - device handler's descriptor

        -

        Returns assigned to the device ID on success, or negative value otherwise.

        -

        Function scst_unregister_virtual_device() unregisters a virtual device. During the - unregistration the device handlers functions detach() and release() will be - called, if defined. The function is defined as the following:

        -

        void scst_unregister_virtual_device(
        - int id)
        -

        -

        Where:

        • id - the device's ID, returned by scst_register_virtual_device()

        +
          +
        • sess - session +
        • +
        • data - target driver supplied to +scst_register_session() data +
        • +
        • result - session initialization result, 0 on success or +appropriate error code otherwise +
        • +
        -

        13.7 scst_add_threads() and scst_del_threads()

        -

        These functions allows to add or delete some SCST threads. For example, if exec() function in - your device handler works synchronously, i.e. wait for job's completion, in order to prevent performance loss you - can add for SCST as many threads as there are devices serviced by your device handler.

        -

        Function scst_add_threads() starts requested number of threads. It is defined as the following:

        -

        int scst_add_threads(
        - int num)
        -

        -

        Where:

        • num - number of the threads to start

        -

        Returns 0 on success, error code otherwise.

        -

        Function scst_del_threads() stops requested number of threads. It is defined as the following:

        -

        void scst_del_threads(
        - int num)
        -

        -

        Where:

        • num - number of the threads to stop

        - -

        13.8 scst_proc_get_tgt_root()

        -

        Function scst_proc_get_tgt_root() returns target driver's root entry in SCST's /proc hierarchy. - The driver can create own files/directories here, which should be deleted in the driver's release(). It is - defined as the following:

        -

        struct proc_dir_entry *scst_proc_get_tgt_root(
        - struct scst_tgt_template *vtt)
        -

        -

        Where:

        • vtt - pointer to the driver's template

        -

        Returns proc_dir_entry on success, NULL otherwise.

        - -

        13.9 scst_proc_get_dev_type_root()

        -

        Function scst_proc_get_dev_type_root() returns device handler's root entry in SCST's /proc - hierarchy. The driver can create own files/directories here, which should be deleted in the driver's detach() or - release(). It is defined as the following:

        -

        struct proc_dir_entry *scst_proc_get_dev_type_root(
        - struct scst_dev_type *dtt)
        -

        -

        Where:

        • dtt - pointer to the handler's description structure

        -

        Returns proc_dir_entry on success, NULL otherwise.

        -
      -
      - - -
      - - - - + +
    +

    +

    A session creation and initialization is a complex task, which requires +sleeping state, so it can't be fully done in interrupt context. +Therefore the "bottom half" of it, if scst_register_session() is +called from atomic context, will be done in SCST thread context. In this +case scst_register_session() will return not completely initialized +session, but the target driver can supply commands to this session via +scst_rx_cmd(). Those commands processing will be delayed inside +SCST until the session initialization is finished, then their processing +will be restarted. The target driver will be notified about finish of +the session initialization by function result_fn(). On success the +target driver could do nothing, but if the initialization fails, the +target driver must ensure that no more new commands being sent or will +be sent to SCST after result_fn() returns. All already sent to SCST +commands for failed session will be returned in xmit_response() +with BUSY status. In case of failure the driver shall call +scst_unregister_session() inside result_fn(), it will NOT be +called automatically. Thus, scst_register_session() can be called +even on IRQ context.

    +

    Session registration is illustrated on Figure 2 and Figure 3.

    +

    +

    + + +
    Session registration when atomic parameter is false +
    +

    +

    +

    + + +
    Session registration when atomic parameter is true +
    +

    - - - - +

    7. SCST session unregistration

    - +

    SCST session unregistration basically is the same, except that instead of +atomic parameter there is wait one.

    +

    +

    +void scst_unregister_session(
    +        struct scst_session *sess, 
    +        int wait,
    +        void (* unreg_done_fn)(
    +                struct scst_session *sess))
    +
    +

    +

    Where:

    +

    +

      +
    • sess - session to be unregistered +
    • +
    • wait - if true, instructs to wait until all commands, which +currently executing and belonged to the session, finished. Otherwise, +target driver should be prepared to receive xmit_response() for +the session after scst_unregister_session() returns. +
    • +
    • unreg_done_fn - pointer to the function that will be +asynchronously called when the last session's command finishes and the +session is about to be completely freed. Can be NULL. Parameter: + +
        +
      • sess - session +
      • +
      + +
    • +
    +

    +

    All outstanding commands will be finished regularly. After +scst_unregister_session() returned no new commands must be sent to +SCST via scst_rx_cmd(). Also, the caller must ensure that no +scst_rx_cmd() or scst_rx_mgmt_fn_*() is called in parallel +with scst_unregister_session().

    +

    Function scst_unregister_session() can be called before +result_fn() of scst_register_session() called, i.e. during the +session registration/initialization.

    + +

    8. The commands processing and interaction between SCST and its drivers

    + +

    The commands processing by SCST started when target driver calls +scst_rx_cmd(). This function returns SCST's command. Then the target +driver finishes the command's initialization, if necessary, for +example, storing necessary target driver specific data there, and calls +scst_cmd_init_done() telling SCST that it can start the processing. +Then SCST translates the command's LUN to local device, determines the +command's data direction and required data buffer size by calling +appropriate device handler's parse() function. Then:

    +

    +

      +
    • If the command required no data transfer, it will be passed to +SCSI mid-level directly or via device handler's exec() call. +
    • +
    • If the command is a READ command (data to the remote/local initiator), +necessary space will be allocated and then the command will be passed +to SCSI mid-level directly or via device handler's exec() call. +
    • +
    • If the command is a WRITE command (data from the remote/local initiator), +necessary space will be allocated, then the target's rdy_to_xfer() +function will be called, telling the target that the space is ready and +it can start data transferring. When all the data are read from the +target, it will call scst_rx_data(), and the command will be passed +to SCSI mid-level directly or via device handler's exec() call. +
    • +
    +

    +

    When the command is finished by SCSI mid-level, device handler's +dev_done() is called to notify it about the command's +completion. Then in order to send the response the target's +xmit_response() is called. When the response, including data, if +any, is transmitted, the target will call scst_tgt_cmd_done() +telling SCST that it can free the command and its data buffer.

    +

    Then during the command's deallocation device handler's and the target's +on_free_cmd() will be called in this order, if set.

    +

    This sequence is illustrated on Figure 4. To simplify the picture, sign +"..." means SCST's waiting state for the corresponding command to +complete. During this state SCST and its drivers continue processing of +other commands, if there are any. One way arrow, for example to +xmit_response(), means that after this function returns, nothing +valuable for the current command will be done and SCST goes sleeping or +to the next command processing until corresponding event happens.

    +

    +

    + + +
    The commands processing flow +
    +

    +

    Additionally, before calling scst_cmd_init_done() the target driver can +set the following the command's flags or parameters:

    +

    +

      +
    • DATA_BUF_ALLOCED - set if the data buffer is already +allocated. The flag is set via scst_cmd_set_data_buff_alloced() and +get via scst_cmd_get_data_buff_alloced(). Useful, for instance, for +iSCSI unsolicited data. +
    • +
    • Expected transfer length and direction via +scst_cmd_set_expected() as supplied by remote initiator, if any. +This values will be used only if the command's opcode is unknown for +SCST, for example for vendor-specific commands. If these values not set +and opcode isn't known, the command will be completed by SCST in +preprocessing phase with INVALID OPCODE sense. +
    • +
    +

    + +

    8.1 The commands processing functions +

    + +

    scst_rx_cmd()

    + +

    Function scst_rx_cmd() creates and sends new command to SCST. Returns +the command on success or NULL otherwise. It is defined as the +following:

    +

    +

    +struct scst_cmd *scst_rx_cmd(
    +        struct scst_session *sess, 
    +        const uint8_t *lun, 
    +        int lun_len,
    +        const uint8_t *cdb, 
    +        int cdb_len, 
    +        int atomic)
    +
    +

    +

    Where:

    +

    +

      +
    • sess - SCST's session +
    • +
    • lun - pointer to device's LUN as specified in SCSI +Architecture Model 2/3 without any byte order translation. Extended +addressing method is not supported. +
    • +
    • lun_len - LUN's length +
    • +
    • cdb - SCSI CDB +
    • +
    • cdb_len - CDB's length +
    • +
    • atomic - if true, the command will be allocated with +GFP_ATOMIC flag, otherwise GFP_KERNEL will be used +
    • +
    +

    + +

    scst_cmd_init_done()

    + +

    Function scst_cmd_init_done() notifies SCST that the driver finished +its part of the command initialization, and the command is ready for +execution. It is defined as the following:

    +

    +

    +void scst_cmd_init_done(
    +        struct scst_cmd *cmd, 
    +        int pref_context)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - the command +
    • +
    • pref_context - preferred command execution context. See +SCST_CONTEXT_* constants below for details. +
    • +
    +

    + +

    scst_rx_data()

    + +

    Function scst_rx_data() notifies SCST that the driver received all +the necessary data and the command is ready for further processing. It +is defined as the following:

    +

    +

    +void scst_rx_data(
    +        struct scst_cmd *cmd, 
    +        int status,
    +        int pref_context)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - the command +
    • +
    • status - completion status, see below. +
    • +
    • pref_context - preferred command execution context. See +SCST_CONTEXT_* constants below for details. +
    • +
    +

    +

    Parameter status can have one of the following values:

    +

    +

      +
    • SCST_RX_STATUS_SUCCESS - success +
    • +
    • SCST_RX_STATUS_ERROR - data receiving finished with error, so +SCST should set the sense and finish the command by calling +xmit_response() +
    • +
    • SCST_RX_STATUS_ERROR_SENSE_SET - data receiving finished with +error and the sense is set, so SCST should finish the command by calling +xmit_response() +
    • +
    • SCST_RX_STATUS_ERROR_FATAL - data receiving finished with +fatal error, so SCST should finish the command, but don't call +xmit_response(). In this case the driver must free all associated +with the command data before calling scst_rx_data(). +
    • +
    +

    + +

    scst_tgt_cmd_done()

    + +

    Function scst_tgt_cmd_done() notifies SCST that the driver sent the +data and/or response. It must not been called if there are an error and +xmit_response() returned something other, than +SCST_TGT_RES_SUCCESS. It is defined as the following:

    +

    +

    +void scst_tgt_cmd_done(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where: +

      +
    • cmd - the command
    • +
    +

    + +

    8.2 The commands processing context +

    + +

    Execution context often is a major problem in the kernel drivers +development, because many contexts, like IRQ one, greatly limit +available functionality, therefore require additional complex code in +order to pass processing to more simple context. SCST does its best to +undertake most of the context handling.

    +

    On the initialization time SCST creates for internal command processing +as many threads as there are processors in the system or specified by +user via scst_threads module parameter. Similarly, as many tasklets +created as there are processors in the system.

    +

    Each command can be processed in one of four contexts:

    +

    +

      +
    1. Directly, i.e. in the caller's context, without limitations
    2. +
    3. Directly atomically, i.e. with sleeping forbidden
    4. +
    5. In the SCST's internal per processor or per session thread
    6. +
    7. In the SCST's per processor tasklet
    8. +
    +

    +

    The target driver sets this context as pref_context parameter for +scst_cmd_init_done() and scst_rx_data(). Additionally, target's +template's xmit_response_atomic and rdy_to_xfer_atomic flags +have direct influence on the context. If one of them is false, the +corresponding function will never be called in the atomic context and, +if necessary, the command will be rescheduled to one of the SCST's +threads.

    +

    SCST in some circumstances can change preferred context to less +restrictive one, for example, for large data buffer allocation, if +there is not enough GFP_ATOMIC memory.

    + +

    Preferred context constants

    + +

    There are the following preferred context constants:

    +

    +

      +
    • SCST_CONTEXT_DIRECT - sets direct command processing (i.e. +regular function calls in the current context) sleeping is allowed, no +context restrictions. Supposed to be used when calling from thread +context where no locks are held and the driver's architecture allows +sleeping without performance degradation or anything like that. +
    • +
    • SCST_CONTEXT_DIRECT_ATOMIC - sets direct command processing +(i.e. regular function calls in the current context), sleeping is not +allowed. Supposed to be used when calling on thread context where there +are locks held, when calling on softirq context or the driver's +architecture does not allow sleeping without performance degradation or +anything like that. +
    • +
    • SCST_CONTEXT_TASKLET - tasklet or thread context required for +the command processing. Supposed to be used when calling from IRQ +context. +
    • +
    • SCST_CONTEXT_THREAD - thread context required for the +command processing. Supposed to be used if the driver's architecture +does not allow using any of above. +
    • +
    +

    + +

    9. Task management functions

    + +

    There are the following task management functions supported:

    +

    +

      +
    • SCST_ABORT_TASK - ABORT_TASK task management function, +aborts the specified task (command). Returns completion status via +task_mgmt_fn_done() when the command (task) is actually aborted. +
    • +
    • SCST_ABORT_TASK_SET - ABORT_TASK_SET task management +function, aborts all tasks (commands) on the specified device. Returns +the success via task_mgmt_fn_done() immediately, not waiting for +the commands being actually aborted. +
    • +
    • SCST_CLEAR_ACA - CLEAR_ACA task management function, +currently does nothing. +
    • +
    • SCST_CLEAR_TASK_SET - CLEAR_TASK_SET task management +function, the same as SCST_ABORT_TASK_SET. +
    • +
    • SCST_LUN_RESET - LUN_RESET task management function, +implemented via scsi_reset_provider() call for the specified device +with SCSI_TRY_RESET_DEVICE parameter. +
    • +
    • SCST_TARGET_RESET - TARGET_RESET task management +function, implemented via scsi_reset_provider() call for all the +hosts in the system (one device per each host) with +SCSI_TRY_RESET_BUS parameter at first and then, if failed, with +SCSI_TRY_RESET_HOST. +
    • +
    +

    + +

    9.1 scst_rx_mgmt_fn_tag() +

    + +

    Function scst_rx_mgmt_fn_tag() tells SCST to perform the specified +task management function, based on the command's tag. Can be used only +for SCST_ABORT_TASK.

    +

    It is defined as the following:

    +

    +

    +int scst_rx_mgmt_fn_tag(
    +        struct scst_session *sess, 
    +        int fn, 
    +        uint32_t tag,
    +        int atomic, 
    +        void *tgt_specific)
    +
    +

    +

    Where:

    +

    +

      +
    • sess - the session, on which the command should be performed. +
    • +
    • fn - task management function, one of the constants above. +
    • +
    • tag - the command's tag. +
    • +
    • atomic - true, if the function called in the atomic context. +
    • +
    • tgt_specific - pointer to the target driver specific data, +can be retrieved in task_mgmt_fn_done() via +scst_mgmt_cmd_get_status() function. +
    • +
    +

    +

    Returns 0 if the command was successfully created and scheduled for +execution, error code otherwise. On success, the completion status of +the command will be reported asynchronously via task_mgmt_fn_done() +driver's callback.

    + +

    9.2 scst_rx_mgmt_fn_lun() +

    + +

    Function scst_rx_mgmt_fn_lun() tells SCST to perform the specified +task management function, based on the LUN. Currently it can be used for +any function, except SCST_ABORT_TASK.

    +

    It is defined as the following:

    +

    +

    +int scst_rx_mgmt_fn_lun(
    +        struct scst_session *sess, 
    +        int fn,
    +        const uint8_t *lun, 
    +        int lun_len,
    +        int atomic, 
    +        void *tgt_specific);
    +
    +

    +

    Where:

    +

    +

      +
    • sess - the session, on which the command should be performed. +
    • +
    • fn - task management function, one of the constants above. +
    • +
    • lun - LUN, the format is the same as for scst_rx_cmd(). +
    • +
    • lun_len - LUN's length. +
    • +
    • atomic - true, if the function called in the atomic context. +
    • +
    • tgt_specific - pointer to the target driver specific data, +can be retrieved in task_mgmt_fn_done() via +scst_mgmt_cmd_get_status() function. +
    • +
    +

    +

    Returns 0 if the command was successfully created and scheduled for +execution, error code otherwise. On success, the completion status of +the command will be reported asynchronously via task_mgmt_fn_done() +driver's callback.

    + +

    10. Device specific drivers (device handlers)

    + +

    Device specific drivers are plugins for SCST, which help SCST to analyze +incoming requests and determine parameters, specific to various types +of devices. Device handlers are intended for the following:

    +

    +

      +
    • To get data transfer length and direction directly from CDB and +current device's configuration exactly as an end-target SCSI device +does. This serves two purposes: + +
        +
      • Improves security and reliability by not trusting the data +supplied by remote initiator via SCSI low-level protocol. +
      • +
      • Some low-level SCSI protocols don't provide data transfer +length and direction, so that information can be get only +directly from CDB and current device's configuration. For +example, for tape devices to get data transfer size it might be +necessary to know block size setting. +
      • +
      + +
    • +
    • To process some exceptional conditions, like ILI on tape devices. +
    • +
    • To initialize incoming commands with some device-specific +parameters, like timeout value. +
    • +
    • To allow some additional device-specific commands pre-, post- +processing or alternative execution, like copying data from system +cache, and do that completely independently from target drivers. +
    • +
    +

    +

    Device handlers performs very lightweight processing and therefore +should not considerably affect performance or CPU load. They are +considered to be part of SCST, so they could directly access any fields +in SCST's structures as well as use the corresponding functions.

    +

    Without appropriate device handler SCST hides devices of this type from +remote initiators and returns HARDWARE ERROR sense data to any +requests to them.

    + +

    10.1 Device specific driver registration +

    + +

    scst_register_dev_driver()

    + +

    To work with SCST a device specific driver must register itself in SCST by +calling scst_register_dev_driver(). It is defined as the following:

    +

    +

    +int scst_register_dev_driver(
    +        struct scst_dev_type *dev_type)
    +
    +

    +

    Where:

    +

    +

      +
    • dev_type - device specific driver's description structure
    • +
    +

    +

    The function returns 0 on success or appropriate error code otherwise.

    + +

    Structure scst_dev_type

    + +

    Structure scst_dev_type is defined as the following:

    +

    +

    +struct scst_dev_type
    +{
    +        char name[15];
    +        int type;
    + 
    +        unsigned parse_atomic:1;
    +        unsigned exec_atomic:1;
    +        unsigned dev_done_atomic:1;
    +        
    +        int (*init) (struct scst_dev_type *dev_type);
    +        void (*release) (struct scst_dev_type *dev_type);
    + 
    +        int (*attach) (struct scst_device *dev);
    +        void (*detach) (struct scst_device *dev);
    + 
    +        int (*attach_tgt) (struct scst_tgt_device *tgt_dev);
    +        void (*detach_tgt) (struct scst_tgt_device *tgt_dev);
    + 
    +        int (*parse) (struct scst_cmd *cmd);
    +        int (*exec) (struct scst_cmd *cmd, 
    +                void (*scst_cmd_done)(struct scsi_cmnd *cmd, int next_state));
    +        int (*dev_done) (struct scst_cmd *cmd);
    +        int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, 
    +                struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort);
    +        int (*on_free_cmd) (struct scst_cmd *cmd);
    +        
    +        int (*proc_info) (char *buffer, char **start, off_t offset,
    +                int length, int *eof, struct scst_dev_type *dev_type,
    +                int inout)
    + 
    +        struct module *module;
    +}
    +
    +

    +

    Where:

    +

    +

      +
    • name - the name of the device handler. Must be defined and +unique +
    • +
    • type - SCSI type of the supported device. Must be defined. +
    • +
    • parse_atomic, exec_atomic, dev_done_atomic - true, +if corresponding function supports execution in the atomic +(non-sleeping) context +
    • +
    • int (*init) (struct scst_dev_type *dev_type) - called on the +device handler load, before the first attach(). Returns 0 on success, +error code otherwise. +
    • +
    • void (*release) (struct scst_dev_type *dev_type) - called on +the device handler unload, after final detach() +
    • +
    • int (*attach) (struct scst_device *dev) - called when new +device is attaching to the device handler +
    • +
    • void (*detach) (struct scst_device *dev) - called when new +device is detaching from the device handler +
    • +
    • int (*attach_tgt) (struct scst_tgt_device *tgt_dev) - called +when new tgt_device (session) is attaching to the device handler +
    • +
    • void (*detach_tgt) (struct scst_tgt_device *tgt_dev) - called +when tgt_device (session) is detaching from the device handler +
    • +
    • int (*parse) (struct scst_cmd *cmd, const struct scst_info_cdb +*cdb_info) - called to parse CDB from the command. It should initialize +cmd->bufflen and cmd->data_direction (see below +SCST_DATA_* constants) if necessary, otherwise defaults based on +cdb_info will be used. Parameter cdb_info provides some info +about the CDB (see below). Pay attention to "atomic" attribute of the +cmd, which can be via by scst_cmd_atomic(): it is true if the +function called in the atomic (non-sleeping) context. Returns the +command's next state or SCST_CMD_STATE_DEFAULT, if the next default +state should be used, or SCST_CMD_STATE_NEED_THREAD_CTX if the +function called in atomic context, but requires sleeping. In the last +case, the function will be recalled in the thread context, where +sleeping is allowed. Additionally, SCST_CMD_DATA_BUF_ALLOCED flag +can be set by parse() (see above). Must be defined. +
    • +
    • int (*exec) (struct scst_cmd *cmd, void (*scst_cmd_done)( struct +scst_cmd *cmd, int next_state)) - called to execute CDB. The result of +the CDB execution is reported via scst_cmd_done() callback. Pay +attention to "atomic" attribute of the command, which can be get via +scst_cmd_atomic(): it is true if the function called in the +atomic (non-sleeping) context. For scst_cmd_done() parameter +next_state is the command's next state or +SCST_CMD_STATE_DEFAULT, if the next default state should be used. +Using this function modules devdisk_perf and devtape_perf were +implemented. These modules in their exec() method skip (pretend to +execute) all READ and WRITE operations and thus allow direct link +performance measurements without overhead of actual data transferring +from/to underlying SCSI device. See also scst_is_cmd_local() below. +Returns: + +
        +
      • SCST_EXEC_COMPLETED - the command is done, go to +other ones +
      • +
      • SCST_EXEC_NEED_THREAD - thread context is required to +execute the command. Exec() will be called again in the +thread context. +
      • +
      • SCST_EXEC_NOT_COMPLETED - the command should be sent +to SCSI mid-level. +
      • +
      + +
    • +
    • int (*dev_done) (struct scst_cmd *cmd) - called to notify +device handler about the result of the command's execution and perform +some post processing. If parse() function is called, +dev_done() is guaranteed to be called as well. The command's fields +tgt_resp_flags and resp_data_len should be set by this +function, but SCST offers good defaults. Pay attention to "atomic" +attribute of the command, which can be get via +scst_cmd_atomic(): it is true if the function called in the +atomic (non-sleeping) context. Returns the command's next state or +SCST_CMD_STATE_DEFAULT, if the next default state should be used, +or SCST_CMD_STATE_NEED_THREAD_CTX if the function called in atomic +context, but requires sleeping. In the last case, the function will be +recalled in the thread context, where sleeping is allowed. +
    • +
    • int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, struct +scst_tgt_dev *tgt_dev, struct scst_cmd *cmd_to_abort) - called to +execute a task management command. Returns: + +
        +
      • SCST_DEV_TM_COMPLETED_SUCCESS - the command is done +with success, no further actions required +
      • +
      • SCST_DEV_TM_COMPLETED_FAILED - the command is failed, +no further actions required +
      • +
      • SCST_DEV_TM_NOT_COMPLETED - regular standard actions +for the command should be done +
      • +
      + + +NOTE: for SCST_ABORT_TASK called under spinlock +
    • +
    • void (*on_free_cmd) (struct scst_cmd *cmd) - called to notify +device handler that the command is about to be freed. Could be called on +IRQ context. +
    • +
    • int (*proc_info) (char *buffer, char **start, off_t offset, +int length, int *eof, struct scst_dev_type *dev_type, int inout) - this +function can be used to export the handler's statistics and other +information to the world outside the kernel. Parameters: + +
        +
      1. buffer, start, offset, length, eof - have the same +meaning as for read_proc_t function of the kernel +
      2. +
      3. dev_type - pointer to the device handler, for which +the function is called +
      4. +
      5. inout - read/write direction flag, 0 - for reads, other +value - for writes +
      6. +
      + + +If the driver needs to create additional files in its /proc +subdirectory, it can use scst_proc_get_dev_type_root() function to get +the root proc_dir_entry. +
    • +
    • struct module *module - pointer to device handler's module +
    • +
    +

    +

    Structure scst_info_cdb is defined as the following:

    +

    +

    +struct scst_info_cdb
    +{
    +        enum scst_cdb_flags flags;
    +        scst_data_direction direction;
    +        unsigned int transfer_len;
    +        unsigned short cdb_len;
    +        const char *op_name;
    +}
    +
    +

    +

    Where:

    +

    +

      +
    • flags - CDB's flags can be (OR'ed): + +
        +
      • SCST_TRANSFER_LEN_TYPE_FIXED - set if data length in +CDB set in blocks +
      • +
      • SCST_SMALL_TIMEOUT - set if CDB requires small timeout +
      • +
      • SCST_LONG_TIMEOUT - set if CDB requires long timeout +
      • +
      + +
    • +
    • direction - one of the SCST_DATA_* constants (see below) +
    • +
    • transfer_len - CDB's data length as set in CDB +
    • +
    • cdb_len - CDB's length +
    • +
    • op_name - the name of the command +
    • +
    +

    +

    Field cmd->data_direction, set by parse(), can have one of the +following values:

    +

    +

      +
    • SCST_DATA_UNKNOWN - data flow direction is unknown +
    • +
    • SCST_DATA_WRITE - data flow direction is WRITE (from +target to initiator) +
    • +
    • SCST_DATA_READ - data flow direction is READ (from +initiator to target) +
    • +
    • SCST_DATA_NONE - there is no data transfer +
    • +
    +

    + +

    10.2 Device specific driver unregistration +

    + +

    Device specific driver is unregistered by calling +scst_unregister_dev_driver(). It is defined as the following:

    +

    +

    +void scst_unregister_dev_driver(
    +        struct scst_dev_type *dev_type)
    +
    +

    +

    Where:

    +

    +

      +
    • dev_type - device specific driver's description structure
    • +
    +

    + +

    11. SCST commands' states

    + +

    +There are the following states, which a SCST command passes through +during execution and which could be returned by device handler's +parse() and dev_done() (but not all states are allowed to be +returned):

    +

    +

      +
    • SCST_CMD_STATE_INIT_WAIT - the command is created, but +scst_cmd_init_done() not called +
    • +
    • SCST_CMD_STATE_INIT - LUN translation (i.e. cmd->tgt_dev +assignment) state +
    • +
    • SCST_CMD_STATE_REINIT - again LUN translation, used if device +handler wants to restart the command on another LUN +
    • +
    • SCST_CMD_STATE_DEV_PARSE - device handler's parse() is going +to be called +
    • +
    • SCST_CMD_STATE_PREPARE_SPACE - allocation of the command's +data buffer +
    • +
    • SCST_CMD_STATE_RDY_TO_XFER - target driver's +rdy_to_xfer() is going to be called +
    • +
    • SCST_CMD_STATE_DATA_WAIT - waiting for data from the initiator +(until scst_rx_data() called) +
    • +
    • SCST_CMD_STATE_SEND_TO_MIDLEV - the command is going to be +sent to SCSI mid-level for execution +
    • +
    • SCST_CMD_STATE_EXECUTING - waiting for the command's execution +finish +
    • +
    • SCST_CMD_STATE_DEV_DONE - device handler's dev_done() is +going to be called +
    • +
    • SCST_CMD_STATE_XMIT_RESP - target driver's +xmit_response() is going to be called +
    • +
    • SCST_CMD_STATE_XMIT_WAIT - waiting for data/response's +transmission finish (until scst_tgt_cmd_done() called) +
    • +
    • SCST_CMD_STATE_FINISHED - the command finished and going to be +freed +
    • +
    +

    + +

    12. SCST's structures manipulation functions

    + +

    Target drivers must not directly access any fields in SCST's structures, +they must use only described below functions.

    + +

    12.1 SCST target driver manipulation functions +

    + +

    scst_tgt_get_tgt_specific() and scst_tgt_set_tgt_specific()

    + +

    +Function scst_tgt_get_tgt_specific() returns pointer to the target +driver specific data, set by scst_tgt_set_tgt_specific(). It is +defined as the following:

    +

    +

    +void *scst_tgt_get_tgt_specific(
    +        struct scst_tgt *tgt)
    +
    +

    +

    Function scst_tgt_set_tgt_specific() stores the target driver +specific data that could be retrieved later by +byscst_tgt_get_tgt_specific(). It is defined as the following:

    +

    +

    +void scst_tgt_set_tgt_specific(
    +        struct scst_tgt *tgt,
    +        void *val)
    +
    +

    +

    Where:

    +

    +

      +
    • tgt - pointer to the SCST target structure
    • +
    • val - pointer to the target driver specific data
    • +
    +

    + +

    12.2 SCST session manipulation functions +

    + +

    scst_sess_get_tgt_specific() and scst_sess_set_tgt_specific()

    + +

    Function scst_sess_get_tgt_specific() returns pointer to the target +driver specific data, set by scst_sess_set_tgt_specific(). It is +defined as the following:

    +

    +

    +void *scst_sess_get_tgt_specific(
    +        struct scst_session *sess)
    +
    +

    +

    Function scst_sess_set_tgt_specific() stores the target driver +specific data that could be retrieved later by +byscst_sess_get_tgt_specific(). It is defined as the following:

    +

    +

    +void scst_sess_set_tgt_specific(
    +        struct scst_session *sess,
    +        void *val)
    +
    +

    +

    Where:

    +

    +

      +
    • sess - pointer to the SCST session structure
    • +
    • val - pointer to the target driver specific data
    • +
    +

    + +

    12.3 SCST command manipulation functions +

    + +

    scst_cmd_atomic()

    + +

    Function scst_cmd_atomic() returns true if the command is +being executed in the atomic context or false otherwise. It is defined +as the following:

    +

    +

    +int scst_cmd_atomic(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command to check
    • +
    +

    + +

    scst_cmd_get_session()

    + +

    Function scst_cmd_get_session() returns the command's session. It +is defined as the following:

    +

    +

    +struct scst_session *scst_cmd_get_session(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_resp_data_len()

    + +

    Function scst_cmd_get_resp_data_len() returns the command's +response data length. It is defined as the following:

    +

    +

    +unsigned int scst_cmd_get_resp_data_len(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_tgt_resp_flags()

    + +

    Function scst_cmd_get_tgt_resp_flags() returns the command's +response data response flags (SCST_TSC_FLAG_* constants). It is defined +as the following:

    +

    +

    +int scst_cmd_get_tgt_resp_flags(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_buffer()

    + +

    Function scst_cmd_get_buffer() returns the command's data buffer. +It is defined as the following:

    +

    +

    +void *scst_cmd_get_buffer(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    +

    It is recommended to use scst_get_buf_*()scst_put_buf()/ family of +function instead of direct access to the data buffers, because they hide +all HIGHMEM and SG/plain buffer issues.

    + +

    scst_cmd_get_bufflen()

    + +

    Function scst_cmd_get_bufflen() returns the command's data buffer +length. It is defined as the following:

    +

    +

    +unsigned int scst_cmd_get_bufflen(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    +

    It is recommended to use scst_get_buf_*()scst_put_buf()/ family of +function instead of direct access to the data buffers, because they hide +all HIGHMEM and SG/plain buffer issues.

    + +

    scst_cmd_get_use_sg()

    + +

    Function scst_cmd_get_use_sg() returns the command's use_sg +value. Its meaning is the same as for scsi_cmnd. The function is +defined as the following:

    +

    +

    +unsigned short scst_cmd_get_use_sg(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    +

    It is recommended to use scst_get_buf_*()scst_put_buf()/ family of +function instead of direct access to the data buffers, because they hide +all HIGHMEM and SG/plain buffer issues.

    + +

    scst_cmd_get_data_direction()

    + +

    Function scst_cmd_get_data_direction() returns the command's data +direction (SCST_DATA_* constants). It is defined as the following:

    +

    +

    +scst_data_direction scst_cmd_get_data_direction(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_status()

    + +

    Functions scst_cmd_get_status() returns the status byte from +host device. It is defined as the following:

    +

    +

    +uint8_t scst_cmd_get_status(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_masked_status()

    + +

    Functions scst_cmd_get_masked_status() returns the status byte set +from host device by status_byte(). It is defined as the following:

    +

    +

    +uint8_t scst_cmd_get_masked_status(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_msg_status()

    + +

    Functions scst_cmd_get_msg_status() returns the status from host +adapter itself. It is defined as the following:

    +

    +

    +uint8_t scst_cmd_get_msg_status(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_host_status()

    + +

    Functions scst_cmd_get_host_status() returns the status set by +low-level driver to indicate its status. It is defined as the following:

    +

    +

    +uint16_t scst_cmd_get_host_status(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_driver_status()

    + +

    Functions scst_cmd_get_driver_status() returns the status set by +SCSI mid-level. It is defined as the following:

    +

    +

    +uint16_t scst_cmd_get_driver_status(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_sense_buffer()

    + +

    Functions scst_cmd_get_sense_buffer() returns pointer to the sense +buffer. It is defined as the following:

    +

    +

    +uint8_t *scst_cmd_get_sense_buffer(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_sense_buffer_len()

    + +

    Functions scst_cmd_get_sense_buffer_len() returns the sense buffer +length. It is defined as the following:

    +

    +

    +int scst_cmd_get_sense_buffer_len(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_get_tag() and scst_cmd_set_tag()

    + +

    Function scst_cmd_get_tag() returns the command's tag, set by +scst_cmd_set_tag(). It is defined as the following:

    +

    +

    +uint32_t scst_cmd_get_tag(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Function scst_cmd_set_tag() sets command's tag that could be +retrieved later by scst_cmd_get_tag(). It is defined as the +following:

    +

    +

    +void scst_cmd_set_tag(
    +        struct scst_cmd *cmd,
    +        uint32_t tag)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    • tag - the tag
    • +
    +

    + +

    scst_cmd_get_tgt_specific() and scst_cmd_get_tgt_specific_lock()

    + +

    Functions scst_cmd_get_tgt_specific() and +scst_cmd_get_tgt_specific_lock() return pointer to the target +driver specific data, set by scst_cmd_set_tgt_specific() or +scst_cmd_set_tgt_specific_lock(). Both function are basically the +same, but the later one additionally takes lock, which helps to prevent +some races. See scst_find_cmd() below for details.

    +

    They are defined as the following:

    +

    +

    +void *scst_cmd_get_tgt_specific(
    +        struct scst_cmd *cmd)
    +
    +

    +

    +

    +void *scst_cmd_get_tgt_specific_lock(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_set_tgt_specific() and scst_cmd_set_tgt_specific_lock()

    + +

    Functions scst_cmd_set_tgt_specific() and +scst_cmd_set_tgt_specific_lock() store the target driver specific +data, that could be retrieved later by scst_cmd_get_tgt_specific() +or scst_cmd_get_tgt_specific_lock(). Both function are basically +the same, but the later one additionally takes lock, which helps to +prevent some races. See scst_find_cmd() below for details.

    +

    They are defined as the following:

    +

    +

    +void *scst_cmd_set_tgt_specific(
    +        struct scst_cmd *cmd,
    +        void *val)
    +
    +

    +

    +

    +void *scst_cmd_set_tgt_specific_lock(
    +        struct scst_cmd *cmd,
    +        void *val)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    • val - pointer to the target driver specific data
    • +
    +

    + +

    scst_cmd_get_data_buff_alloced() and scst_cmd_set_data_buff_alloced()

    + +

    Function scst_cmd_get_data_buff_alloced() returns the state of the +SCST_CMD_DATA_BUF_ALLOCED flag. It is defined as the following:

    +

    +

    +int scst_cmd_get_data_buff_alloced(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Function scst_cmd_set_data_buff_alloced() tells SCST that the data +buffer is alloced by target driver or device handler by setting the +SCST_CMD_DATA_BUF_ALLOCED flag on. Could be useful, for instance, +for iSCSI unsolicited data. It is defined as the following:

    +

    +

    +void scst_cmd_set_data_buff_alloced(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    scst_cmd_set_expected(), scst_cmd_is_expected_set(),scst_cmd_get_expected_data_direction() andscst_cmd_get_expected_transfer_len()

    + +

    Function scst_cmd_set_expected() tells SCST expected data transfer +direction and its length, as supplied by remote initiator. It is defined +as the following:

    +

    +

    +void scst_cmd_set_expected(
    +        struct scst_cmd *cmd,
    +        scst_data_direction expected_data_direction,
    +        unsigned int expected_transfer_len)
    +
    +

    +

    Function scst_cmd_is_expected_set() returns true, if the expected +values were set by target driver and false otherwise. It is defined as +the following:

    +

    +

    +int scst_cmd_is_expected_set(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Function scst_cmd_get_expected_data_direction() returns expected +data direction set by target driver, if any. If this value was not set, +the return value is undefined. It is defined as the following:

    +

    +

    +scst_data_direction scst_cmd_get_expected_data_direction(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Function scst_cmd_get_expected_transfer_len() returns expected +transfer length set by target driver, if any. If this value was not set, +the return value is undefined. It is defined as the following:

    +

    +

    +unsigned int scst_cmd_get_expected_transfer_len(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    • expected_data_direction - expected data direction
    • +
    • expected_transfer_len - expected transfer length
    • +
    +

    + +

    scst_get_buf_first(), scst_get_buf_next(),scst_put_buf() and scst_get_buf_count()

    + +

    These functions are designed to simplify and unify access to the +commands data (SG vector or plain data buffer) in all possible +conditions, including HIGHMEM environment, and should be used instead of +direct access.

    +

    Function scst_get_buf_first() starts access to the data. It is defined +as the following:

    +

    +

    +int scst_get_buf_first(
    +        struct scst_cmd *cmd, 
    +        uint8_t **buf)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    • buf - pointer, where pointer to the first data chunk will be put
    • +
    +

    +

    Returns the length of the chunk of data for success, 0 for the end of +data, negative error code otherwise.

    +

    Function scst_get_buf_next() continues access to the data. It is defined +as the following:

    +

    +

    +int scst_get_buf_next(
    +        struct scst_cmd *cmd, 
    +        uint8_t **buf)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    • buf - pointer, where pointer to the next data chunk will be put
    • +
    +

    +

    Returns the length of the chunk of data for success, 0 for the end of +data, negative error code otherwise.

    +

    Function scst_put_buf() tells SCST that the user of the chunk of +data, returned by scst_get_buf_first() or scst_get_buf_next(), +finished accessing the data. This function must be called for all chunks +of data, returned by scst_get_buf_first() or +scst_get_buf_next(). It is defined as the following:

    +

    +

    +void scst_put_buf(
    +        struct scst_cmd *cmd, 
    +        uint8_t *buf)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    • buf - pointer to the data chunk
    • +
    +

    +

    Function scst_get_buf_count() returns the approximate higher +rounded count of data chunks that scst_get_buf_[first|next]() will +return. It is defined as the following:

    +

    +

    +int scst_get_buf_count(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - pointer to the command
    • +
    +

    + +

    12.4 SCST task management commands manipulation functions +

    + +

    scst_mgmt_cmd_get_tgt_specific()

    + +

    Function scst_mgmt_cmd_get_tgt_specific() returns pointer to the +target driver specific data, set on call of scst_rx_mgmt_fn_tag() +or scst_rx_mgmt_fn_lun(). It is defined as the following:

    +

    +

    +void *scst_mgmt_cmd_get_tgt_specific(
    +        struct scst_mgmt_cmd *mcmd)
    +
    +

    +

    Where:

    +

    +

      +
    • mcmd - pointer to the task management command
    • +
    +

    + +

    scst_mgmt_cmd_get_status()

    + +

    Functions scst_mgmt_cmd_get_status() returns task management +command's completion status. It is defined as the following:

    +

    +

    +void *scst_mgmt_cmd_get_status(
    +        struct scst_mgmt_cmd *mcmd)
    +
    +

    +

    Where:

    +

    +

      +
    • mcmd - pointer to the task management command
    • +
    +

    +

    The following status values are possible:

    +

    +

      +
    • SCST_MGMT_STATUS_SUCCESS - the task management command completed +successfully +
    • +
    • SCST_MGMT_STATUS_FAILED - the task management command failed. +
    • +
    +

    + +

    13. Miscellaneous functions

    + +

    13.1 scst_find_cmd_by_tag() +

    + +

    Function scst_find_cmd_by_tag() is designed to find SCST's command +based on the supplied tag comparing it with one that previously set by +scst_cmd_set_tag(). This value should be set by the target driver +on the command's initialization time.

    +

    It is defined as the following:

    +

    +

    +struct scst_cmd *scst_find_cmd_by_tag(
    +        struct scst_session *sess, 
    +        uint32_t tag)
    +
    +

    +

    Where:

    +

    +

      +
    • sess - session to which the command belongs
    • +
    • tag - the tag
    • +
    +

    +

    Returns found command or NULL otherwise.

    + +

    13.2 scst_find_cmd() +

    + +

    Function scst_find_cmd() is designed to find SCST's command. For example, +it can be used to find the command by internal serial number that was +supplied by a remote target's response.

    +

    It is defined as the following:

    +

    +

    +struct scst_cmd *scst_find_cmd(
    +        struct scst_session *sess, 
    +        void *data, 
    +        int (*cmp_fn)(struct scst_cmd *cmd, void *data))
    +
    +

    +

    Where:

    +

    +

      +
    • sess - session to which the command belongs +
    • +
    • data - comparison data that will be passed to cmp_fn() +as is +
    • +
    • cmp_fn - comparison callback function that will be called for +each the session's command. Should return true if the command is found, +false otherwise. Parameters: + +
        +
      • cmd - the command to compare
      • +
      • data - comparison data.
      • +
      + +
    • +
    +

    +

    Returns found command or NULL otherwise.

    +

    IMPORTANT

    +

    SCST is designed in a such way that any command is always processed only +by one thread at any time, so no locking is necessary. But there is one +exception from that rule, it is scst_find_cmd() function. Since it +calls the callback over all commands of the session in the internal +lists, despite of the command's current state, there is a race +possibility accessing to target specific data pointer between +scst_cmd_set_tgt_specific() caller and cmp_fn(), which usually +calls scst_cmd_get_tgt_specific() from the different context. The +only place, where it is safe to call scst_cmd_set_tgt_specific() +without the race probability, is between scst_rx_cmd() and +scst_cmd_init_done(). Thus, if you call +scst_cmd_set_tgt_specific() only there, there is nothing to worry, +always use the functions without "lock" suffix. Otherwise, be careful +and, if necessary, use "lock" functions. In addition, cmp_fn() is +allowed to use only target specific data and forbidden to call any +SCST's functions.

    + +

    13.3 scst_get_cdb_info() +

    + +

    Function scst_get_cdb_info() provides various CDB info. It is +defined as the following:

    +

    +

    +int scst_get_cdb_info(
    +        const uint8_t *cdb_p, 
    +        int dev_type, 
    +        struct scst_info_cdb *info_p)
    +
    +

    +

    Where:

    +

    +

      +
    • cdb_p - pointer to CDB +
    • +
    • dev_type - SCSI device type +
    • +
    • info_p - the result structure, see description in device +handler's parse() chapter +
    • +
    +

    +

    Returns 0 on success, -1 otherwise.

    + +

    13.4 scst_to_dma_dir() +

    + +

    Function scst_to_dma_dir() translates SCST's data direction to +DMA one. It is defined as the following:

    +

    +

    +int scst_to_dma_dir(
    +        int scst_dir)
    +
    +

    +

    Where:

    +

    +

      +
    • scst_dir - one of the SCST_DATA_* constants
    • +
    +

    +

    Returns the corresponding PCI_DMA_* constant.

    + +

    13.5 scst_is_cmd_local() +

    + +

    Function scst_is_cmd_local() checks if the command is handled +by SCST (i.e. locally, as, e.g., REPORT LUNS command). Intended to be +used in device handler's exec(), when the device handler wants to +perform all the commands, except ones that should be done by SCST +itself.

    +

    It is defined as the following:

    +

    +

    +int scst_is_cmd_local(
    +        struct scst_cmd *cmd)
    +
    +

    +

    Where:

    +

    +

      +
    • cmd - the command, which CDB should be checked
    • +
    +

    +

    Returns 1, if the command's CDB is locally handled by SCST or 0 +otherwise

    + +

    13.6 scst_register_virtual_device() and scst_unregister_virtual_device() +

    + +

    These functions provide a way for device handlers to register a virtual +(emulated) device, which will be visible only by remote initiators. For +example, FILEIO device handler uses files on file system to makes from +them virtual remotely available SCSI disks.

    +

    Function scst_register_virtual_device() registers a virtual device. +During the registration the device handlers functions init() and +attach() will be called, if defined. The function is defined as the +following:

    +

    +

    +int scst_register_virtual_device(
    +        struct scst_dev_type *dev_handler)
    +
    +

    +

    Where:

    +

    +

      +
    • dev_handler - device handler's descriptor
    • +
    +

    +

    Returns assigned to the device ID on success, or negative value otherwise.

    +

    Function scst_unregister_virtual_device() unregisters a virtual +device. During the unregistration the device handlers functions +detach() and release() will be called, if defined. The +function is defined as the following:

    +

    +

    +void scst_unregister_virtual_device(
    +        int id)
    +
    +

    +

    Where:

    +

    +

      +
    • id - the device's ID, returned by +scst_register_virtual_device()
    • +
    +

    + +

    13.7 scst_add_threads() and scst_del_threads() +

    + +

    These functions allows to add or delete some SCST threads. For example, +if exec() function in your device handler works synchronously, i.e. +wait for job's completion, in order to prevent performance loss you +can add for SCST as many threads as there are devices serviced by your +device handler.

    +

    Function scst_add_threads() starts requested number of threads. It +is defined as the following:

    +

    +

    +int scst_add_threads(
    +        int num)
    +
    +

    +

    Where:

    +

    +

      +
    • num - number of the threads to start
    • +
    +

    +

    Returns 0 on success, error code otherwise.

    +

    Function scst_del_threads() stops requested number of threads. It +is defined as the following:

    +

    +

    +void scst_del_threads(
    +        int num)
    +
    +

    +

    Where:

    +

    +

      +
    • num - number of the threads to stop
    • +
    +

    + +

    13.8 scst_proc_get_tgt_root() +

    + +

    Function scst_proc_get_tgt_root() returns target driver's root +entry in SCST's /proc hierarchy. The driver can create own +files/directories here, which should be deleted in the driver's +release(). It is defined as the following:

    +

    +

    +struct proc_dir_entry *scst_proc_get_tgt_root(
    +        struct scst_tgt_template *vtt)
    +
    +

    +

    Where:

    +

    +

      +
    • vtt - pointer to the driver's template
    • +
    +

    +

    Returns proc_dir_entry on success, NULL otherwise.

    + +

    13.9 scst_proc_get_dev_type_root() +

    + +

    Function scst_proc_get_dev_type_root() returns device handler's +root entry in SCST's /proc hierarchy. The driver can create own +files/directories here, which should be deleted in the driver's +detach() or release(). It is defined as the following:

    +

    +

    +struct proc_dir_entry *scst_proc_get_dev_type_root(
    +        struct scst_dev_type *dtt)
    +
    +

    +

    Where:

    +

    +

      +
    • dtt - pointer to the handler's description structure
    • +
    +

    +

    Returns proc_dir_entry on success, NULL otherwise.

    + + + diff --git a/www/scst_pg.pdf b/www/scst_pg.pdf index 9f5fb01a2..80e19cd44 100644 --- a/www/scst_pg.pdf +++ b/www/scst_pg.pdf @@ -882,23 +882,23 @@ endobj /ProcSet [ /PDF /Text ] >> endobj 379 0 obj << -/Length 3136 +/Length 3081 /Filter /FlateDecode >> stream -x\KFϯm5@c {qrH T<[ݔI%VO9ؤ_U?8t]7708[0Aby3]l JBj rǛyӅ#NsxwdHBZ,/]Y'o[m] nrޮزW?kۓ" 9B>qCcCcmlY$Wi'Hi#6'0 (R.55wn3e} 8=gݰfC KR΄Vw L Ki1|N9Ԩv 09SD1e.~L?>&26A˯L*e)un*R0Kd3,/#ڤh=Bل}&`e˽_OR@Ef (}1_Jn4/V?Ƙ>W\A'Jm|S^IC8Ke8"n6!N 3 9XQzw @!j!YH9E'Lpv}aBr`bi: 6bH+7ÿ뤅Gtr*u+hV j!bTL1Y:Ьr)T0KLy}՘B߰! ra9C^@ThRPfY*T^;KyYE >_d]c@P]NYlv% v\IPd$-ܹQ46xcp?@^i0\cL~~=w8NN%h -X+fkH: &d/qdLH >f?7N18Ĩy?;g&HHθM\n'K&KvRBHamQ !Cӝ,z]h zFr|wh{lʶ.M w\4'Ca *_5vT:[u8X#5zVZT6M̻.Xfb;JO) K\!tBʋJ-szNBMDu -LCSp#5PM!*"N>;: 0#K H~np^f|l^ y >м>df9J?{8TČv^9t 8hyYfU?`Ʒgw๒8)G*"'&x%怫fx8 8iUMM=zX3C%mqc@HÈyyq8D -X`tdžm=02 DgaQ`>\pټ >,wIwQsEkb;<;$Pq)0a7wSDω2<VD ~I'}y6=@fYo(~AHv?)]#b -Uq˦Z 2,%sn~{j+nAv~ &߫p="NW!zp۹]|Qq3祯:Y=xL"LQ{:M;ՆL Նj[QZ5-..m1CM['WNM30ذECXh<09 `$Z6Ew2?f/o6Նh̬faR4UU91xJ`umϋ+H"0;l&/cp.Qįl:/ gq9A#p{f % w?݇EI'&%Ԏ0;œ\)]ӂʇQafnĮRcFcS$e>>&T9S1-)0-$ '_Vv ѱסsF6j~1­Rɾ4rů^3B"er^+\2ZOb[;1Ão…61\جJct2\jwG}N+ί]6p6ySl|AሔoVlpP, w,aҧ fЉ#ﳵwo; ->.>nհòaEm݄ǎ5/*\S)٦-=Cx@X|o -9owB+s2|^in_Bj/Y{c><ߒ߭yԌU rF;VնΊ9& -ʇ^yӆ_15qx1k"b yACh0`=tO2q5qfevWE`΂Y47њ!yubQ% -YqF@Q -TTDgU#0xQƱ?e^#B?9ԆxUyqm,Qy-N8na-j*@FhmOmo^|(y)|zƾHI:M^YF83އ#w 2k=p̟!nfGVˏQ}-YpǻB=佖M|ZqѮ3 @ +6G$ fu]}s#]+#)ԭD:ڭpVWŗo8]94׫w7A$Tջ|㚑ˍurl5ˍpr徾ݷ_)R B/D4684ᛋFrEfp"6bs"""ZyQ~76SwPJӾA9q֍{?l6Hk$Lhv`Z,] +L+vstt&ng09@Z3E~_n]''(ccB:'-Z~;gzU)K\ʟ-!Th7([ J +s&/۹DH! $}nKiǩ62:bj>L 0u ,-!FPdPJ6Ɯe$I0gdj5@mEsh ZNX%/mYgQZoà.&Rg!HҁfYKq^dC!g]3 $4J)! +e]Mq"J%鵳D1nUԟTSE˳~ ˹"-.+ ,;54*xob^n"+ &kt/M;̹lj֩MRks R',*`F<Y~LhN11iavB1DG-ɳ R3.A$2cIĒI0^v;)!QT(ƐXq}]h zFr^}`m+BS~PeX…+7t`5.VzIu,s(-*HܦX CIi.Xff;JϏ) K\1|BʋJ-s~NBMDu +Lccp5RM!*"N8: 0#K H~iptl Cӡy6dCȶ˺C{,8px00vs&.qf]nboOww乒8)'*"G&x%怳fx8 84&ypt>,Tƙ1Ē1 aDC\\pٲ:!,wYw/~w)ywDIN'nGkS`WK3sDω2<VDM fI'}}6/z3"W!zp߹Uǩ]|Vq祯:YxL"LQ{-nn  IײY5j[] +\PcƸyk-fWM30ظECXh<9 `$Z6Dw2?f_l 1~8E1$¤i,?2xJavwMW$hǏl(/K)0+Nka:b3iYm4vIޥYAIB]MaL8"xdʼn` sI0#N0g*qJWG' 1fT6nߣŦ[2Ř&(IY/|Zϵ*o>mz3~on|'m s=*o"Msrf~2wއg[:j?:8APhrgJ`m2x<: W~v`!<} +К8àokpTrշQY.60`TqGUU4qR>z`:/. ܈gfp30>x4RewC= ۿj{|1 +@.%x@bвoQ 34Nw78vw!G\!h>azx 3l{4> endobj 610 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.3)/Keywords() -/CreationDate (D:20090518215821+04'00') -/ModDate (D:20090518215821+04'00') +/CreationDate (D:20090915153306+04'00') +/ModDate (D:20090915153306+04'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX using libpoppler, Version 3.141592-1.40.3-2.2 (Web2C 7.5.6) kpathsea version 3.5.6) >> endobj @@ -4351,288 +4351,288 @@ xref 0000000004 00000 f 0000000000 00000 f 0000000015 00000 n -0000028567 00000 n -0000372119 00000 n +0000028512 00000 n +0000372064 00000 n 0000000060 00000 n 0000000090 00000 n -0000031698 00000 n -0000372033 00000 n +0000031643 00000 n +0000371978 00000 n 0000000135 00000 n 0000000175 00000 n -0000034150 00000 n -0000371945 00000 n +0000034095 00000 n +0000371890 00000 n 0000000221 00000 n 0000000257 00000 n -0000034206 00000 n -0000371820 00000 n +0000034151 00000 n +0000371765 00000 n 0000000303 00000 n 0000000348 00000 n -0000034262 00000 n -0000371709 00000 n +0000034207 00000 n +0000371654 00000 n 0000000399 00000 n 0000000451 00000 n -0000110284 00000 n -0000371648 00000 n +0000110229 00000 n +0000371593 00000 n 0000000507 00000 n 0000000557 00000 n -0000110340 00000 n -0000371537 00000 n +0000110285 00000 n +0000371482 00000 n 0000000608 00000 n 0000000663 00000 n -0000110396 00000 n -0000371463 00000 n +0000110341 00000 n +0000371408 00000 n 0000000719 00000 n 0000000780 00000 n -0000111954 00000 n -0000371389 00000 n +0000111899 00000 n +0000371334 00000 n 0000000836 00000 n 0000000875 00000 n -0000112010 00000 n -0000371264 00000 n +0000111955 00000 n +0000371209 00000 n 0000000921 00000 n 0000000968 00000 n -0000112066 00000 n -0000371190 00000 n +0000112011 00000 n +0000371135 00000 n 0000001019 00000 n 0000001060 00000 n -0000112122 00000 n -0000371116 00000 n +0000112067 00000 n +0000371061 00000 n 0000001111 00000 n 0000001174 00000 n -0000115323 00000 n -0000371028 00000 n +0000115268 00000 n +0000370973 00000 n 0000001220 00000 n 0000001264 00000 n -0000187877 00000 n -0000370940 00000 n +0000187822 00000 n +0000370885 00000 n 0000001310 00000 n 0000001356 00000 n -0000187933 00000 n -0000370815 00000 n +0000187878 00000 n +0000370760 00000 n 0000001402 00000 n 0000001489 00000 n -0000191484 00000 n -0000370704 00000 n +0000191429 00000 n +0000370649 00000 n 0000001540 00000 n 0000001592 00000 n -0000191540 00000 n -0000370630 00000 n +0000191485 00000 n +0000370575 00000 n 0000001648 00000 n 0000001688 00000 n -0000237425 00000 n -0000370543 00000 n +0000237370 00000 n +0000370488 00000 n 0000001744 00000 n 0000001794 00000 n -0000237480 00000 n -0000370456 00000 n +0000237425 00000 n +0000370401 00000 n 0000001850 00000 n 0000001891 00000 n -0000240999 00000 n -0000370382 00000 n +0000240944 00000 n +0000370327 00000 n 0000001947 00000 n 0000001996 00000 n -0000241055 00000 n -0000370271 00000 n +0000241000 00000 n +0000370216 00000 n 0000002047 00000 n 0000002097 00000 n -0000244895 00000 n -0000370210 00000 n +0000244840 00000 n +0000370155 00000 n 0000002153 00000 n 0000002199 00000 n -0000244951 00000 n -0000370083 00000 n +0000244896 00000 n +0000370028 00000 n 0000002245 00000 n 0000002289 00000 n -0000245007 00000 n -0000370007 00000 n +0000244952 00000 n +0000369952 00000 n 0000002340 00000 n 0000002395 00000 n -0000247173 00000 n -0000369930 00000 n +0000247118 00000 n +0000369875 00000 n 0000002447 00000 n 0000002502 00000 n -0000250190 00000 n -0000369799 00000 n +0000250135 00000 n +0000369744 00000 n 0000002550 00000 n 0000002613 00000 n -0000250247 00000 n -0000369681 00000 n +0000250192 00000 n +0000369626 00000 n 0000002666 00000 n 0000002721 00000 n -0000250304 00000 n -0000369602 00000 n +0000250249 00000 n +0000369547 00000 n 0000002779 00000 n 0000002836 00000 n -0000252098 00000 n -0000369523 00000 n +0000252043 00000 n +0000369468 00000 n 0000002894 00000 n 0000002943 00000 n -0000263576 00000 n -0000369444 00000 n +0000263521 00000 n +0000369389 00000 n 0000002996 00000 n 0000003053 00000 n -0000263633 00000 n -0000369351 00000 n +0000263578 00000 n +0000369296 00000 n 0000003101 00000 n 0000003142 00000 n -0000266468 00000 n -0000369219 00000 n +0000266413 00000 n +0000369164 00000 n 0000003190 00000 n 0000003250 00000 n -0000266525 00000 n -0000369101 00000 n +0000266470 00000 n +0000369046 00000 n 0000003303 00000 n 0000003364 00000 n -0000266582 00000 n -0000369036 00000 n +0000266527 00000 n +0000368981 00000 n 0000003422 00000 n 0000003529 00000 n -0000266639 00000 n -0000368904 00000 n +0000266584 00000 n +0000368849 00000 n 0000003582 00000 n 0000003637 00000 n -0000266696 00000 n -0000368839 00000 n +0000266641 00000 n +0000368784 00000 n 0000003695 00000 n 0000003804 00000 n -0000268325 00000 n -0000368706 00000 n +0000268270 00000 n +0000368651 00000 n 0000003857 00000 n 0000003912 00000 n -0000268382 00000 n -0000368627 00000 n +0000268327 00000 n +0000368572 00000 n 0000003970 00000 n 0000004015 00000 n -0000268438 00000 n -0000368534 00000 n +0000268383 00000 n +0000368479 00000 n 0000004073 00000 n 0000004126 00000 n -0000270453 00000 n -0000368441 00000 n +0000270398 00000 n +0000368386 00000 n 0000004184 00000 n 0000004249 00000 n -0000270510 00000 n -0000368348 00000 n +0000270455 00000 n +0000368293 00000 n 0000004307 00000 n 0000004373 00000 n -0000270567 00000 n -0000368255 00000 n +0000270512 00000 n +0000368200 00000 n 0000004431 00000 n 0000004483 00000 n -0000270624 00000 n -0000368162 00000 n +0000270569 00000 n +0000368107 00000 n 0000004541 00000 n 0000004594 00000 n -0000272552 00000 n -0000368069 00000 n +0000272497 00000 n +0000368014 00000 n 0000004652 00000 n 0000004707 00000 n -0000272609 00000 n -0000367976 00000 n +0000272554 00000 n +0000367921 00000 n 0000004765 00000 n 0000004828 00000 n -0000272666 00000 n -0000367883 00000 n +0000272611 00000 n +0000367828 00000 n 0000004886 00000 n 0000004938 00000 n -0000274393 00000 n -0000367790 00000 n +0000274338 00000 n +0000367735 00000 n 0000004997 00000 n 0000005059 00000 n -0000274450 00000 n -0000367697 00000 n +0000274395 00000 n +0000367642 00000 n 0000005118 00000 n 0000005177 00000 n -0000274507 00000 n -0000367604 00000 n +0000274452 00000 n +0000367549 00000 n 0000005236 00000 n 0000005296 00000 n -0000274564 00000 n -0000367511 00000 n +0000274509 00000 n +0000367456 00000 n 0000005355 00000 n 0000005417 00000 n -0000276354 00000 n -0000367418 00000 n +0000276299 00000 n +0000367363 00000 n 0000005476 00000 n 0000005537 00000 n -0000276411 00000 n -0000367325 00000 n +0000276356 00000 n +0000367270 00000 n 0000005596 00000 n 0000005664 00000 n -0000276468 00000 n -0000367232 00000 n +0000276413 00000 n +0000367177 00000 n 0000005723 00000 n 0000005806 00000 n -0000279372 00000 n -0000367139 00000 n +0000279317 00000 n +0000367084 00000 n 0000005865 00000 n 0000005980 00000 n -0000279429 00000 n -0000367046 00000 n +0000279374 00000 n +0000366991 00000 n 0000006039 00000 n 0000006154 00000 n -0000279486 00000 n -0000366953 00000 n +0000279431 00000 n +0000366898 00000 n 0000006213 00000 n 0000006336 00000 n -0000281989 00000 n -0000366860 00000 n +0000281934 00000 n +0000366805 00000 n 0000006395 00000 n 0000006606 00000 n -0000284309 00000 n -0000366781 00000 n +0000284254 00000 n +0000366726 00000 n 0000006665 00000 n 0000006808 00000 n -0000286634 00000 n -0000366663 00000 n +0000286579 00000 n +0000366608 00000 n 0000006861 00000 n 0000006933 00000 n -0000286691 00000 n -0000366584 00000 n +0000286636 00000 n +0000366529 00000 n 0000006991 00000 n 0000007060 00000 n -0000286748 00000 n -0000366505 00000 n +0000286693 00000 n +0000366450 00000 n 0000007118 00000 n 0000007178 00000 n -0000286805 00000 n -0000366387 00000 n +0000286750 00000 n +0000366332 00000 n 0000007226 00000 n 0000007269 00000 n -0000286861 00000 n -0000366308 00000 n +0000286806 00000 n +0000366253 00000 n 0000007322 00000 n 0000007378 00000 n -0000289831 00000 n -0000366215 00000 n +0000289776 00000 n +0000366160 00000 n 0000007431 00000 n 0000007474 00000 n -0000292002 00000 n -0000366122 00000 n +0000291947 00000 n +0000366067 00000 n 0000007527 00000 n 0000007577 00000 n -0000292059 00000 n -0000366029 00000 n +0000292004 00000 n +0000365974 00000 n 0000007630 00000 n 0000007678 00000 n -0000292116 00000 n -0000365936 00000 n +0000292061 00000 n +0000365881 00000 n 0000007731 00000 n 0000007781 00000 n -0000294597 00000 n -0000365843 00000 n +0000294542 00000 n +0000365788 00000 n 0000007834 00000 n 0000007943 00000 n -0000294654 00000 n -0000365750 00000 n +0000294599 00000 n +0000365695 00000 n 0000007996 00000 n 0000008073 00000 n -0000296472 00000 n -0000365657 00000 n +0000296417 00000 n +0000365602 00000 n 0000008126 00000 n 0000008184 00000 n -0000296529 00000 n -0000365578 00000 n +0000296474 00000 n +0000365523 00000 n 0000008237 00000 n 0000008303 00000 n 0000010031 00000 n @@ -4663,13 +4663,13 @@ xref 0000008355 00000 n 0000013763 00000 n 0000013820 00000 n -0000364010 00000 n -0000363723 00000 n -0000363866 00000 n -0000363436 00000 n +0000363955 00000 n +0000363668 00000 n +0000363811 00000 n +0000363381 00000 n 0000013877 00000 n -0000363292 00000 n -0000364442 00000 n +0000363237 00000 n +0000364387 00000 n 0000017049 00000 n 0000017205 00000 n 0000017361 00000 n @@ -4701,266 +4701,266 @@ xref 0000021535 00000 n 0000021699 00000 n 0000021863 00000 n -0000025651 00000 n +0000025596 00000 n 0000022082 00000 n 0000016518 00000 n 0000014056 00000 n 0000022025 00000 n -0000025813 00000 n -0000025977 00000 n -0000026304 00000 n -0000026467 00000 n -0000026623 00000 n -0000026784 00000 n -0000026947 00000 n -0000027099 00000 n -0000027256 00000 n -0000027413 00000 n -0000027570 00000 n -0000027726 00000 n -0000027883 00000 n -0000028040 00000 n -0000028197 00000 n -0000028353 00000 n -0000028622 00000 n -0000025383 00000 n +0000025758 00000 n +0000025922 00000 n +0000026249 00000 n +0000026412 00000 n +0000026568 00000 n +0000026729 00000 n +0000026892 00000 n +0000027044 00000 n +0000027201 00000 n +0000027358 00000 n +0000027515 00000 n +0000027671 00000 n +0000027828 00000 n +0000027985 00000 n +0000028142 00000 n +0000028298 00000 n +0000028567 00000 n +0000025328 00000 n 0000022166 00000 n -0000028510 00000 n -0000026141 00000 n -0000364154 00000 n -0000031469 00000 n -0000031754 00000 n -0000031337 00000 n -0000028732 00000 n -0000031641 00000 n -0000364298 00000 n -0000034937 00000 n -0000034318 00000 n -0000033981 00000 n -0000031877 00000 n -0000034093 00000 n -0000098094 00000 n -0000034825 00000 n -0000034428 00000 n -0000097976 00000 n -0000098033 00000 n -0000100765 00000 n -0000100596 00000 n -0000098214 00000 n -0000100708 00000 n -0000363580 00000 n -0000364559 00000 n -0000105776 00000 n -0000105436 00000 n -0000100888 00000 n -0000105548 00000 n -0000105605 00000 n -0000105662 00000 n -0000105719 00000 n -0000110450 00000 n -0000110115 00000 n -0000105873 00000 n -0000110227 00000 n -0000112178 00000 n -0000111785 00000 n -0000110560 00000 n -0000111897 00000 n -0000115987 00000 n -0000145187 00000 n -0000115379 00000 n -0000115154 00000 n -0000112301 00000 n -0000115266 00000 n -0000144583 00000 n -0000115875 00000 n -0000115502 00000 n -0000144464 00000 n -0000144521 00000 n -0000184878 00000 n -0000145075 00000 n -0000144703 00000 n -0000184760 00000 n -0000184817 00000 n -0000364676 00000 n -0000187989 00000 n -0000187708 00000 n -0000184998 00000 n -0000187820 00000 n -0000192225 00000 n -0000191596 00000 n -0000191315 00000 n -0000188112 00000 n -0000191427 00000 n -0000235335 00000 n -0000192113 00000 n -0000191719 00000 n -0000235216 00000 n -0000235273 00000 n -0000237536 00000 n -0000237256 00000 n -0000235455 00000 n -0000237368 00000 n -0000241337 00000 n -0000240830 00000 n -0000237646 00000 n -0000240942 00000 n -0000241110 00000 n -0000241166 00000 n -0000241223 00000 n -0000241280 00000 n -0000245063 00000 n -0000244726 00000 n -0000241460 00000 n -0000244838 00000 n -0000364793 00000 n -0000247230 00000 n -0000247004 00000 n -0000245173 00000 n -0000247116 00000 n -0000250361 00000 n -0000250021 00000 n -0000247353 00000 n -0000250133 00000 n -0000252155 00000 n -0000251929 00000 n -0000250484 00000 n -0000252041 00000 n -0000257290 00000 n -0000257121 00000 n -0000252265 00000 n -0000257233 00000 n -0000260673 00000 n -0000260333 00000 n -0000257400 00000 n -0000260445 00000 n -0000260502 00000 n -0000260559 00000 n -0000260616 00000 n -0000263689 00000 n -0000263407 00000 n -0000260783 00000 n -0000263519 00000 n -0000364910 00000 n -0000266753 00000 n -0000266299 00000 n -0000263825 00000 n -0000266411 00000 n -0000268495 00000 n -0000268156 00000 n -0000266876 00000 n -0000268268 00000 n -0000270681 00000 n -0000270284 00000 n -0000268618 00000 n -0000270396 00000 n -0000272722 00000 n -0000272383 00000 n -0000270791 00000 n -0000272495 00000 n -0000274621 00000 n -0000274224 00000 n -0000272832 00000 n -0000274336 00000 n -0000276524 00000 n -0000276185 00000 n -0000274731 00000 n -0000276297 00000 n -0000365027 00000 n -0000279542 00000 n -0000279203 00000 n -0000276634 00000 n -0000279315 00000 n -0000282046 00000 n -0000281820 00000 n -0000279652 00000 n -0000281932 00000 n -0000284366 00000 n -0000284140 00000 n -0000282156 00000 n -0000284252 00000 n -0000286917 00000 n -0000286465 00000 n -0000284476 00000 n -0000286577 00000 n -0000289888 00000 n -0000289662 00000 n -0000287040 00000 n -0000289774 00000 n -0000292172 00000 n -0000291833 00000 n -0000290011 00000 n -0000291945 00000 n -0000365144 00000 n -0000294711 00000 n -0000294428 00000 n -0000292295 00000 n -0000294540 00000 n -0000296586 00000 n -0000296303 00000 n -0000294834 00000 n -0000296415 00000 n -0000296709 00000 n -0000296734 00000 n -0000297097 00000 n -0000297632 00000 n -0000298275 00000 n -0000298912 00000 n -0000299389 00000 n -0000300012 00000 n -0000300351 00000 n -0000314920 00000 n -0000315392 00000 n -0000324023 00000 n -0000324394 00000 n -0000326070 00000 n -0000326298 00000 n -0000342338 00000 n -0000342818 00000 n -0000347804 00000 n -0000348125 00000 n -0000352153 00000 n -0000352414 00000 n -0000353703 00000 n -0000353934 00000 n -0000362864 00000 n -0000365237 00000 n -0000365355 00000 n -0000365432 00000 n -0000365502 00000 n -0000372191 00000 n -0000372360 00000 n -0000372526 00000 n -0000372699 00000 n -0000372869 00000 n -0000373038 00000 n -0000373208 00000 n -0000373377 00000 n -0000373547 00000 n -0000373709 00000 n -0000373900 00000 n -0000374080 00000 n -0000374294 00000 n -0000374528 00000 n -0000374759 00000 n -0000374979 00000 n -0000375241 00000 n -0000375523 00000 n -0000375804 00000 n -0000376080 00000 n -0000376349 00000 n -0000376609 00000 n -0000376720 00000 n -0000376837 00000 n -0000376968 00000 n -0000377079 00000 n -0000377186 00000 n -0000377224 00000 n -0000377352 00000 n +0000028455 00000 n +0000026086 00000 n +0000364099 00000 n +0000031414 00000 n +0000031699 00000 n +0000031282 00000 n +0000028677 00000 n +0000031586 00000 n +0000364243 00000 n +0000034882 00000 n +0000034263 00000 n +0000033926 00000 n +0000031822 00000 n +0000034038 00000 n +0000098039 00000 n +0000034770 00000 n +0000034373 00000 n +0000097921 00000 n +0000097978 00000 n +0000100710 00000 n +0000100541 00000 n +0000098159 00000 n +0000100653 00000 n +0000363525 00000 n +0000364504 00000 n +0000105721 00000 n +0000105381 00000 n +0000100833 00000 n +0000105493 00000 n +0000105550 00000 n +0000105607 00000 n +0000105664 00000 n +0000110395 00000 n +0000110060 00000 n +0000105818 00000 n +0000110172 00000 n +0000112123 00000 n +0000111730 00000 n +0000110505 00000 n +0000111842 00000 n +0000115932 00000 n +0000145132 00000 n +0000115324 00000 n +0000115099 00000 n +0000112246 00000 n +0000115211 00000 n +0000144528 00000 n +0000115820 00000 n +0000115447 00000 n +0000144409 00000 n +0000144466 00000 n +0000184823 00000 n +0000145020 00000 n +0000144648 00000 n +0000184705 00000 n +0000184762 00000 n +0000364621 00000 n +0000187934 00000 n +0000187653 00000 n +0000184943 00000 n +0000187765 00000 n +0000192170 00000 n +0000191541 00000 n +0000191260 00000 n +0000188057 00000 n +0000191372 00000 n +0000235280 00000 n +0000192058 00000 n +0000191664 00000 n +0000235161 00000 n +0000235218 00000 n +0000237481 00000 n +0000237201 00000 n +0000235400 00000 n +0000237313 00000 n +0000241282 00000 n +0000240775 00000 n +0000237591 00000 n +0000240887 00000 n +0000241055 00000 n +0000241111 00000 n +0000241168 00000 n +0000241225 00000 n +0000245008 00000 n +0000244671 00000 n +0000241405 00000 n +0000244783 00000 n +0000364738 00000 n +0000247175 00000 n +0000246949 00000 n +0000245118 00000 n +0000247061 00000 n +0000250306 00000 n +0000249966 00000 n +0000247298 00000 n +0000250078 00000 n +0000252100 00000 n +0000251874 00000 n +0000250429 00000 n +0000251986 00000 n +0000257235 00000 n +0000257066 00000 n +0000252210 00000 n +0000257178 00000 n +0000260618 00000 n +0000260278 00000 n +0000257345 00000 n +0000260390 00000 n +0000260447 00000 n +0000260504 00000 n +0000260561 00000 n +0000263634 00000 n +0000263352 00000 n +0000260728 00000 n +0000263464 00000 n +0000364855 00000 n +0000266698 00000 n +0000266244 00000 n +0000263770 00000 n +0000266356 00000 n +0000268440 00000 n +0000268101 00000 n +0000266821 00000 n +0000268213 00000 n +0000270626 00000 n +0000270229 00000 n +0000268563 00000 n +0000270341 00000 n +0000272667 00000 n +0000272328 00000 n +0000270736 00000 n +0000272440 00000 n +0000274566 00000 n +0000274169 00000 n +0000272777 00000 n +0000274281 00000 n +0000276469 00000 n +0000276130 00000 n +0000274676 00000 n +0000276242 00000 n +0000364972 00000 n +0000279487 00000 n +0000279148 00000 n +0000276579 00000 n +0000279260 00000 n +0000281991 00000 n +0000281765 00000 n +0000279597 00000 n +0000281877 00000 n +0000284311 00000 n +0000284085 00000 n +0000282101 00000 n +0000284197 00000 n +0000286862 00000 n +0000286410 00000 n +0000284421 00000 n +0000286522 00000 n +0000289833 00000 n +0000289607 00000 n +0000286985 00000 n +0000289719 00000 n +0000292117 00000 n +0000291778 00000 n +0000289956 00000 n +0000291890 00000 n +0000365089 00000 n +0000294656 00000 n +0000294373 00000 n +0000292240 00000 n +0000294485 00000 n +0000296531 00000 n +0000296248 00000 n +0000294779 00000 n +0000296360 00000 n +0000296654 00000 n +0000296679 00000 n +0000297042 00000 n +0000297577 00000 n +0000298220 00000 n +0000298857 00000 n +0000299334 00000 n +0000299957 00000 n +0000300296 00000 n +0000314865 00000 n +0000315337 00000 n +0000323968 00000 n +0000324339 00000 n +0000326015 00000 n +0000326243 00000 n +0000342283 00000 n +0000342763 00000 n +0000347749 00000 n +0000348070 00000 n +0000352098 00000 n +0000352359 00000 n +0000353648 00000 n +0000353879 00000 n +0000362809 00000 n +0000365182 00000 n +0000365300 00000 n +0000365377 00000 n +0000365447 00000 n +0000372136 00000 n +0000372305 00000 n +0000372471 00000 n +0000372644 00000 n +0000372814 00000 n +0000372983 00000 n +0000373153 00000 n +0000373322 00000 n +0000373492 00000 n +0000373654 00000 n +0000373845 00000 n +0000374025 00000 n +0000374239 00000 n +0000374473 00000 n +0000374704 00000 n +0000374924 00000 n +0000375186 00000 n +0000375468 00000 n +0000375749 00000 n +0000376025 00000 n +0000376294 00000 n +0000376554 00000 n +0000376665 00000 n +0000376782 00000 n +0000376913 00000 n +0000377024 00000 n +0000377131 00000 n +0000377169 00000 n +0000377297 00000 n trailer << /Size 611 /Root 609 0 R /Info 610 0 R -/ID [<231C092BE069ADFEE72134ECF23EACA4> <231C092BE069ADFEE72134ECF23EACA4>] >> +/ID [ ] >> startxref -377683 +377628 %%EOF diff --git a/www/target_emulex.html b/www/target_emulex.html index 3fdd274d7..e2bc61aae 100644 --- a/www/target_emulex.html +++ b/www/target_emulex.html @@ -35,7 +35,7 @@

    Target Drivers

    • iSCSI-SCST
    • -
    • QLogic FC 22xx/23xx
    • +
    • QLogic FC QLA2X00T
    • QLogic FC ISP
    • SCSI RDMA Protocol (SRP)
    • Marvell SAS adapters
    • diff --git a/www/target_fcoe.html b/www/target_fcoe.html index 02de41892..3dd31aeae 100644 --- a/www/target_fcoe.html +++ b/www/target_fcoe.html @@ -35,7 +35,7 @@

      Target Drivers

      • iSCSI-SCST
      • -
      • QLogic FC 22xx/23xx
      • +
      • QLogic FC QLA2X00T
      • QLogic FC ISP
      • SCSI RDMA Protocol (SRP)
      • Marvell SAS adapters
      • diff --git a/www/target_iscsi.html b/www/target_iscsi.html index 05be28e72..8c0a8dccf 100644 --- a/www/target_iscsi.html +++ b/www/target_iscsi.html @@ -35,7 +35,7 @@

        Target Drivers

        • iSCSI-SCST
        • -
        • QLogic FC 22xx/23xx
        • +
        • QLogic FC QLA2X00T
        • QLogic FC ISP
        • SCSI RDMA Protocol (SRP)
        • Marvell SAS adapters
        • diff --git a/www/target_local.html b/www/target_local.html index 4619443b7..e25b9b7ba 100644 --- a/www/target_local.html +++ b/www/target_local.html @@ -35,7 +35,7 @@

          Target Drivers

          • iSCSI-SCST
          • -
          • QLogic FC 22xx/23xx
          • +
          • QLogic FC QLA2X00T
          • QLogic FC ISP
          • SCSI RDMA Protocol (SRP)
          • Marvell SAS adapters
          • diff --git a/www/target_lsi.html b/www/target_lsi.html index 8f71bf9de..a3f23352d 100644 --- a/www/target_lsi.html +++ b/www/target_lsi.html @@ -35,7 +35,7 @@

            Target Drivers

            • iSCSI-SCST
            • -
            • QLogic FC 22xx/23xx
            • +
            • QLogic FC QLA2X00T
            • QLogic FC ISP
            • SCSI RDMA Protocol (SRP)
            • Marvell SAS adapters
            • diff --git a/www/target_mvsas.html b/www/target_mvsas.html index 49d121fe1..55765d4e7 100644 --- a/www/target_mvsas.html +++ b/www/target_mvsas.html @@ -35,7 +35,7 @@

              Target Drivers

              • iSCSI-SCST
              • -
              • QLogic FC 22xx/23xx
              • +
              • QLogic FC QLA2X00T
              • QLogic FC ISP
              • SCSI RDMA Protocol (SRP)
              • Marvell SAS adapters
              • diff --git a/www/target_old.html b/www/target_old.html index 03170d06d..d469afbf3 100644 --- a/www/target_old.html +++ b/www/target_old.html @@ -35,7 +35,7 @@

                Target Drivers

                • iSCSI-SCST
                • -
                • QLogic FC 22xx/23xx
                • +
                • QLogic FC QLA2X00T
                • QLogic FC ISP
                • SCSI RDMA Protocol (SRP)
                • Marvell SAS adapters
                • @@ -53,9 +53,9 @@
                  -

                  Old target driver for QLogic 22xx/23xx adapters for 2.4 kernels

                  +

                  Old target driver for QLogic qla2x00t adapters for 2.4 kernels

                  - Old target driver for QLogic 22xx/23xx adapters is capable to work on 2.4 kernels. + Old target driver for QLogic qla2x00t adapters is capable to work on 2.4 kernels. It has all required features and looks to be quite stable. It is designed to work in conjunction with the initiator driver, which is intended to perform all the initialization and shutdown tasks. In the current release as a base for the initiator driver was taken Red Hat's driver from the stock 2.4.20 kernel. Then it was patched to diff --git a/www/target_qla22xx_23xx.html b/www/target_qla2x00t.html similarity index 82% rename from www/target_qla22xx_23xx.html rename to www/target_qla2x00t.html index 9aed46f4b..7a96be337 100644 --- a/www/target_qla22xx_23xx.html +++ b/www/target_qla2x00t.html @@ -35,7 +35,7 @@

                  Target Drivers

                  -

                  Target driver for QLogic 22xx/23xx adapters

                  +

                  Target driver qla2x00t for QLogic FC adapters

                  - This is target driver for QLogic 22xx/23xx (qla2200/qla2300) Fibre Channel adapters. It is stable and well tested.

                  + This is target driver for QLogic qla2xxx (qla22xx/qla23xx) Fibre Channel adapters. It is stable and well tested.

                  The latest stable version is 1.0.1.1. Requires Linux kernel version 2.6.16.x or higher and SCST version 1.0.1.1 or higher.

                  -

                  You can find the latest development version of this driver in the SCST SVN. See the download page how to setup +

                  The latest development version is 2.0.0. It additionally has support for 24xx/25xx (4/8 Gbps) adapters + as well as several other important improvements, thanks to ID7. + Requires Linux kernel version 2.6.16.x or higher and SCST version 1.0.2 or higher. + You can download it from SCST SVN repository. See the download page how to setup access to it.

                  +

                  This driver is mainline Linux kernel ready and after cosmetic cleanups going to be pushed to it + together with other SCST patches.

                  +

                  Target Drivers

                  • iSCSI-SCST
                  • -
                  • QLogic FC 22xx/23xx
                  • +
                  • QLogic FC QLA2X00T
                  • QLogic FC ISP
                  • SCSI RDMA Protocol (SRP)
                  • Marvell SAS adapters
                  • @@ -61,9 +61,9 @@ http://www.feral.com/isp.html. The update for SCST was made by Stanislaw Gruszka for Open-E Inc.

                    -

                    This driver operates on the same hardware as the qla2x00t target driver, but has one notable - advantage over it: support of 24xx (4Gbps) and 25xx (8Gbps) series of QLogic adapters. - From other side, qla2x00t is simpler, smaller and much better tested on 22xx and 23xx, hence perform more +

                    This driver operates on the same hardware as the qla2x00t target driver, but, comparing to version 1.0.1.1 + of it, has two notable advantages: support of 24xx (4Gbps) and 25xx (8Gbps) series of QLogic adapters as well + as NPIV support. From other side, qla2x00t is simpler, smaller and much better tested on 22xx and 23xx, hence perform more reliable and, thus, is recommended for these adapters.

                    The latest release is 1.0.1. It does not support 25xx chipsets yet and can be compiled with Linux diff --git a/www/target_srp.html b/www/target_srp.html index a768b3e62..7626d63f4 100644 --- a/www/target_srp.html +++ b/www/target_srp.html @@ -35,7 +35,7 @@

                    Target Drivers