Support for 24xx+ QLA chips added to qla2x00t. So far only for 2.6.26+ kernels.
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 17 Sep 2009 17:44:07 +0000 (17:44 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 17 Sep 2009 17:44:07 +0000 (17:44 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1104 d57e44dd-8a1f-0410-8b47-8ef2f437770f

43 files changed:
doc/scst_pg.sgml
qla2x00t/Kconfig
qla2x00t/Makefile
qla2x00t/qla2x00-target/ChangeLog
qla2x00t/qla2x00-target/Kconfig
qla2x00t/qla2x00-target/Makefile
qla2x00t/qla2x00-target/README
qla2x00t/qla2x00-target/ToDo
qla2x00t/qla2x00-target/V2_RELEASE_NOTES [new file with mode: 0644]
qla2x00t/qla2x00-target/qla2x00t.c
qla2x00t/qla2x00-target/qla2x00t.h
qla2x00t/qla2x_tgt.h
qla2x00t/qla2x_tgt_def.h
qla2x00t/qla_attr.c
qla2x00t/qla_dbg.c
qla2x00t/qla_def.h
qla2x00t/qla_gbl.h
qla2x00t/qla_gs.c
qla2x00t/qla_init.c
qla2x00t/qla_inline.h
qla2x00t/qla_iocb.c
qla2x00t/qla_isr.c
qla2x00t/qla_mbx.c
qla2x00t/qla_mid.c
qla2x00t/qla_os.c
qla2x00t/qla_sup.c
qla2x00t/qla_version.h
www/handler_fileio_tgt.html
www/index.html
www/scst_admin.html
www/scst_pg.html
www/scst_pg.pdf
www/target_emulex.html
www/target_fcoe.html
www/target_iscsi.html
www/target_local.html
www/target_lsi.html
www/target_mvsas.html
www/target_old.html
www/target_qla2x00t.html [moved from www/target_qla22xx_23xx.html with 82% similarity]
www/target_qla_isp.html
www/target_srp.html
www/targets.html

index ee19cd6..e3e899b 100644 (file)
@@ -39,10 +39,8 @@ required.
 
 <item> 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.
 
 <item> Performs all required pre- and post- processing of incoming
 requests and all necessary error recovery functionality. 
index e4c991f..d48abfe 100644 (file)
@@ -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.
index 9d13c6a..2d9fefe 100644 (file)
@@ -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
index f93a5a2..aae7fd4 100644 (file)
@@ -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
 ---------------------------------------------------
 
index 76fceca..856ea60 100644 (file)
@@ -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".
index 416649e..b5c351d 100644 (file)
@@ -2,7 +2,9 @@
 #  Qlogic 2x00 SCSI target driver makefile
 #  
 #  Copyright (C) 2004 - 2009 Vladislav Bolkhovitin <vst@vlnb.net>
+#  Copyright (C) 2004 - 2009 Vladislav Bolkhovitin <vst@vlnb.net>
 #  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),)
index 8cdfef7..a2fa70f 100644 (file)
@@ -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.
-
-At first, make sure that the link "/lib/modules/`you_kernel_version`/build" 
-points to the source code for your currently running kernel.
+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.
+
+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 <nate@misrule.us> for porting to new 2.6 kernel
 initiator driver.
index db19eb0..97d152c 100644 (file)
@@ -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 <X the tape skips all blocks with size X
    until the next correct block or filemark found, instead of returning
    ILI with negative counter. Looks like the initiator retries the
-   command quetly. 2200 works correctly. With the latest firmware that
+   command quetly. 22xx works correctly. With the latest firmware that
    might be fixed.
diff --git a/qla2x00t/qla2x00-target/V2_RELEASE_NOTES b/qla2x00t/qla2x00-target/V2_RELEASE_NOTES
new file mode 100644 (file)
index 0000000..083bac3
--- /dev/null
@@ -0,0 +1,29 @@
+Throughout the last three years ID7 and we have been working on an 
+enterprise class Fibre Channel target based on the QLogic range of 4/8Gb 
+HBA's. This has been solely funded by ID7 over the last years however 
+given the positive contributions by many of the SCST community both 
+personal and commercial, ID7 have chosen to merge with existing QLogic's 
+open source driver qla2x00t and introduce the driver under GPL license 
+for community use.
+
+This driver was designed from ground up to perform at the highest of 
+levels and interoperate with leading infrastructures, accordingly the 
+design and flow is much more focused and structured whilst the stability 
+is as you would expect at the Enterprise level.
+
+Although there is a current QLogic target driver qla_isp, this driver 
+has the key advantage that it was designed to be as simple as possible. 
+As the result, this driver is a lot smaller, cleaner and mainline Linux 
+kernel ready. It's going to be pushed for the in-kernel inclusion 
+together other SCST patches.
+
+Another advantage is that this driver fully supports FC tapes, including 
+transport level retries on data delivery problems.
+
+Once again we are grateful for all the positive work and contributions 
+and hopeful that this target helps drive SCST further forward as an 
+Enterprise class target. We encourage people to introduce and evaluate 
+this target driver.
+
+Vladislav Bolkhovitin,
+Mark Klarzynski, ID7 Ltd. (http://www.id-7.com)
index 87ecb8d..a0dcd8c 100644 (file)
@@ -4,14 +4,15 @@
  *  Copyright (C) 2004 - 2009 Vladislav Bolkhovitin <vst@vlnb.net>
  *  Copyright (C) 2004 - 2005 Leonid Stoljar
  *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
+ *  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
 
 #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
  */
 
-#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;
+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 tgt_template = {
-       .name                   = "qla2x00tgt",
-       .sg_tablesize           = 0,
-       .use_clustering         = 1,
+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,
+       .xmit_response_atomic = 0,
+       .rdy_to_xfer_atomic = 0,
 #else
-       .xmit_response_atomic   = 1,
-       .rdy_to_xfer_atomic     = 1,
+       .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
-       .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,
+       .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 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_exec_queue(scsi_qla_host_t *ha)
+static inline void q2t_sess_get(struct q2t_sess *sess)
 {
-       tgt_data.isp_cmd(ha);
+       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 void q2t_modify_command_count(scsi_qla_host_t *ha, int cmd_count,
-       int imm_count)
+static inline void q2t_sess_put(struct q2t_sess *sess)
 {
-       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++;
+       TRACE_DBG("sess %p, new sess_ref %d", sess, sess->sess_ref-1);
+       sBUG_ON(sess->sess_ref == 0);
 
-       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;
-       }
+       sess->sess_ref--;
+       if (sess->sess_ref == 0)
+               q2t_unreg_sess(sess);
+}
 
-       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;
+/* 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;
        }
-
-       pkt->timeout = 0;       /* Use default */
-       q2t_exec_queue(ha);
-
-       TRACE_EXIT();
-       return;
+       return NULL;
 }
 
-/* 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)
+/* 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 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;
+       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;
+}
 
-       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);
+/* 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;
+}
 
-       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)
+static inline void q2t_exec_queue(scsi_qla_host_t *ha)
 {
-       __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);
+       tgt_data.isp_cmd(ha);
 }
 
-/*
- * register with initiator driver (but target mode isn't enabled till
+/* Might release hw lock, then reaquire!! */
+static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked)
+{
+       /* Send marker if required */
+       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;
+       }
+       return (QLA_SUCCESS);
+}
+
+/* 
+ * 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,1702 +327,4557 @@ 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_ENTRY();
+
+       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;
 
-       TRACE_MGMT_DBG("scsi(%ld) Unregistering session %p loop_id=%d",
-             ha->host_no, sess, loop_id);
+                       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);
 
-       q2t_unreg_sess(sess);
+       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 */
-static int q2t_target_release(struct scst_tgt *scst_tgt)
+/* Called in a thread context */
+static void q2t_alloc_session_done(struct scst_session *scst_sess,
+                                  void *data, int result)
 {
-       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);
-       tgt->tgt_shutdown = 1;
-       q2t_clear_tgt_db(tgt);
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       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;
 
-       wait_event(tgt->waitQ, test_tgt_sess_count(tgt, ha));
+               PRINT_INFO("qla2x00tgt(%ld): Session initialization failed",
+                          ha->instance);
 
-       mutex_lock(&qla_mgmt_mutex);
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               q2t_sess_put(sess);
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       }
 
-       /* big hammer */
-       if (!ha->flags.host_shutting_down)
-               tgt_data.disable_lun(ha);
+       TRACE_EXIT();
+       return;
+}
 
-       /* wait for sessions to clear out (just in case) */
-       wait_event(tgt->waitQ, test_tgt_sess_count(tgt, ha));
+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_MGMT_DBG("Finished waiting for tgt %p: empty(sess_list)=%d "
-               "sess_count=%d", tgt, list_empty(&tgt->sess_list),
-               tgt->sess_count);
+       TRACE_ENTRY();
 
-       /* The lock is needed, because we still can get an incoming packet */
        spin_lock_irqsave(&ha->hardware_lock, flags);
-       scst_tgt_set_tgt_priv(scst_tgt, NULL);
-       ha->tgt = NULL;
+       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);
 
-       mutex_unlock(&qla_mgmt_mutex);
-
-       kfree(tgt);
-
-       TRACE_EXIT_RES(res);
-       return res;
+       TRACE_EXIT();
+       return;
 }
 
-static int q2t_pci_map_calc_cnt(struct q2t_prm *prm)
+/*
+ * 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)
 {
-       int res = 0;
+       char *wwn_str;
+       const int wwn_str_len = 3*WWN_SIZE+2;
+       struct q2t_tgt *tgt = ha->tgt;
+       struct q2t_sess *sess;
 
-       sBUG_ON(prm->sg_cnt == 0);
+       TRACE_ENTRY();
 
-       /* 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));
-       if (unlikely(prm->seg_cnt == 0))
-               goto out_err;
-       /*
-        * If greater than four sg entries then we need to allocate
-        * the continuation entries
-        */
-       if (prm->seg_cnt > prm->tgt->datasegs_per_cmd) {
-               prm->req_cnt += (uint16_t)(prm->seg_cnt -
-                               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->req_cnt++;
+       /* 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);
 
-out:
-       TRACE_DBG("seg_cnt=%d, req_cnt=%d, res=%d", prm->seg_cnt,
-               prm->req_cnt, res);
-       return res;
+       /* We are under tgt_mutex, so a new sess can't be added behind us */
 
-out_err:
-       PRINT_ERROR("qla2x00tgt(%ld): PCI mapping failed: sg_cnt=%d",
-               prm->tgt->ha->host_no, prm->sg_cnt);
-       res = -1;
-       goto out;
-}
+       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;
+       }
 
-/* ha->hardware_lock supposed to be held on entry */
-static inline uint32_t q2t_make_handle(scsi_qla_host_t *ha)
-{
-       uint32_t h;
+       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));
 
-       h = ha->current_cmd;
-       /* always increment cmd handle */
-       do {
-               ++h;
-               if (h > MAX_OUTSTANDING_COMMANDS)
-                       h = 0;
+       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;
+       }
 
-               if (h == ha->current_cmd) {
-                       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));
+       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);
 
-       if (h != Q2T_NULL_HANDLE)
-               ha->current_cmd = h;
+       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);
 
-       return h;
-}
+       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 ");
 
-/* 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)
-{
-       uint16_t timeout;
-       uint32_t h;
+       kfree(wwn_str);
 
-       prm->pkt = (struct ctio_common_entry *)tgt_data.req_pkt(prm->tgt->ha);
+out:
+       TRACE_EXIT_HRES(sess);
+       return sess;
 
-       if (prm->tgt->tgt_enable_64bit_addr)
-               prm->pkt->entry_type = CTIO_A64_TYPE;
-       else
-               prm->pkt->entry_type = CONTINUE_TGT_IO_TYPE;
+out_free_sess_wwn:
+       kfree(wwn_str);
+       /* go through */
 
-       prm->pkt->entry_count = (uint8_t) prm->req_cnt;
+out_free_sess:
+       kfree(sess);
+       sess = NULL;
+       goto out;
+}
 
-       h = q2t_make_handle(prm->tgt->ha);
-       if (h != Q2T_NULL_HANDLE)
-               prm->tgt->ha->cmds[h] = prm->cmd;
-       prm->pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK;
+static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport)
+{
+       struct q2t_tgt *tgt;
+       struct q2t_sess *sess;
 
-       timeout = Q2T_TIMEOUT;
-       prm->pkt->timeout = cpu_to_le16(timeout);
-
-       /* Set initiator ID */
-       h = GET_TARGET_ID(prm->tgt->ha, &prm->cmd->atio);
-       SET_TARGET_ID(prm->tgt->ha, prm->pkt->target, h);
+       TRACE_ENTRY();
 
-       prm->pkt->exchange_id = prm->cmd->atio.exchange_id;
+       mutex_lock(&ha->tgt_mutex);
 
-       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));
+       tgt = ha->tgt;
 
-}
+       if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR))
+               goto out_unlock;
 
-static void q2t_load_data_segments(struct q2t_prm *prm)
-{
-       uint32_t cnt;
-       uint32_t *dword_ptr;
-       int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr;
+       if (tgt->tgt_shutdown)
+               goto out_unlock;
 
-       TRACE_DBG("iocb->scsi_status=%x, iocb->flags=%x",
-             le16_to_cpu(prm->pkt->scsi_status), le16_to_cpu(prm->pkt->flags));
+       spin_lock_irq(&ha->hardware_lock);
 
-       prm->pkt->transfer_length = cpu_to_le32(prm->bufflen);
+       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;
+       }
 
-       /* Setup packet address segment pointer */
-       dword_ptr = prm->pkt->dseg_0_address;
+       spin_unlock_irq(&ha->hardware_lock);
 
-       if (prm->seg_cnt == 0) {
-               /* No data transfer */
-               *dword_ptr++ = 0;
-               *dword_ptr = 0;
+out_unlock:
+       mutex_unlock(&ha->tgt_mutex);
 
-               TRACE_BUFFER("No data, CTIO packet data",
-                            prm->pkt, REQUEST_ENTRY_SIZE);
-               goto out;
-       }
+       TRACE_EXIT();
+       return;
+}
 
-       /* Set total data segment count */
-       prm->pkt->dseg_count = cpu_to_le16(prm->seg_cnt);
+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;
 
-       /* 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_ENTRY();
 
-               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));
+       mutex_lock(&ha->tgt_mutex);
 
-               prm->sg++;
-       }
+       tgt = ha->tgt;
 
-       TRACE_BUFFER("Scatter/gather, CTIO packet data",
-                    prm->pkt, REQUEST_ENTRY_SIZE);
+       if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR))
+               goto out_unlock;
 
-       /* 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);
+       dev_loss_tmo = ha->port_down_retry_count + 5;
 
-               /*
-                * Make sure that from cont_pkt64 none of
-                * 64-bit specific fields used for 32-bit
-                * addressing. Cast to (cont_entry_t *) for
-                * that.
-                */
+       if (tgt->tgt_shutdown)
+               goto out_unlock;
 
-               memset(cont_pkt64, 0, sizeof(*cont_pkt64));
+       spin_lock_irq(&ha->hardware_lock);
 
-               cont_pkt64->entry_count = 1;
-               cont_pkt64->sys_define = 0;
+       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);
+       }
 
-               if (enable_64bit_addressing) {
-                       cont_pkt64->entry_type = CONTINUE_A64_TYPE;
-                       dword_ptr =
-                           (uint32_t *)&cont_pkt64->dseg_0_address;
-               } else {
-                       cont_pkt64->entry_type = CONTINUE_TYPE;
-                       dword_ptr =
-                           (uint32_t *)&((cont_entry_t *)
-                                           cont_pkt64)->dseg_0_address;
-               }
+out_unlock_ha:
+       spin_unlock_irq(&ha->hardware_lock);
 
-               /* Load continuation entry data segments */
-               for (cnt = 0;
-                    cnt < prm->tgt->datasegs_per_cont && 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));
+out_unlock:
+       mutex_unlock(&ha->tgt_mutex);
 
-                       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_EXIT();
+       return;
+}
 
-                       prm->sg++;
-               }
+static inline int test_tgt_sess_count(struct q2t_tgt *tgt)
+{
+       unsigned long flags;
+       int res;
 
-               TRACE_BUFFER("Continuation packet data",
-                            cont_pkt64, REQUEST_ENTRY_SIZE);
-       }
+       /*
+        * 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);
 
-out:
-       return;
+       return res;
 }
 
-static void q2t_init_ctio_ret_entry(struct ctio_ret_entry *ctio_m1,
-       struct q2t_prm *prm)
+/* 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;
+
        TRACE_ENTRY();
 
-       prm->sense_buffer_len = min((uint32_t)prm->sense_buffer_len,
-                                   (uint32_t)sizeof(ctio_m1->sense_data));
+       /* 
+        * Mutex needed to sync with q2t_fc_port_[added,deleted].
+        * Lock is needed, because we still can get an incoming packet.
+        */
 
-       ctio_m1->flags = __constant_cpu_to_le16(OF_SSTS | OF_FAST_POST |
-                                               OF_NO_DATA | OF_SS_MODE_1);
-       ctio_m1->flags |= __constant_cpu_to_le16(OF_INC_RC);
-       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);
-               ctio_m1->sense_length = cpu_to_le16(prm->sense_buffer_len);
-               memcpy(ctio_m1->sense_data, prm->sense_buffer,
-                      prm->sense_buffer_len);
-       } else {
-               memset(ctio_m1->sense_data, 0, sizeof(ctio_m1->sense_data));
-               ctio_m1->sense_length = 0;
+       mutex_lock(&ha->tgt_mutex);
+       spin_lock_irq(&ha->hardware_lock);
+       tgt->tgt_shutdown = 1;
+       q2t_clear_tgt_db(tgt, false);
+       spin_unlock_irq(&ha->hardware_lock);
+       mutex_unlock(&ha->tgt_mutex);
+
+       del_timer_sync(&tgt->sess_del_timer);
+
+       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);
 
-       TRACE_BUFFER("CTIO returned packet data", ctio_m1, REQUEST_ENTRY_SIZE);
+       TRACE_MGMT_DBG("Waiting for tgt %p: list_empty(sess_list)=%d "
+               "sess_count=%d", tgt, list_empty(&tgt->sess_list),
+               tgt->sess_count);
 
-       /* Sense with len > 26, is it possible ??? */
+       wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
 
-       TRACE_EXIT();
-       return;
-}
+       /* Big hammer */
+       if (!ha->host_shutting_down) 
+               tgt_data.disable_tgt_mode(ha);
 
-static inline int q2t_has_data(struct scst_cmd *scst_cmd)
-{
-       return scst_cmd_get_resp_data_len(scst_cmd) > 0;
-}
+       /* Wait for sessions to clear out (just in case) */
+       wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); 
 
-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;
-       struct q2t_prm prm;
-       int data_sense_flag = 0;
-       uint16_t full_req_cnt;
+       TRACE_MGMT_DBG("Waiting for %d IRQ commands to complete (tgt %p)",
+               tgt->irq_cmd_count, tgt);
 
-       TRACE_ENTRY();
-       TRACE(TRACE_SCSI, "tag=%lld", scst_cmd_get_tag(scst_cmd));
+       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_irq(&ha->hardware_lock);
+       mutex_unlock(&ha->tgt_mutex);
 
-#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD
-       if (scst_cmd_atomic(scst_cmd))
-               return SCST_TGT_RES_NEED_THREAD_CTX;
-#endif
+       TRACE_MGMT_DBG("Release of tgt %p finished", tgt);
 
-       memset(&prm, 0, sizeof(prm));
+       kfree(tgt);
 
-       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));
+       TRACE_EXIT_RES(res);
+       return res;
+}
 
-       if (unlikely(scst_cmd_aborted(scst_cmd))) {
-               scsi_qla_host_t *ha = sess->tgt->ha;
+/*
+ * 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(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));
+       TRACE_ENTRY();
 
-               scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_ABORTED);
+       TRACE_DBG("Sending MODIFY_LUN (ha=%p, cmd=%d, imm=%d)", 
+                 ha, cmd_count, imm_count);
 
-               prm.cmd->state = Q2T_STATE_ABORTED;
+       /* Sending marker isn't necessary, since we called from ISR */
 
-               q2t_send_term_exchange(ha, prm.cmd, &prm.cmd->atio, 0);
-               /* !! At this point cmd could be already freed !! */
+       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;
        }
 
-       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);
-
-       TRACE_DBG("rq_result=%x, is_send_status=%x", prm.rq_result,
-               is_send_status);
+       ha->tgt->modify_lun_expected++;
 
-       if (prm.rq_result != 0)
-               TRACE_BUFFER("Sense", prm.sense_buffer, prm.sense_buffer_len);
+       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 (!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 (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;
        }
 
-       /* Acquire ring specific lock */
-       spin_lock_irqsave(&prm.tgt->ha->hardware_lock, flags);
+       pkt->timeout = 0;       /* Use default */
 
-       /* 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_BUFFER("MODIFY LUN packet data", pkt, REQUEST_ENTRY_SIZE);
 
-       TRACE_DBG("CTIO start: ha(%d)", (int) prm.tgt->ha->host_no);
+       q2t_exec_queue(ha);
 
-       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);
+out:
+       TRACE_EXIT();
+       return;
+}
 
-                               TRACE_DBG("%s", "Building additional status "
-                                       "packet");
+/*
+ * 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();
 
-                               memcpy(ctio_m1, prm.pkt, sizeof(*ctio_m1));
-                               ctio_m1->entry_count = 1;
+       TRACE_DBG("Sending NOTIFY_ACK (ha=%p)", ha);
 
-                               /* Real finish is ctio_m1's finish */
-                               prm.pkt->handle = Q2T_SKIP_HANDLE |
-                                               CTIO_COMPLETION_HANDLE_MARK;
+       /* Send marker if required */
+       if (q2t_issue_marker(ha, 1) != QLA_SUCCESS)
+               goto out;
 
-                               prm.pkt->flags &= ~__constant_cpu_to_le16(OF_INC_RC);
+       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;
+       }
 
-                               q2t_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);
-       } else
-               q2t_init_ctio_ret_entry((struct ctio_ret_entry *)prm.pkt,
-                                       &prm);
+       if (ha->tgt != NULL)
+               ha->tgt->notify_ack_expected++;
 
-       /* Mid-level is done processing */
-       prm.cmd->state = Q2T_STATE_PROCESSED;
+       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;
 
-       TRACE_BUFFER("Xmitting", prm.pkt, REQUEST_ENTRY_SIZE);
+       if (resp_code_valid) {
+               ntfy->resp_code = cpu_to_le16(resp_code);
+               ntfy->flags |= __constant_cpu_to_le16(
+                       NOTIFY_ACK_TM_RESP_CODE_VALID);
+       }
 
-       q2t_exec_queue(prm.tgt->ha);
+       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);       
 
-out_unlock:
-       /* Release ring specific lock */
-       spin_unlock_irqrestore(&prm.tgt->ha->hardware_lock, flags);
+       q2t_exec_queue(ha);
 
 out:
-       TRACE_EXIT_RES(res);
-       return res;
+       TRACE_EXIT();
+       return;
 }
 
-static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd)
+/*
+ * 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)
 {
-       int res = SCST_TGT_RES_SUCCESS;
-       struct q2t_sess *sess;
-       unsigned long flags = 0;
-       struct q2t_prm prm;
+       abts24_resp_entry_t *resp;
+       uint32_t f_ctl;
+       uint8_t *p;
 
        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
+       TRACE_DBG("Sending task mgmt ABTS response (ha=%p, atio=%p, "
+               "status=%x", ha, abts, status);
 
-       memset(&prm, 0, sizeof(prm));
+       /* Send marker if required */
+       if (q2t_issue_marker(ha, 1) != QLA_SUCCESS)
+               goto out;
 
-       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));
+       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;
+       }
 
-       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;
+       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 */
+       }
 
-       /* Acquire ring specific lock */
-       spin_lock_irqsave(&prm.tgt->ha->hardware_lock, flags);
+       TRACE_BUFFER("ABTS RESP packet data", resp, REQUEST_ENTRY_SIZE);
 
-       /* 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;
-       }
+       ha->tgt->abts_resp_expected++;
 
-       TRACE_DBG("CTIO_start: ha(%d)", (int) prm.tgt->ha->host_no);
+       q2t_exec_queue(ha);
 
-       /* Calculate number of entries and segments required */
-       if (q2t_pci_map_calc_cnt(&prm) != 0) {
-               res = SCST_TGT_RES_QUEUE_FULL;
-               goto out_unlock;
-       }
+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;
 
-       prm.cmd->iocb_cnt = prm.req_cnt;
+       TRACE_ENTRY();
+
+       TRACE_DBG("Sending retry TERM EXCH CTIO7 (ha=%p)", ha);
 
-       q2t_build_ctio_pkt(&prm);
+       /* Send marker if required */
+       if (q2t_issue_marker(ha, 1) != QLA_SUCCESS)
+               goto out;
 
-       prm.pkt->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_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;
+       }
 
-       q2t_load_data_segments(&prm);
+       /*
+        * We've got on entrance firmware's response on by us generated
+        * ABTS response. So, in it ID fields are reversed.
+        */
 
-       prm.cmd->state = Q2T_STATE_NEED_DATA;
+       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("Xfering", prm.pkt, REQUEST_ENTRY_SIZE);
+       TRACE_BUFFER("CTIO7 retry TERM EXCH packet data", ctio, 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);
+       q24_send_abts_resp(ha, (abts24_recv_entry_t *)entry,
+               SCST_MGMT_STATUS_SUCCESS, true);
 
-       TRACE_EXIT_RES(res);
-       return res;
+out:
+       TRACE_EXIT();
+       return;
 }
 
-static void q2t_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
-       struct atio_entry *atio, int ha_locked)
+/*
+ * 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)
 {
-       struct ctio_ret_entry *ctio;
-       unsigned long flags = 0;
-       int do_tgt_cmd_done = 0;
+       uint32_t tag;
+       int rc;
+       struct q2t_mgmt_cmd *mcmd;
+       struct q2t_sess *sess;
 
        TRACE_ENTRY();
 
-       TRACE_DBG("Sending TERM EXCH CTIO (ha=%p)", ha);
+       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;
+       }
 
-       if (!ha_locked)
-               spin_lock_irqsave(&ha->hardware_lock, flags);
+       tag = abts->exchange_addr_to_abort;
 
-       /* 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;
+       if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
+               TRACE_MGMT_DBG("qla2x00tgt(%ld): ABTS: Unknown Exchange "
+                       "Address received", ha->instance);
+               goto out_err;
        }
 
-       ctio = (struct ctio_ret_entry *)tgt_data.req_pkt(ha);
-       if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
-                       "request packet", ha->host_no, __func__);
-               goto out_unlock;
+       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;
        }
 
-       ctio->entry_type = CTIO_RET_TYPE;
-       ctio->entry_count = 1;
+       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));
 
-       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;
-                       do_tgt_cmd_done = 1;
-               }
-       } else
-               ctio->handle = Q2T_SKIP_HANDLE;
+       mcmd->sess = sess;
+       memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
 
-       ctio->handle |= CTIO_COMPLETION_HANDLE_MARK;
+       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;
+       }
 
-       SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio));
-       ctio->exchange_id = atio->exchange_id;
+out:
+       TRACE_EXIT();
+       return;
 
-       /* Most likely, it isn't needed */
-       ctio->residual = atio->data_length;
-       if (ctio->residual != 0)
-               ctio->scsi_status |= SS_RESIDUAL_UNDER;
+out_err_free:
+       mempool_free(mcmd, q2t_mgmt_cmd_mempool);
 
-       ctio->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_TERM_EXCH |
-                       OF_NO_DATA | OF_SS_MODE_1);
-       ctio->flags |= __constant_cpu_to_le16(OF_INC_RC);
+out_err:
+       q24_send_abts_resp(ha, abts, SCST_MGMT_STATUS_REJECTED, false);
+       goto out;
+}
 
-       TRACE_BUFFER("CTIO TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE);
+/*
+ * 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;
 
-       q2t_exec_queue(ha);
+       TRACE_ENTRY();
 
-out_unlock:
-       if (!ha_locked)
-               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       TRACE_DBG("Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x", 
+                 ha, atio, resp_code);
 
-       if (do_tgt_cmd_done) {
-               if (!in_interrupt()) {
-                       msleep(250);
-                       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 !! */
+       /* 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;
 }
 
-static inline void q2t_free_cmd(struct q2t_cmd *cmd)
+/*
+ * 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)
 {
-       kmem_cache_free(q2t_cmd_cachep, cmd);
-}
+       nack24xx_entry_t *nack;
+       
+       TRACE_ENTRY();
 
-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);
+       TRACE_DBG("Sending NOTIFY_ACK24 (ha=%p)", ha);
 
-       TRACE_ENTRY();
-       TRACE(TRACE_SCSI, "END Command tag %lld", scst_cmd_get_tag(scst_cmd));
+       /* Send marker if required */
+       if (q2t_issue_marker(ha, 1) != QLA_SUCCESS)
+               goto out;
 
-       scst_cmd_set_tgt_priv(scst_cmd, NULL);
+       if (ha->tgt != NULL)
+               ha->tgt->notify_ack_expected++;
 
-       q2t_free_cmd(cmd);
+       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;
 }
 
-/* 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)
+int q2t_convert_to_fc_tm_status(int scst_mstatus)
 {
-       if (ha->cmds[handle] != NULL) {
-               struct scst_cmd *cmd = ha->cmds[handle]->scst_cmd;
-               ha->cmds[handle] = NULL;
-               return cmd;
-       } else
-               return NULL;
+       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;
 }
 
-/* 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)
+/* SCST Callback */
+static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
 {
-       struct scst_cmd *scst_cmd;
-       struct q2t_cmd *cmd;
-       uint16_t loop_id = -1;
-       enum scst_exec_context context;
-       int err = 0;
+       struct q2t_mgmt_cmd *mcmd;
+       unsigned long flags;
+       scsi_qla_host_t *ha;
 
        TRACE_ENTRY();
 
-#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD
-       context = SCST_CONTEXT_THREAD;
-#else
-       context = SCST_CONTEXT_TASKLET;
-#endif
+       TRACE_MGMT_DBG("scst_mcmd (%p) status %#x state %#x", scst_mcmd,
+               scst_mcmd->status, scst_mcmd->state);
 
-       if (ctio != NULL)
-               loop_id = GET_TARGET_ID(ha, ctio);
+       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;
+       }
 
-       TRACE(TRACE_DEBUG|TRACE_SCSI, "handle(ctio %p status %#x) <- %08x I %x",
-             ctio, status, handle, loop_id);
+       ha = mcmd->sess->tgt->ha;
 
-       /* Clear out CTIO_COMPLETION_HANDLE_MARK */
-       handle &= ~CTIO_COMPLETION_HANDLE_MARK;
+       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);
 
-       if (status != CTIO_SUCCESS) {
-               err = 1;
-               switch (status) {
-               case CTIO_LIP_RESET:
-               case CTIO_TARGET_RESET:
-               case CTIO_ABORTED:
-               case CTIO_TIMEOUT:
-               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);
-                       break;
+       scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL);
+       mempool_free(mcmd, q2t_mgmt_cmd_mempool);
 
-               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);
-                       break;
+out:
+       TRACE_EXIT();
+       return;
+}
 
-               default:
-                       PRINT_ERROR("qla2x00tgt(%ld): CTIO with error status "
-                                   "0x%x received", ha->host_no, status);
-                       break;
-               }
-               q2t_modify_command_count(ha, 1, 0);
-       }
+/* No locks */
+static int q2t_pci_map_calc_cnt(struct q2t_prm *prm)
+{
+       int res = 0;
 
-       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);
+       sBUG_ON(prm->cmd->sg_cnt == 0);
 
-               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;
+       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;
+       /*
+        * If greater than four sg entries then we need to allocate
+        * the continuation entries
+        */
+       if (prm->seg_cnt > prm->tgt->datasegs_per_cmd) {
+               prm->req_cnt += (uint16_t)(prm->seg_cnt -
+                               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->req_cnt++;
                }
+       }
 
-               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]);
+out:
+       TRACE_DBG("seg_cnt=%d, req_cnt=%d, res=%d", prm->seg_cnt, 
+               prm->req_cnt, res);
+       return res;
 
-               TRACE_DBG("Found scst_cmd %p", scst_cmd);
-       } else
-               goto out;
+out_err:
+       PRINT_ERROR("qla2x00tgt(%ld): PCI mapping failed: sg_cnt=%d", 
+               prm->tgt->ha->instance, prm->cmd->sg_cnt);
+       res = -1;
+       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);
+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;
 
-       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)));
-               }
-               goto out_free;
-       } else if (cmd->state == Q2T_STATE_NEED_DATA) {
-               int rx_status = SCST_RX_STATUS_SUCCESS;
+       TRACE_ENTRY();
 
-               cmd->state = Q2T_STATE_DATA_IN;
+       if (ha->req_q_cnt < (req_cnt + 2)) {
+               if (IS_FWI2_CAPABLE(ha))
+                       cnt = (uint16_t)RD_REG_DWORD(
+                                   &reg->isp24.req_q_out);
+               else
+                       cnt = qla2x00_debounce_register(
+                                   ISP_REQ_Q_OUT(ha, &reg->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 (status != CTIO_SUCCESS)
-                       rx_status = SCST_RX_STATUS_ERROR;
+       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;
+       }
 
-               TRACE_DBG("Data received, context %x, rx_status %d",
+       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_handle;
+       /* always increment cmd handle */
+       do {
+               ++h;
+               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_SKIP_HANDLE) || 
+                (ha->cmds[h-1] != NULL));
+
+       if (h != Q2T_NULL_HANDLE)
+               ha->current_handle = h;
+
+       return h;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void q2x_build_ctio_pkt(struct q2t_prm *prm)
+{
+       uint32_t h;
+       ctio_entry_t *pkt;
+       scsi_qla_host_t *ha = 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)
+               pkt->common.entry_type = CTIO_A64_TYPE;
+       else
+               pkt->common.entry_type = CONTINUE_TGT_IO_TYPE;
+
+       pkt->common.entry_count = (uint8_t)prm->req_cnt;
+
+       h = q2t_make_handle(ha);
+       if (h != Q2T_NULL_HANDLE)
+               ha->cmds[h-1] = prm->cmd;
+
+       pkt->common.handle = h | CTIO_COMPLETION_HANDLE_MARK;
+       pkt->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT);
+
+       /* Set initiator ID */
+       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);
+
+       TRACE(TRACE_DEBUG|TRACE_SCSI, 
+             "handle(scst_cmd) -> %08x, timeout %d L %#x -> I %#x E %#x",
+             pkt->common.handle, Q2T_TIMEOUT,
+             le16_to_cpu(prm->cmd->atio.atio2x.lun),
+             GET_TARGET_ID(ha, &pkt->common), pkt->common.rx_id);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int q24_build_ctio_pkt(struct q2t_prm *prm)
+{
+       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_ENTRY();
+
+       /* Build continuation packets */
+       while (prm->seg_cnt > 0) {
+               cont_a64_entry_t *cont_pkt64 =
+                       (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
+                * that.
+                */
+
+               memset(cont_pkt64, 0, sizeof(*cont_pkt64));
+
+               cont_pkt64->entry_count = 1;
+               cont_pkt64->sys_define = 0;
+
+               if (enable_64bit_addressing) {
+                       cont_pkt64->entry_type = CONTINUE_A64_TYPE;
+                       dword_ptr =
+                           (uint32_t *)&cont_pkt64->dseg_0_address;
+               } else {
+                       cont_pkt64->entry_type = CONTINUE_TYPE;
+                       dword_ptr =
+                           (uint32_t *)&((cont_entry_t *)
+                                           cont_pkt64)->dseg_0_address;
+               }
+
+               /* Load continuation entry data segments */
+               for (cnt = 0;
+                    cnt < prm->tgt->datasegs_per_cont && 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 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++;
+               }
+
+               TRACE_BUFFER("Continuation packet data",
+                            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;
+}
+
+/*
+ * 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, 
+                                   (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);
+       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)) {
+               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);
+       } else {
+               memset(ctio_m1->sense_data, 0, sizeof(ctio_m1->sense_data));
+               ctio_m1->sense_length = 0;
+       }
+
+       /* Sense with len > 26, is it possible ??? */
+
+       TRACE_EXIT();
+       return;
+}
+
+static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type)
+{
+       int res;
+       unsigned long flags;
+       scsi_qla_host_t *ha;
+       struct q2t_prm prm;
+       ctio_common_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;
+
+       q2x_build_ctio_pkt(&prm);
+       pkt = (ctio_common_entry_t *)prm.pkt;
+
+       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);
+
+               q2x_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(OF_SSTS);
+                               if (q2t_need_explicit_conf(ha, cmd, 0)) {
+                                       pkt->flags |= __constant_cpu_to_le16(
+                                                       OF_EXPL_CONF |
+                                                       OF_CONF_REQ);
+                               }
+                       }
+               } 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
+               q2x_init_ctio_ret_entry((ctio_ret_entry_t *)pkt, &prm);
+
+       cmd->state = Q2T_STATE_PROCESSED;       /* Mid-level is done processing */
+
+       TRACE_BUFFER("Xmitting", pkt, REQUEST_ENTRY_SIZE);
+
+       q2t_exec_queue(ha);
+
+       /* Release ring specific lock */
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+#ifdef CONFIG_QLA_TGT_DEBUG_SRR
+static void q2t_check_srr_debug(struct q2t_cmd *cmd, int *xmit_type)
+{
+#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
+
+       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
+
+       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, cmd->bufflen=%d, cmd->sg_cnt=%d, "
+               "cmd->data_direction=%d", is_send_status, cmd->bufflen,
+               cmd->sg_cnt, cmd->data_direction);
+
+       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));
+       }
+
+       /* 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;
+       }
+
+       /* 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, prm.req_cnt);
+       if (res != SCST_TGT_RES_SUCCESS)
+               goto out_unlock_free_unmap;
+
+       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;
+       }
+
+       cmd->state = Q2T_STATE_NEED_DATA;
+
+       TRACE_BUFFER("Xfering", p, 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_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;
+}
+
+/* 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)
+{
+       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);
+
+       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_unlock;
+       }
+
+       ctio->entry_type = CTIO_RET_TYPE;
+       ctio->entry_count = 1;
+       if (cmd != NULL) {
+               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;
+       }
+       ctio->handle = Q2T_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+
+       /* Set IDs */
+       SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio));
+       ctio->rx_id = atio->rx_id;
+
+       /* Most likely, it isn't needed */
+       ctio->residual = atio->data_length;
+       if (ctio->residual != 0)
+               ctio->scsi_status |= SS_RESIDUAL_UNDER;
+
+       ctio->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_TERM_EXCH |
+                       OF_NO_DATA | OF_SS_MODE_1);
+       ctio->flags |= __constant_cpu_to_le16(OF_INC_RC);
+
+       TRACE_BUFFER("CTIO 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;
+}
+
+/* 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;
+
+       TRACE_ENTRY();
+
+       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);
+
+       TRACE_EXIT();
+       return;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+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 q2t_cmd *cmd = ha->cmds[handle];
+               ha->cmds[handle] = NULL;
+               return cmd;
+       } else
+               return NULL;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+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;
+       enum scst_exec_context context;
+
+       TRACE_ENTRY();
+
+#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD
+       context = SCST_CONTEXT_THREAD;
+#else
+       context = SCST_CONTEXT_TASKLET;
+#endif
+
+       TRACE(TRACE_DEBUG|TRACE_SCSI, "handle(ctio %p status %#x) <- %08x", 
+             ctio, status, handle);
+
+       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;
+       }
+
+       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;
+       }
+
+       scst_cmd = cmd->scst_cmd;
+
+       if (unlikely(status != CTIO_SUCCESS)) {
+               switch (status & 0xFFFF) {
+               case CTIO_LIP_RESET:
+               case CTIO_TARGET_RESET:
+               case CTIO_ABORTED:
+               case CTIO_TIMEOUT:
+               case CTIO_INVALID_RX_ID:
+                       /* they are OK */
+                       TRACE(TRACE_MGMT_MINOR, "qla2x00tgt(%ld): CTIO with "
+                               "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 (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 (state %x, scst_cmd %p, op %x)",
+                               ha->instance, status, cmd->state, scst_cmd,
+                               scst_cmd->cdb[0]);
+                       break;
+               }
+
+               if (cmd->state != Q2T_STATE_NEED_DATA)
+                       if (q2t_term_ctio_exchange(ha, ctio, cmd, status))
+                               goto out;
+       }
+
+       if (cmd->state == Q2T_STATE_PROCESSED) {
+               TRACE_DBG("Command %p finished", 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) {
+               int rx_status = SCST_RX_STATUS_SUCCESS;
+
+               cmd->state = Q2T_STATE_DATA_IN;
+
+               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 (tag %d) finished", cmd,
+                       cmd->tag);
+       } else {
+               PRINT_ERROR("qla2x00tgt(%ld): A command in state (%d) should "
+                       "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;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+/* called via callback from qla2xxx */
+static void q2x_ctio_completion(scsi_qla_host_t *ha, uint32_t handle)
+{
+       struct q2t_tgt *tgt = ha->tgt;
+
+       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_EXIT();
+       return;
+}
+
+/* ha->hardware_lock is supposed to be held on entry */
+static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd)
+{
+       int res = 0;
+       struct q2t_sess *sess = cmd->sess;
+       uint16_t lun;
+       atio_entry_t *atio = &cmd->atio.atio2x;
+       scst_data_direction dir;
+       int context;
+
+       TRACE_ENTRY();
+
+       /* make it be in network byte order */
+       lun = swab16(le16_to_cpu(atio->lun));
+       cmd->scst_cmd = scst_rx_cmd(sess->scst_sess, (uint8_t *)&lun,
+                                   sizeof(lun), atio->cdb, Q2T_MAX_CDB_LEN,
+                                   SCST_ATOMIC);
+
+       if (cmd->scst_cmd == NULL) {
+               PRINT_ERROR("%s", "qla2x00tgt: scst_rx_cmd() failed");
+               res = -EFAULT;
+               goto out;
+       }
+
+       cmd->tag = atio->rx_id;
+       scst_cmd_set_tag(cmd->scst_cmd, cmd->tag);
+       scst_cmd_set_tgt_priv(cmd->scst_cmd, cmd);
+
+       if (atio->execution_codes & ATIO_EXEC_READ)
+               dir = SCST_DATA_READ;
+       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(atio->data_length));
+
+       switch(atio->task_codes) {
+       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->task_codes);
+               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
+               break;
+       }
+
+#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 (tag %d, queue_type %d)",
+               cmd->tag, cmd->scst_cmd->queue_type);
+       scst_cmd_init_done(cmd->scst_cmd, context);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* 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();
+
+       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);
+
+       if (cmd->scst_cmd == NULL) {
+               PRINT_ERROR("%s", "qla2x00tgt: scst_rx_cmd() failed");
+               res = -EFAULT;
+               goto out;
+       }
+
+       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;
+       }
+
+#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:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha,
+       struct q2t_cmd *cmd, struct q2t_sess *sess)
+{
+       int res;
+
+       TRACE_ENTRY();
+
+       cmd->sess = sess;
+       cmd->loop_id = sess->loop_id;
+       cmd->conf_compl_supported = sess->conf_compl_supported;
+
+       if (IS_FWI2_CAPABLE(ha))
+               res = q24_do_send_cmd_to_scst(cmd);
+       else
+               res = q2x_do_send_cmd_to_scst(cmd);
+
+       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;
+       }
+
+       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;
+       }
+
+       memcpy(&cmd->atio.atio2x, atio, sizeof(*atio));
+       cmd->state = Q2T_STATE_NEW;
+       cmd->tgt = ha->tgt;
+
+       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;
+               }
+       } 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;
+               }
+       }
+
+       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_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_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;
+
+       TRACE_ENTRY();
+
+       mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (mcmd == NULL) {
+               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;
+       if (iocb) {
+               memcpy(&mcmd->orig_iocb.notify_entry, iocb, 
+                       sizeof(mcmd->orig_iocb.notify_entry));
+       }
+       mcmd->flags = flags;
+
+       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,
+                                        lun, lun_size, SCST_ATOMIC, mcmd);
+               break;
+
+       case Q2T_TARGET_RESET:
+               TRACE(TRACE_MGMT, "%s", "TARGET_RESET received");
+               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_TARGET_RESET,
+                                        lun, lun_size, SCST_ATOMIC, mcmd);
+               break;
+
+       case Q2T_LUN_RESET:
+               TRACE(TRACE_MGMT, "%s", "LUN_RESET received");
+               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_LUN_RESET,
+                                        lun, lun_size, SCST_ATOMIC, mcmd);
+               break;
+
+       case Q2T_CLEAR_TS:
+               TRACE(TRACE_MGMT, "%s", "CLEAR_TS received");
+               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_TASK_SET,
+                                        lun, lun_size, SCST_ATOMIC, mcmd);
+               break;
+
+       case Q2T_ABORT_TS:
+               TRACE(TRACE_MGMT, "%s", "ABORT_TS received");
+               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_TASK_SET,
+                                        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",
+                           sess->tgt->ha->instance, fn);
+               rc = -1;
+               break;
+       }
+
+       if (rc != 0) {
+               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_lun() failed: %d",
+                           sess->tgt->ha->instance, rc);
+               res = -EFAULT;
+               goto out_free;
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_free:
+       mempool_free(mcmd, q2t_mgmt_cmd_mempool);
+       goto out;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+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;
+       struct q2t_sess *sess;
+       int loop_id;
+       uint32_t tag;
+
+       TRACE_ENTRY();
+
+       loop_id = GET_TARGET_ID(ha, iocb);
+       tag = le16_to_cpu(iocb->seq_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->instance);
+               ha->tgt->tm_to_unknown = 1;
+               res = -EFAULT;
+               goto out;
+       }
+       
+       mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
+       if (mcmd == NULL) {
+               PRINT_ERROR("%s: Allocation of ABORT cmd failed", __func__);
+               res = -ENOMEM;
+               goto out;
+       }
+       memset(mcmd, 0, sizeof(*mcmd));
+
+       mcmd->sess = sess;
+       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, 
+               SCST_ATOMIC, mcmd);
+       if (rc != 0) {
+               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_tag() failed: %d",
+                           ha->instance, rc);
+               res = -EFAULT;
+               goto out_free;
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_free:
+       mempool_free(mcmd, q2t_mgmt_cmd_mempool);
+       goto out;
+}
+
+/*
+ * 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)
+{
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       TRACE(TRACE_MGMT, "ELS opcode %x", iocb->status_subcode);
+
+       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;
+       }
+
+       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);
+
+       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();
 
-               scst_rx_data(scst_cmd, rx_status, context);
-       } else if (cmd->state == Q2T_STATE_ABORTED) {
-               TRACE_MGMT_DBG("Aborted command %p finished", cmd);
-               goto out_free;
-       } else {
-               PRINT_ERROR("qla2x00tgt(%ld): A command in state (%d) should "
-                       "not return a CTIO complete", ha->host_no, cmd->state);
-               goto out_free;
+       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_free:
-       if (unlikely(err)) {
-               TRACE_MGMT_DBG("%s", "Finishing failed CTIO");
-               scst_set_delivery_status(scst_cmd, SCST_CMD_DELIVERY_FAILED);
+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));
        }
-       scst_tgt_cmd_done(scst_cmd, context);
+
+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;
 }
 
-/* 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 q2t_reject_free_srr_imm(scsi_qla_host_t *ha, struct srr_imm *imm,
+       int ha_locked)
 {
-       TRACE_ENTRY();
-       sBUG_ON(ha == NULL);
+       if (!ha_locked)
+               spin_lock_irq(&ha->hardware_lock);
 
-       if (ha->tgt != NULL) {
-               q2t_do_ctio_completion(ha, handle,
-                                      CTIO_SUCCESS, NULL);
+       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 {
-               TRACE_DBG("CTIO, but target mode not enabled. ha %p handle %#x",
-                         ha, handle);
+               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);
        }
-       TRACE_EXIT();
+
+       if (!ha_locked)
+               spin_unlock_irq(&ha->hardware_lock);
+
+       kfree(imm);
        return;
 }
 
-/* ha->hardware_lock supposed to be held on entry */
-static void q2t_send_busy(scsi_qla_host_t *ha, struct atio_entry *atio)
+static void q2t_handle_srr_work(struct work_struct *work)
 {
-       struct ctio_ret_entry *ctio;
+       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();
 
-       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;
+       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;
+                       }
+               }
 
-       /* Set IDs */
-       SET_TARGET_ID(ha, ctio->target, GET_TARGET_ID(ha, atio));
-       ctio->exchange_id = atio->exchange_id;
+               TRACE_MGMT_DBG("IMM SRR %p, CTIO SRR %p (id %d)", imm, sctio,
+                       sctio->srr_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);
+               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);
 
-       TRACE_BUFFER("CTIO BUSY packet data", ctio, REQUEST_ENTRY_SIZE);
+               spin_unlock_irq(&tgt->srr_lock);
 
-       q2t_exec_queue(ha);
+               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 is supposed to be held on entry */
-static int q2t_do_send_cmd_to_scst(scsi_qla_host_t *ha, struct q2t_cmd *cmd)
+/* ha->hardware_lock supposed to be held on entry */
+static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb)
 {
-       int res = 0;
-       struct q2t_sess *sess = cmd->sess;
-       uint16_t lun;
-       scst_data_direction dir = SCST_DATA_NONE;
-       enum scst_exec_context context;
+       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);
 
-       TRACE_ENTRY();
+                               kfree(imm);
 
-       /* make it be in network byte order */
-       lun = swab16(cmd->atio.lun);
-       cmd->scst_cmd = scst_rx_cmd(sess->scst_sess, (uint8_t *)&lun,
-                                   sizeof(lun), cmd->atio.cdb, Q2T_MAX_CDB_LEN,
-                                   SCST_ATOMIC);
+                               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;
+       }
 
-       if (cmd->scst_cmd == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_cmd() failed for "
-                    "host %ld(%p)", ha->host_no, ha->host_no, ha);
-               res = -EFAULT;
-               goto out;
+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;
+}
 
-       scst_cmd_set_tag(cmd->scst_cmd, le16_to_cpu(cmd->atio.exchange_id));
-       scst_cmd_set_tgt_priv(cmd->scst_cmd, cmd);
+/*
+ * 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;
 
-       if (cmd->atio.execution_codes & ATIO_EXEC_READ)
-               dir = SCST_DATA_READ;
-       else if (cmd->atio.execution_codes & ATIO_EXEC_WRITE)
-               dir = SCST_DATA_WRITE;
-       scst_cmd_set_expected(cmd->scst_cmd, dir,
-               le32_to_cpu(cmd->atio.data_length));
+       TRACE_ENTRY();
 
-       switch (cmd->atio.task_codes) {
-       case ATIO_SIMPLE_QUEUE:
-               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_SIMPLE;
+       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 ATIO_HEAD_OF_QUEUE:
-               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_HEAD_OF_QUEUE;
+       }
+
+       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 ATIO_ORDERED_QUEUE:
-               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
+       }
+
+       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 ATIO_ACA_QUEUE:
-               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ACA;
+
+       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 ATIO_UNTAGGED:
-               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED;
+
+       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;
-       default:
-               PRINT_ERROR("qla2x00tgt(%ld): Unknown task code %x, use "
-                       "ORDERED instead", ha->host_no, cmd->atio.task_codes);
-               cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
+
+       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;
-       }
 
-#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD
-       context = SCST_CONTEXT_THREAD;
-#else
-       context = SCST_CONTEXT_TASKLET;
-#endif
+       case IMM_NTFY_IOCB_OVERFLOW:
+               PRINT_ERROR("qla2x00tgt(%ld): Cannot provide requested "
+                       "capability (IOCB overflowed the immediate notify "
+                       "resource count)", ha->instance);
+               break;
 
-       TRACE_DBG("Context %x", context);
-       TRACE(TRACE_SCSI, "START Command (tag %lld)",
-               scst_cmd_get_tag(cmd->scst_cmd));
-       scst_cmd_init_done(cmd->scst_cmd, context);
+       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;
 
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
+       case IMM_NTFY_RESOURCE:
+               PRINT_ERROR("qla2x00tgt(%ld): Out of resources, host %ld",
+                           ha->instance, ha->host_no);
+               break;
 
-/* Called in SCST's thread context */
-static void q2t_alloc_session_done(struct scst_session *scst_sess,
-                                  void *data, int result)
-{
-       TRACE_ENTRY();
+       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;
 
-       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;
+       case IMM_NTFY_ELS:
+               if (q24_handle_els(ha, iocb24) == 0)
+                       send_notify_ack = 0;
+               break;
 
-               PRINT_INFO("qla2x00tgt(%ld): Session initialization failed",
-                          ha->host_no);
+       case IMM_NTFY_SRR:
+               q2t_prepare_srr_imm(ha, iocb);
+               send_notify_ack = 0;
+               break;
 
-               spin_lock_irqsave(&ha->hardware_lock, flags);
-               q2t_unreg_sess(sess);
-               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       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;
 }
 
-static char *q2t_find_name(scsi_qla_host_t *ha, int loop_id)
+/*
+ * 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)
 {
-       int wwn_found = 0;
-       char *wwn_str;
-       fc_port_t *fcl;
+       ctio_ret_entry_t *ctio;
 
-       wwn_str = kmalloc(3*WWN_SIZE, GFP_ATOMIC);
-       if (wwn_str == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of wwn_str failed");
+       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;
        }
 
-       /* 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;
-               break;
-           }
-       }
+       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;
 
-       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;
-       }
+       /* 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:
-       return wwn_str;
+       TRACE_EXIT();
+       return;
 }
 
-static char *q2t_make_name(scsi_qla_host_t *ha, const uint8_t *name)
+/*
+ * 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)
 {
-       char *wwn_str;
+       ctio7_status1_entry_t *ctio;
+       struct q2t_sess *sess;
 
-       wwn_str = kmalloc(3*WWN_SIZE, GFP_ATOMIC);
-       if (wwn_str == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of wwn_str failed");
+       TRACE_ENTRY();
+
+       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;
+       }
+
+       /* Sending marker isn't necessary, since we called from ISR */
+
+       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;
        }
-       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]);
+
+       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:
-       return wwn_str;
+       TRACE_EXIT();
+       return;
 }
 
 /* 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)
+/* called via callback from qla2xxx */
+static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio)
 {
-       int res = 0;
-       struct q2t_tgt *tgt;
-       struct q2t_sess *sess;
-       struct q2t_cmd *cmd;
-       uint16_t *pn;
-       int loop_id;
+       int rc;
+       struct q2t_tgt *tgt = ha->tgt;
 
        TRACE_ENTRY();
 
-       tgt = ha->tgt;
-       loop_id = GET_TARGET_ID(ha, atio);
-
-       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 (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
-       if (cmd == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of cmd failed");
-               res = -ENOMEM;
+       if (unlikely(tgt == NULL)) {
+               TRACE_MGMT_DBG("ATIO pkt, but no tgt (ha %p)", ha);
                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));
-       cmd->state = Q2T_STATE_NEW;
+       TRACE(TRACE_SCSI, "ATIO pkt %p: type %02x count %02x",
+             atio, atio->entry_type, atio->entry_count);
 
-       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;
-               }
+       /*
+        * In tgt_shutdown mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
 
-               sess->tgt = tgt;
-               sess->loop_id = loop_id;
-               INIT_LIST_HEAD(&sess->list);
+       tgt->irq_cmd_count++;
 
-               /* 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;
+       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);
                        }
-
-                       sess->scst_sess = scst_register_session(
-                               tgt->scst_tgt, 1, name, sess,
-                               q2t_alloc_session_done);
-                       kfree(name);
                }
+               break;
 
-               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;
+       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;
                }
-               scst_sess_set_tgt_priv(sess->scst_sess, sess);
+               TRACE_DBG("%s", "IMMED_NOTIFY ATIO");
+               q2t_handle_imm_notify(ha, pkt);
+               break;
+       }
 
-               /* add session data to host data structure */
-               list_add(&sess->list, &tgt->sess_list);
-               tgt->sess_count++;
+       default:
+               PRINT_ERROR("qla2x00tgt(%ld): Received unknown ATIO atio "
+                    "type %x", ha->instance, atio->entry_type);
+               break;
        }
 
-       cmd->sess = sess;
-       res = q2t_do_send_cmd_to_scst(ha, cmd);
-       if (res != 0)
-               goto out_free_cmd;
+       tgt->irq_cmd_count--;
 
 out:
-       TRACE_EXIT_RES(res);
-       return res;
-
-out_free_sess:
-       kfree(sess);
-       /* go through */
-
-out_free_cmd:
-       q2t_free_cmd(cmd);
-       goto out;
+       TRACE_EXIT();
+       return;
 }
 
 /* ha->hardware_lock supposed to be held on entry */
-static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, struct notify_entry *iocb)
+/* called via callback from qla2xxx */
+static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
 {
-       int res = 0, rc = -1;
-       struct q2t_mgmt_cmd *mcmd;
-       struct q2t_tgt *tgt;
-       struct q2t_sess *sess;
-       int loop_id;
-       uint16_t lun;
+       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;
+       }
 
-       tgt = ha->tgt;
-       loop_id = GET_TARGET_ID(ha, iocb);
+       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);
 
-       /* Make it be in network byte order */
-       lun = swab16(iocb->lun);
+       /*
+        * In tgt_shutdown mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
 
-       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;
+       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;
+               }
        }
 
-       mcmd = kzalloc(sizeof(*mcmd), GFP_ATOMIC);
-       if (mcmd == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of mgmt cmd failed");
-               res = -ENOMEM;
-               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;
        }
 
-       mcmd->sess = sess;
-       mcmd->notify_entry = *iocb;
+       case ACCEPT_TGT_IO_TYPE:
+       {
+               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 */
+                               q2x_send_busy(ha, atio);
+#else
+                               q2x_send_term_exchange(ha, NULL, atio, 1);
+#endif
+                       } else {
+                               PRINT_INFO("qla2x00tgt(%ld): Unable to send "
+                                       "command to SCST, sending BUSY status",
+                                       ha->instance);
+                               q2x_send_busy(ha, atio);
+                       }
+               }
+       }
+       break;
+                       
+       case CONTINUE_TGT_IO_TYPE:
+       {
+               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:
+       {
+               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, (notify_entry_t *)pkt);
+               break;
 
-       switch (iocb->task_flags) {
-       case IMM_NTFY_CLEAR_ACA:
-               TRACE(TRACE_MGMT, "%s", "IMM_NTFY_CLEAR_ACA received");
-               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_ACA,
-                                        (uint8_t *)&lun, sizeof(lun),
-                                        SCST_ATOMIC, mcmd);
+       case NOTIFY_ACK_TYPE:
+               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));
+                       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->instance, 
+                                           le16_to_cpu(entry->status));
+                       }
+               } else {
+                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected NOTIFY_ACK "
+                                   "received", ha->instance);
+               }
                break;
 
-       case IMM_NTFY_TARGET_RESET:
-               TRACE(TRACE_MGMT, "%s", "IMM_NTFY_TARGET_RESET received");
-               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_TARGET_RESET,
-                                        (uint8_t *)&lun, sizeof(lun),
-                                        SCST_ATOMIC, mcmd);
+       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 IMM_NTFY_LUN_RESET:
-               TRACE(TRACE_MGMT, "%s", "IMM_NTFY_LUN_RESET received");
-               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_LUN_RESET,
-                                        (uint8_t *)&lun, sizeof(lun),
-                                        SCST_ATOMIC, mcmd);
+       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 IMM_NTFY_CLEAR_TS:
-               TRACE(TRACE_MGMT, "%s", "IMM_NTFY_CLEAR_TS received");
-               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_CLEAR_TASK_SET,
-                                        (uint8_t *)&lun, sizeof(lun),
-                                        SCST_ATOMIC, mcmd);
+       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->instance, 
+                                           entry->status);
+                       }
+               } else {
+                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected MODIFY_LUN "
+                           "received", (ha != NULL) ? (long)ha->instance : -1);
+               }
                break;
 
-       case IMM_NTFY_ABORT_TS:
-               TRACE(TRACE_MGMT, "%s", "IMM_NTFY_ABORT_TS received");
-               rc = scst_rx_mgmt_fn_lun(sess->scst_sess, SCST_ABORT_TASK_SET,
-                                        (uint8_t *)&lun, sizeof(lun),
-                                        SCST_ATOMIC, mcmd);
+       case ENABLE_LUN_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): Unknown task mgmt fn 0x%x",
-                           ha->host_no, iocb->task_flags);
+               PRINT_ERROR("qla2x00tgt(%ld): Received unknown response pkt "
+                    "type %x", ha->instance, pkt->entry_type);
                break;
        }
 
-       if (rc != 0) {
-               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_lun() failed: %d",
-                           ha->host_no, rc);
-               res = -EFAULT;
-               goto out_free;
-       }
+       tgt->irq_cmd_count--;
 
 out:
-       TRACE_EXIT_RES(res);
-       return res;
-
-out_free:
-       kfree(mcmd);
-       goto out;
+       TRACE_EXIT();
+       return;
 }
 
-/* ha->hardware_lock supposed to be held on entry */
-static int q2t_abort_task(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 q2t_async_event(uint16_t code, scsi_qla_host_t *ha,
+       uint16_t *mailbox)
 {
-       int res = 0, rc;
-       struct q2t_mgmt_cmd *mcmd;
-       struct q2t_sess *sess;
-       int loop_id;
-       uint32_t tag;
+       struct q2t_tgt *tgt = ha->tgt;
 
        TRACE_ENTRY();
 
-       loop_id = GET_TARGET_ID(ha, iocb);
-       tag = le16_to_cpu(iocb->seq_id);
-
-       sess = q2t_find_sess_by_lid(ha->tgt, loop_id);
-       if (sess == NULL) {
-               TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task abort for unexisting "
-                       "session", ha->host_no);
-               res = -EFAULT;
-               goto out;
-       }
-
-       mcmd = kzalloc(sizeof(*mcmd), GFP_ATOMIC);
-       if (mcmd == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of mgmt cmd failed");
-               res = -ENOMEM;
+       if (unlikely(tgt == NULL)) {
+               TRACE_DBG("ASYNC EVENT %#x, but no tgt (ha %p)", code, ha);
                goto out;
        }
 
-       mcmd->sess = sess;
-       mcmd->notify_entry = *iocb;
-
-       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);
-               res = -EFAULT;
-               goto out_free;
-       }
+       /*
+        * In tgt_shutdown mode we also should allow all requests to pass.
+        * Otherwise, some commands can stuck.
+        */
 
-out:
-       TRACE_EXIT_RES(res);
-       return res;
+       tgt->irq_cmd_count++;
 
-out_free:
-       kfree(mcmd);
-       goto out;
-}
+       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_ATIO_TRANSFER_ERR:     /* ATIO Queue Transfer Error */
+               TRACE(TRACE_MGMT, "System error async event %#x occured", code);
+               break;
 
-/* SCST Callback */
-static void q2t_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
-{
-       struct q2t_mgmt_cmd *mcmd;
-       unsigned long flags;
+       case MBA_LOOP_UP:
+       {
+               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;
+       }
 
-       TRACE_ENTRY();
+       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;
 
-       TRACE_MGMT_DBG("scst_mcmd (%p) status %#x state %#x", scst_mcmd,
-               scst_mcmd->status, scst_mcmd->state);
+       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;
 
-       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;
+       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;
        }
 
-       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);
-
-       /* scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); */
-       scst_mcmd->tgt_priv = NULL;
-       kfree(mcmd);
+       tgt->irq_cmd_count--;
 
 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)
+static int q2t_get_target_name(scsi_qla_host_t *ha, char **wwn)
 {
-       uint16_t status;
-       int loop_id;
-       uint32_t add_flags = 0;
-       int send_notify_ack = 1;
-
-       TRACE_ENTRY();
-
-       status = le16_to_cpu(iocb->status);
-       loop_id = GET_TARGET_ID(ha, iocb);
+       const int wwn_len = 3*WWN_SIZE+2;
+       int res = 0;
+       char *name;
 
-       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));
+       name = kmalloc(wwn_len, GFP_KERNEL);
+       if (name == NULL) {
+               TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt name failed");
+               res = -ENOMEM;
                goto out;
        }
 
-       TRACE_BUFFER("IMMED Notify Coming Up", iocb, sizeof(*iocb));
-
-       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;
+       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]);
 
-       case IMM_NTFY_IOCB_OVERFLOW:
-               PRINT_ERROR("qla2x00tgt(%ld): Cannot provide requested "
-                       "capability (IOCB overflow)", ha->host_no);
-               break;
+       *wwn = name;
 
-       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;
+out:
+       return res;
+}
 
-       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;
+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;
 
-       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;
+       TRACE_ENTRY();
 
-       case IMM_NTFY_RESOURCE:
-               PRINT_ERROR("qla2x00tgt(%ld): Out of resources, host %ld",
-                           ha->host_no, ha->host_no);
-               break;
+       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;
+       }
 
-       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;
+       /* 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;
+       }
 
-       default:
-               PRINT_ERROR("qla2x00tgt(%ld): Received unknown immediate "
-                       "notify status %x", ha->host_no, status);
-               break;
+       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:
-       if (send_notify_ack)
-               q2t_send_notify_ack(ha, iocb, add_flags, 0, 0);
-
-       TRACE_EXIT();
-       return;
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
-/* 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)
+/* Must be called under tgt_mutex */
+static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio)
 {
-       struct atio_entry *atio;
+       struct q2t_sess *sess = NULL;
+       fc_port_t *fcport = NULL;
+       uint16_t loop_id = 0xFFFF; /* to remove warning */
+       int rc;
 
        TRACE_ENTRY();
 
-       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 (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);
 
-       if (unlikely(ha->tgt == NULL)) {
-               TRACE_DBG("response pkt, but no tgt. ha %p tgt_flag %d",
-                       ha, ha->flags.enable_target_mode);
+       fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+       if (fcport == NULL) {
+               PRINT_ERROR("%s", "Allocation of tmp FC port failed");
                goto out;
        }
 
-       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;
+       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;
        }
 
-       switch (pkt->entry_type) {
-       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) {
-#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */
-                                       q2t_send_busy(ha, atio);
-#else
-                                       q2t_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 if (!ha->tgt->tgt_shutdown) {
-                       PRINT_ERROR("qla2x00tgt(%ld): ATIO, but target mode "
-                               "disabled", ha->host_no);
-               }
-               break;
+       sess = q2t_create_sess(ha, fcport, true);
 
-       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);
-               }
-               break;
+out_free_fcport:
+       kfree(fcport);
 
-       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);
-               }
-               break;
+out:
+       TRACE_EXIT_HRES((unsigned long)sess);
+       return sess;
+}
 
-       case IMMED_NOTIFY_TYPE:
-               TRACE_DBG("%s", "IMMED_NOTIFY");
-               q2t_handle_imm_notify(ha, (struct notify_entry *)pkt);
-               break;
+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;
 
-       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",
-                                 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)) {
-                               PRINT_ERROR("qla2x00tgt(%ld): NOTIFY_ACK "
-                                           "failed %x", ha->host_no,
-                                           le16_to_cpu(entry->status));
-                       }
-               } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected NOTIFY_ACK "
-                                   "received", ha->host_no);
-               }
-               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);
-                       tgt->modify_lun_expected--;
-                       if (entry->status != MODIFY_LUN_SUCCESS) {
-                               PRINT_ERROR("qla2x00tgt(%ld): MODIFY_LUN "
-                                           "failed %x", ha->host_no,
-                                           entry->status);
-                       }
-               } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected MODIFY_LUN "
-                               "received", (ha != NULL) ? ha->host_no : -1);
-               }
-               break;
+       TRACE_ENTRY();
 
-       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;
+       TRACE_MGMT_DBG("cmd %p", cmd);
 
-       default:
-               PRINT_INFO("qla2x00tgt(%ld): Received unknown response pkt "
-                    "type %x", ha->host_no, pkt->entry_type);
-               break;
+       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 */
        }
 
-out:
-       TRACE_EXIT();
-       return;
+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;
 }
 
-/* ha->hardware_lock supposed to be held on entry */
-/* called via callback from qla2xxx */
-static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha,
-       uint16_t *mailbox)
+static void q2t_sess_work_fn(struct work_struct *work)
 {
+       struct q2t_tgt *tgt = container_of(work, struct q2t_tgt, sess_work);
+
        TRACE_ENTRY();
 
-       sBUG_ON(ha == NULL);
+       TRACE_MGMT_DBG("Sess work (tgt %p)", tgt);
 
-       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);
-               goto out;
-       }
+       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);
 
-       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
+                * Prm must be in the list on the exec time to sync with
+                * tm_to_unknown clearance
                 */
-               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;
+               list_del(&prm->sess_works_list_entry);
 
-       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]));
-               break;
+               if (rc != 0) {
+                       PRINT_CRIT_ERROR("%s","Unable to complete sess work");
+                       q2t_free_cmd(prm->cmd);
+               }
 
-       case MBA_LOOP_UP:
-       default:
-               TRACE_MGMT_DBG("Async event %#x occured: ignore", code);
-               /* just don't DO anything */
-               break;
+               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);
 
-out:
        TRACE_EXIT();
        return;
 }
@@ -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);
-
-               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;
-               }
+               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 (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);
+
+               mutex_unlock(&ha->tgt_mutex);
 
-               TRACE_DBG("Enable lun for host %ld(%ld,%p)",
-                         ha->host_no, ha->host_no, ha);
-               tgt_data.enable_lun(ha);
+               list_for_each_entry_rcu(fcport, &ha->fcports, list) {
+                       q2t_fc_port_added(ha, fcport);
+               }
 
-               mutex_unlock(&qla_mgmt_mutex);
+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;
        }
-       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;
+       case DISABLE_TARGET_MODE:
+               if (!qla_tgt_mode_enabled(ha)) {
+                       PRINT_INFO("qla2x00tgt(%ld): Target mode already "
+                               "disabled", ha->instance);
+                       break;
                }
 
-               tgt = ha->tgt;
-               spin_unlock_irqrestore(&ha->hardware_lock, flags);
+               PRINT_INFO("qla2x00tgt(%ld): Disabling target mode",
+                       ha->instance);
 
-               mutex_unlock(&qla_mgmt_mutex);
+               sBUG_ON(ha->tgt == NULL);
 
-               TRACE_DBG("Shutting down host %ld(%ld,%p)",
-                         ha->host_no, ha->host_no, ha);
-               scst_unregister(tgt->scst_tgt);
-               /*
+               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");
+
+       /* 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);
 
-       scst_unregister_target_template(&tgt_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);
index 20c0d38..f7b24ac 100644 (file)
@@ -1,17 +1,18 @@
 /*
  *  qla2x00t.h
- *
+ *  
  *  Copyright (C) 2004 - 2009 Vladislav Bolkhovitin <vst@vlnb.net>
  *  Copyright (C) 2004 - 2005 Leonid Stoljar
  *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
+ *  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
 #ifndef __QLA2X00T_H
 #define __QLA2X00T_H
 
-/* Sneaky hack to skip redefinitions from qla_dbg.h */
-#define __QLA_DBG_H
 #include <qla_def.h>
-#include "qla2x_tgt_def.h"
+#include <qla2x_tgt.h>
+#include <qla2x_tgt_def.h>
 
 #include <scst_debug.h>
 
 /* 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
 #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
 
 /* 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)
 #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;
+};
+
 /*
  * Equi