Skip to contentSkip to conversation

target: qla2xxx LLD target mode and tcm_qla2xxx fabric module

RFC Superseded Nicholas A. Bellinger 1 participant 4 messages Dec 15, 2010

Subsystems (3)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover target: qla2xxx LLD target mode and tcm_qla2xxx fabric module

From: Nicholas Bellinger <[email protected]>

Greetings all,

Attached is the initial RFC series for adding QLA2XXX LLD target mode support against v8.03.04-k0 on .37-rc3, along a new tcm_qla2xxx.ko fabric module in complete (eg: non incremental) form in order to add a bit more context for review. At this point this code should be considered a 'for-39' item, and is being posted for early review now in order to get the discussion rolling on necessary changes required to enable target mode within the mainline qla2xxx LLD.

An overview of the current status and mini-howto is available here:

http://www.linux-iscsi.org/index.php/Qlogic

The series has been broken up into three individual patches:

This qla2xxx LLD changes in patch #1 and #2 are based on qla2x00t v8.02.01-k4 / .26 and qla2x00t-target.ko from the SCST project, and are a modern refactoring -> forward port of this logic to mainline qla2xxx code.

Patch #1 adds the main qla2x_target.c logic and associated headers into the qla2xxx LLD, including the main struct qla_target_template API for interaction with tcm_qla2xxx.

Patch #2 adds the various callbacks into struct qla_hw_data->qla2x_tmpl and qla2x_target.c logic within qla2xxx proper.

Patch #3 adds the seperate tcm_qla2xxx fabric module using a generic configfs control plane on modern v4 target mode code. This module defines it's own set of struct qla_target_template callers for interaction with qla2x_target.c -> qla2xxx LLD code.

The LLD changes and tcm_qla2xxx should currently be considered ALPHA -> BETA status, and thus far has been run exclusively using PCIe device passthrough with MSI-X interrupts into KVM guest. Large block I/O is stable, and is able to run for sustained periods at line-rate (1000 MB/sec) in non NPIV mode. The FC nexus handling in qla2x_target.c is stable and able to pass initial testing (shutdown, cable pull, etc). This also includes being able to shutdown tcm_qla2xx and disable target mode on an individual struct qla_hw_data, and then reload tcm_qla2xxx and recreate the configfs groups, re-enable target mode on the same qla_hw_data and everything work as expected w/o shutting down qla2xxx LLD.

There will be ongoing work to continue to improve and stabilize this code for future mainline acceptance. Code review and feedback are welcome.

Best Regards,

Nicholas Bellinger (3):
qla2xxx: Add target mode support into 2xxx series LLD code
qla2xxx: Enable 2xxx series LLD target mode support
tcm_qla2xxx: Add HW target mode I/O, control and TMR path code

 drivers/scsi/qla2xxx/Makefile                     |    4 +-
 drivers/scsi/qla2xxx/qla2x_target.c               | 5060 +++++++++++++++++++++
 drivers/scsi/qla2xxx/qla2x_target.h               |  290 ++
 drivers/scsi/qla2xxx/qla2x_tgt.h                  |  140 +
 drivers/scsi/qla2xxx/qla2x_tgt_def.h              |  717 +++
 drivers/scsi/qla2xxx/qla_attr.c                   |   14 +-
 drivers/scsi/qla2xxx/qla_def.h                    |   68 +-
 drivers/scsi/qla2xxx/qla_fw.h                     |   10 +
 drivers/scsi/qla2xxx/qla_gbl.h                    |   11 +
 drivers/scsi/qla2xxx/qla_gs.c                     |   14 +-
 drivers/scsi/qla2xxx/qla_init.c                   |  329 ++-
 drivers/scsi/qla2xxx/qla_iocb.c                   |  105 +-
 drivers/scsi/qla2xxx/qla_isr.c                    |  232 +-
 drivers/scsi/qla2xxx/qla_mbx.c                    |  172 +-
 drivers/scsi/qla2xxx/qla_mid.c                    |   35 +-
 drivers/scsi/qla2xxx/qla_os.c                     |  250 +-
 drivers/target/tcm_qla2xxx/Kbuild                 |    6 +
 drivers/target/tcm_qla2xxx/Kconfig                |    6 +
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h     |   91 +
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c | 1319 ++++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c   |  766 ++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h   |   48 +
 22 files changed, 9631 insertions(+), 56 deletions(-)
 create mode 100644 drivers/scsi/qla2xxx/qla2x_target.c
 create mode 100644 drivers/scsi/qla2xxx/qla2x_target.h
 create mode 100644 drivers/scsi/qla2xxx/qla2x_tgt.h
 create mode 100644 drivers/scsi/qla2xxx/qla2x_tgt_def.h
 create mode 100644 drivers/target/tcm_qla2xxx/Kbuild
 create mode 100644 drivers/target/tcm_qla2xxx/Kconfig
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h
Signature · 1 line
1.7.3.3
#1 lore raw

RFC 1/3 qla2xxx: Add target mode support into 2xxx series LLD code

From: Nicholas Bellinger <[email protected]>

This patch adds support for qla2xxx series target mode using a new target fabric module API based on SCST qla2x00t LLD code using a 8.02.01-k4 version, and refactors 80% of the qla2x00t module code into qla2xxx LLD code at qla2x_target.c using modern 8.03.04-k0 and v4.0 target/configfs
infrastructure presented as a seperate patch to tcm_qla2xxx.ko This patch
introduces a new target fabric module API in qla2x_tgt_def.h here:

struct qla_target_template {

       int (*handle_cmd)(scsi_qla_host_t *, struct q2t_cmd *, uint32_t, uint32_t, int, int, int);
       int (*handle_data)(struct q2t_cmd *);
       int (*handle_tmr)(struct q2t_mgmt_cmd *, uint32_t, uint8_t);
       void (*free_cmd)(struct q2t_cmd *);
       void (*free_session)(struct q2t_sess *);

       int (*check_initiator_node_acl)(scsi_qla_host_t *, void *, uint8_t *, uint16_t);
       struct q2t_sess *(*find_sess_by_loop_id)(scsi_qla_host_t *, const uint16_t);
       struct q2t_sess *(*find_sess_by_s_id)(scsi_qla_host_t *, const uint8_t *);
};

This is called via scsi_qla_host_t->hw->qla2x_tmpl within both existing qla2xxx LLD and new qla2x_target.c code to process LLD internal target mode operations and tcm_qla2xxx fabric module specific operations for callers within qla2x_target.c.

There are still a handful of TODO items including proper FC SRR support (service retransmission request) and TMRs currently using hardcoded LUN=0, but the main pieces are up and running, and properly handling nexus reset and tcm_qla2xxx module shutdown+restart w/o having to reload qla2xxx.ko

---
 drivers/scsi/qla2xxx/qla2x_target.c  | 5060 ++++++++++++++++++++++++++++++++++
 drivers/scsi/qla2xxx/qla2x_target.h  |  290 ++
 drivers/scsi/qla2xxx/qla2x_tgt.h     |  140 +
 drivers/scsi/qla2xxx/qla2x_tgt_def.h |  717 +++++
 4 files changed, 6207 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla2xxx/qla2x_target.c
 create mode 100644 drivers/scsi/qla2xxx/qla2x_target.h
 create mode 100644 drivers/scsi/qla2xxx/qla2x_tgt.h
 create mode 100644 drivers/scsi/qla2xxx/qla2x_tgt_def.h
drivers/scsi/qla2xxx/qla2x_target.c new file +5060 -0
new file mode 100644
index 0000000..59a8885
@@ -0,0 +1,5060 @@
+/*
+ *  qla2x_target.c SCSI LLD infrastructure for QLogic 22xx/23xx/24xx/25xx
+ *
+ *  based on qla2x00t.c code:
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <[email protected]>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2006 Nathaniel Clark <[email protected]>
+ *  Copyright (C) 2006 - 2010 ID7 Ltd.
+ *
+ *  Forward port and refactoring to modern qla2xxx and target/configfs
+ *
+ *  Copyright (C) 2010 Nicholas A. Bellinger <[email protected]>
+ *
+ *  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
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <asm/unaligned.h>
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_tmr.h>
+
+#include "qla_def.h"
+#include "qla2x_tgt.h"
+#include "qla2x_target.h"
+
+/*
+ * From scsi/fc/fc_fcp.h
+ */
+enum fcp_resp_rsp_codes {
+	FCP_TMF_CMPL = 0,
+	FCP_DATA_LEN_INVALID = 1,
+	FCP_CMND_FIELDS_INVALID = 2,
+	FCP_DATA_PARAM_MISMATCH = 3,
+	FCP_TMF_REJECTED = 4,
+	FCP_TMF_FAILED = 5,
+	FCP_TMF_INVALID_LUN = 9,
+};
+
+/*
+ * fc_pri_ta from scsi/fc/fc_fcp.h
+ */
+#define FCP_PTA_SIMPLE      0   /* simple task attribute */
+#define FCP_PTA_HEADQ       1   /* head of queue task attribute */
+#define FCP_PTA_ORDERED     2   /* ordered task attribute */
+#define FCP_PTA_ACA         4   /* auto. contigent allegiance */
+#define FCP_PTA_MASK        7   /* mask for task attribute field */
+#define FCP_PRI_SHIFT       3   /* priority field starts in bit 3 */
+#define FCP_PRI_RESVD_MASK  0x80        /* reserved bits in priority field */
+
+/*
+ * This driver calls qla2x00_req_pkt() and qla2x00_issue_marker(), which
+ * must be called under HW lock and could unlock/lock it inside.
+ * It isn't an issue, since in the current implementation on the time when
+ * those functions are called:
+ *
+ *   - Either context is IRQ and only IRQ handler can modify HW data,
+ *     including rings related fields,
+ *
+ *   - Or access to target mode variables from struct q2t_tgt doesn't
+ *     cross those functions boundaries, except tgt_stop, which
+ *     additionally protected by irq_cmd_count.
+ */
+
+#if 0
+#define TRACE_DBG(x...) printk(KERN_INFO x)
+#else
+#define TRACE_DBG(x...)
+#endif
+
+#if 0
+#define TRACE_MGMT_DBG(x...) printk(KERN_INFO x)
+#else
+#define TRACE_MGMT_DBG(x...)
+#endif
+
+#if 0
+#define TRACE_SCSI(x...) printk(KERN_INFO x)
+#else
+#define TRACE_SCSI(x...)
+#endif
+
+#if 0
+#define TRACE_SG(x...) printk(KERN_INFO x)
+#else
+#define TRACE_SG(x...)
+#endif
+
+#if 0
+#define TRACE_MGMT(x...) printk(KERN_INFO x)
+#else
+#define TRACE_MGMT(x...)
+#endif
+
+#define TRACE_BUFFER(x, buff, len)
+
+static int __q24_xmit_response(struct q2t_cmd *, int, uint8_t);
+
+/* Predefs for callbacks handed to qla2xxx(target) */
+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 int q2t_issue_task_mgmt(struct q2t_sess *sess, uint32_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 int q2t_unreg_sess(struct q2t_sess *sess);
+
+
+/*
+ * Global Variables
+ */
+
+static struct kmem_cache *q2t_cmd_cachep;
+static struct kmem_cache *q2t_mgmt_cmd_cachep;
+static mempool_t *q2t_mgmt_cmd_mempool;
+
+static DECLARE_RWSEM(q2t_unreg_rwsem);
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline void q2t_sess_get(struct q2t_sess *sess)
+{
+	sess->sess_ref++;
+	TRACE_DBG("sess %p, new sess_ref %d\n", sess, sess->sess_ref);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline void q2t_sess_put(struct q2t_sess *sess)
+{
+	TRACE_DBG("sess %p, new sess_ref %d\n", sess, sess->sess_ref-1);
+	BUG_ON(sess->sess_ref == 0);
+
+	sess->sess_ref--;
+	if (sess->sess_ref == 0)
+		q2t_unreg_sess(sess);
+}
+
+/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
+static struct q2t_sess *q2t_find_sess_by_port_name(
+	struct q2t_tgt *tgt,
+	const uint8_t *port_name)
+{
+	struct q2t_sess *sess;
+
+	list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
+		if ((sess->port_name[0] == port_name[0]) &&
+		    (sess->port_name[1] == port_name[1]) &&
+		    (sess->port_name[2] == port_name[2]) &&
+		    (sess->port_name[3] == port_name[3]) &&
+		    (sess->port_name[4] == port_name[4]) &&
+		    (sess->port_name[5] == port_name[5]) &&
+		    (sess->port_name[6] == port_name[6]) &&
+		    (sess->port_name[7] == port_name[7]))
+			return sess;
+	}
+
+	return NULL;
+}
+
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline request_t *q2t_req_pkt(scsi_qla_host_t *vha)
+{
+	return qla2x00_req_pkt(vha);
+}
+
+/* Might release hw lock, then reaquire!! */
+static inline int q2t_issue_marker(scsi_qla_host_t *vha, int vha_locked)
+{
+	/* Send marker if required */
+	if (unlikely(vha->marker_needed != 0)) {
+		int rc = qla2x00_issue_marker(vha, vha_locked);
+		if (rc != QLA_SUCCESS) {
+			printk(KERN_ERR "qla2x00t(%d): issue_marker() "
+				"failed\n", vha->vp_idx);
+		}
+		return rc;
+	}
+	return QLA_SUCCESS;
+}
+
+static inline
+scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *vha, uint8_t *d_id)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if ((vha->d_id.b.area != d_id[1]) || (vha->d_id.b.domain != d_id[0]))
+		return NULL;
+
+	if (vha->d_id.b.al_pa == d_id[2])
+		return vha;
+
+	if (IS_FWI2_CAPABLE(ha)) {
+		uint8_t vp_idx;
+		BUG_ON(ha->tgt_vp_map == NULL);
+		vp_idx = ha->tgt_vp_map[d_id[2]].idx;
+		if (likely(test_bit(vp_idx, ha->vp_idx_map)))
+			return ha->tgt_vp_map[vp_idx].vha;
+	}
+
+	return NULL;
+}
+
+static inline
+scsi_qla_host_t *q2t_find_host_by_vp_idx(scsi_qla_host_t *vha, uint16_t vp_idx)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if (vha->vp_idx == vp_idx)
+		return vha;
+
+	if (IS_FWI2_CAPABLE(ha)) {
+		BUG_ON(ha->tgt_vp_map == NULL);
+		if (likely(test_bit(vp_idx, ha->vp_idx_map)))
+			return ha->tgt_vp_map[vp_idx].vha;
+	}
+
+	return NULL;
+}
+
+void q24_atio_pkt_all_vps(scsi_qla_host_t *vha, atio7_entry_t *atio)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	switch (atio->entry_type) {
+	case ATIO_TYPE7:
+	{
+		scsi_qla_host_t *host = q2t_find_host_by_d_id(vha, atio->fcp_hdr.d_id);
+		if (unlikely(NULL == host)) {
+			printk(KERN_ERR "qla2x00t(%d): Received ATIO_TYPE7 "
+				"with unknown d_id %x:%x:%x\n", vha->vp_idx,
+				atio->fcp_hdr.d_id[0], atio->fcp_hdr.d_id[1],
+				atio->fcp_hdr.d_id[2]);
+			break;
+		}
+		q24_atio_pkt(host, atio);
+		break;
+	}
+
+	case IMMED_NOTIFY_TYPE:
+	{
+		scsi_qla_host_t *host = vha;
+
+		if (IS_FWI2_CAPABLE(ha)) {
+			notify24xx_entry_t *entry = (notify24xx_entry_t *)atio;
+			if ((entry->vp_index != 0xFF) &&
+			    (entry->nport_handle != 0xFFFF)) {
+				host = q2t_find_host_by_vp_idx(vha,
+							entry->vp_index);
+				if (unlikely(!host)) {
+					printk(KERN_ERR "qla2x00t(%d): Received "
+						"ATIO (IMMED_NOTIFY_TYPE) "
+						"with unknown vp_index %d\n",
+						vha->vp_idx, entry->vp_index);
+					break;
+				}
+			}
+		}
+		q24_atio_pkt(host, atio);
+		break;
+	}
+
+	default:
+		printk(KERN_ERR "qla2x00t(%d): Received unknown ATIO atio "
+		     "type %x\n", vha->vp_idx, atio->entry_type);
+		break;
+	}
+
+	return;
+}
+
+void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	switch (pkt->entry_type) {
+	case CTIO_TYPE7:
+	{
+		ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt;
+		scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
+						entry->vp_index);
+		if (unlikely(!host)) {
+			printk(KERN_ERR "qla2x00t(%d): Response pkt (CTIO_TYPE7) "
+				"received, with unknown vp_index %d\n",
+				vha->vp_idx, entry->vp_index);
+			break;
+		}
+		q2t_response_pkt(host, pkt);
+		break;
+	}
+
+	case IMMED_NOTIFY_TYPE:
+	{
+		scsi_qla_host_t *host = vha;
+		if (IS_FWI2_CAPABLE(ha)) {
+			notify24xx_entry_t *entry = (notify24xx_entry_t *)pkt;
+			host = q2t_find_host_by_vp_idx(vha, entry->vp_index);
+			if (unlikely(!host)) {
+				printk(KERN_ERR "qla2x00t(%d): Response pkt "
+					"(IMMED_NOTIFY_TYPE) received, "
+					"with unknown vp_index %d\n",
+					vha->vp_idx, entry->vp_index);
+				break;
+			}
+		}
+		q2t_response_pkt(host, pkt);
+		break;
+	}
+
+	case NOTIFY_ACK_TYPE:
+	{
+		scsi_qla_host_t *host = vha;
+		if (IS_FWI2_CAPABLE(ha)) {
+			nack24xx_entry_t *entry = (nack24xx_entry_t *)pkt;
+			if (0xFF != entry->vp_index) {
+				host = q2t_find_host_by_vp_idx(vha,
+						entry->vp_index);
+				if (unlikely(!host)) {
+					printk(KERN_ERR "qla2x00t(%d): Response "
+						"pkt (NOTIFY_ACK_TYPE) "
+						"received, with unknown "
+						"vp_index %d\n", vha->vp_idx,
+						entry->vp_index);
+					break;
+				}
+			}
+		}
+		q2t_response_pkt(host, pkt);
+		break;
+	}
+
+	case ABTS_RECV_24XX:
+	{
+		abts24_recv_entry_t *entry = (abts24_recv_entry_t *)pkt;
+		scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
+						entry->vp_index);
+		if (unlikely(!host)) {
+			printk(KERN_ERR "qla2x00t(%d): Response pkt "
+				"(ABTS_RECV_24XX) received, with unknown "
+				"vp_index %d\n", vha->vp_idx, entry->vp_index);
+			break;
+		}
+		q2t_response_pkt(host, pkt);
+		break;
+	}
+
+	case ABTS_RESP_24XX:
+	{
+		abts24_resp_entry_t *entry = (abts24_resp_entry_t *)pkt;
+		scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
+						entry->vp_index);
+		if (unlikely(!host)) {
+			printk(KERN_ERR "qla2x00t(%d): Response pkt "
+				"(ABTS_RECV_24XX) received, with unknown "
+				"vp_index %d\n", vha->vp_idx, entry->vp_index);
+			break;
+		}
+		q2t_response_pkt(host, pkt);
+		break;
+	}
+
+	default:
+		q2t_response_pkt(vha, pkt);
+		break;
+	}
+
+}
+
+
+/* ha->hardware_lock supposed to be held on entry */
+static void q2t_free_session_done(struct q2t_sess *sess)
+{
+	struct q2t_tgt *tgt;
+	scsi_qla_host_t *vha = sess->vha;
+	struct qla_hw_data *ha = vha->hw;
+
+	tgt = sess->tgt;
+	/*
+	 * Release the target session for FC Nexus from fabric module code.
+	 */
+	if (sess->se_sess != NULL)
+		ha->qla2x_tmpl->free_session(sess);
+
+	TRACE_MGMT_DBG("Unregistration of sess %p finished\n", sess);
+
+	kfree(sess);
+
+	if (!tgt)
+		return;
+
+	TRACE_DBG("empty(sess_list) %d sess_count %d\n",
+	      list_empty(&tgt->sess_list), tgt->sess_count);
+
+	ha = tgt->ha;
+	/*
+	 * We need to protect against race, when tgt is freed before or
+	 * inside wake_up()
+	 */
+	tgt->sess_count--;
+	if (tgt->sess_count == 0)
+		wake_up_all(&tgt->waitQ);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int q2t_unreg_sess(struct q2t_sess *sess)
+{
+	int res = 1;
+
+	BUG_ON(sess == NULL);
+	BUG_ON(sess->sess_ref != 0);
+
+	list_del(&sess->sess_list_entry);
+
+	if (sess->deleted)
+		list_del(&sess->del_list_entry);
+
+	printk(KERN_INFO "qla2x00t(%d): %ssession for loop_id %d deleted\n",
+		sess->vha->vp_idx, sess->local ? "local " : "",
+		sess->loop_id);
+
+	q2t_free_session_done(sess);
+
+	return res;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int q2t_reset(scsi_qla_host_t *vha, void *iocb, int mcmd)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_sess *sess = NULL;
+	uint16_t lun = 0, loop_id;
+	int res = 0;
+	uint8_t s_id[3];
+
+	memset(&s_id, 0, 3);
+
+	if (IS_FWI2_CAPABLE(ha)) {
+		notify24xx_entry_t *n = (notify24xx_entry_t *)iocb;
+		loop_id = le16_to_cpu(n->nport_handle);
+		s_id[0] = n->port_id[0];
+		s_id[1] = n->port_id[1];
+		s_id[2] = n->port_id[2];
+	} else
+		loop_id = GET_TARGET_ID(ha, (notify_entry_t *)iocb);
+
+	if (loop_id == 0xFFFF) {
+#warning FIXME: Re-enable Global event handling..
+#if 0
+		/* Global event */
+		printk("Processing q2t_reset with loop_id=0xffff global event............\n");
+		atomic_inc(&ha->q2t_tgt->tgt_global_resets_count);
+		q2t_clear_tgt_db(ha->q2t_tgt, 1);
+		if (!list_empty(&ha->q2t_tgt->sess_list)) {
+			sess = list_entry(ha->q2t_tgt->sess_list.next,
+				typeof(*sess), sess_list_entry);
+			switch (mcmd) {
+			case Q2T_NEXUS_LOSS_SESS:
+				mcmd = Q2T_NEXUS_LOSS;
+				break;
+			case Q2T_ABORT_ALL_SESS:
+				mcmd = Q2T_ABORT_ALL;
+				break;
+			case Q2T_NEXUS_LOSS:
+			case Q2T_ABORT_ALL:
+				break;
+			default:
+				printk(KERN_ERR "qla2x00t(%d): Not allowed "
+					"command %x in %s", vha->vp_idx,
+					mcmd, __func__);
+				sess = NULL;
+				break;
+			}
+		} else
+			sess = NULL;
+#endif
+	} else {
+		sess = ha->qla2x_tmpl->find_sess_by_loop_id(vha, loop_id);
+	}
+
+	TRACE_DBG("Using sess for q2t_reset: %p\n", sess);
+	if (!sess) {
+		res = -ESRCH;
+		ha->q2t_tgt->tm_to_unknown = 1;
+		return res;
+	}
+
+	printk(KERN_INFO "scsi(%ld): resetting (session %p from port "
+		"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, "
+		"mcmd %x, loop_id %d)\n", vha->host_no, sess,
+		sess->port_name[0], sess->port_name[1],
+		sess->port_name[2], sess->port_name[3],
+		sess->port_name[4], sess->port_name[5],
+		sess->port_name[6], sess->port_name[7],
+		mcmd, loop_id);
+
+	return q2t_issue_task_mgmt(sess, lun, sizeof(lun), mcmd,
+				iocb, Q24_MGMT_SEND_NACK);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess)
+{
+	struct q2t_tgt *tgt = sess->tgt;
+	uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5;
+	bool schedule;
+
+	if (sess->deleted)
+		return;
+	/*
+	 * If the list is empty, then, most likely, the work isn't
+	 * scheduled.
+	 */
+	schedule = list_empty(&tgt->del_sess_list);
+
+	TRACE_DBG("Scheduling sess %p for deletion (schedule %d)", sess,
+		schedule);
+	list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
+	sess->deleted = 1;
+	sess->expires = jiffies + dev_loss_tmo * HZ;
+
+	printk(KERN_INFO "qla2x00t(%d): session for port %02x:%02x:%02x:"
+		"%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for "
+		"deletion in %d secs\n", sess->vha->vp_idx,
+		sess->port_name[0], sess->port_name[1],
+		sess->port_name[2], sess->port_name[3],
+		sess->port_name[4], sess->port_name[5],
+		sess->port_name[6], sess->port_name[7],
+		sess->loop_id, dev_loss_tmo);
+
+	if (schedule)
+		schedule_delayed_work(&tgt->sess_del_work,
+				jiffies - sess->expires);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void q2t_clear_tgt_db(struct q2t_tgt *tgt, bool local_only)
+{
+	struct q2t_sess *sess, *sess_tmp;
+
+	list_for_each_entry_safe(sess, sess_tmp, &tgt->sess_list,
+					sess_list_entry) {
+		if (local_only) {
+			if (!sess->local)
+				continue;
+			q2t_schedule_sess_for_deletion(sess);
+		} else
+			q2t_sess_put(sess);
+	}
+
+	/* At this point tgt could be already dead */
+}
+
+static int q24_get_loop_id(scsi_qla_host_t *vha, const uint8_t *s_id,
+	uint16_t *loop_id)
+{
+	struct qla_hw_data *ha = vha->hw;
+	dma_addr_t gid_list_dma;
+	struct gid_list_info *gid_list;
+	char *id_iter;
+	int res, rc, i;
+	uint16_t entries;
+
+	gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
+			&gid_list_dma, GFP_KERNEL);
+	if (!gid_list) {
+		printk(KERN_ERR "qla2x00t(%d): DMA Alloc failed of %zd\n",
+			vha->vp_idx, GID_LIST_SIZE);
+		return -ENOMEM;
+	}
+
+	/* Get list of logged in devices */
+	rc = qla2x00_get_id_list(vha, gid_list, gid_list_dma, &entries);
+	if (rc != QLA_SUCCESS) {
+		printk(KERN_ERR "qla2x00t(%d): get_id_list() failed: %x\n",
+			vha->vp_idx, 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 == s_id[2]) &&
+		    (gid->area == s_id[1]) &&
+		    (gid->domain == s_id[0])) {
+			*loop_id = le16_to_cpu(gid->loop_id);
+			res = 0;
+			break;
+		}
+		id_iter += ha->gid_list_info_size;
+	}
+
+out_free_id_list:
+	dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, gid_list, gid_list_dma);
+
+	return res;
+}
+
+static bool q2t_check_fcport_exist(scsi_qla_host_t *vha, struct q2t_sess *sess)
+{
+	struct qla_hw_data *ha = vha->hw;
+	bool res, found = false;
+	int rc, i;
+	uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */
+	uint16_t entries;
+	void *pmap;
+	int pmap_len;
+	fc_port_t *fcport;
+	int global_resets;
+
+retry:
+	global_resets = atomic_read(&ha->q2t_tgt->tgt_global_resets_count);
+
+	rc = qla2x00_get_node_name_list(vha, &pmap, &pmap_len);
+	if (rc != QLA_SUCCESS) {
+		res = false;
+		goto out;
+	}
+
+	if (IS_FWI2_CAPABLE(ha)) {
+		struct qla_port24_data *pmap24 = pmap;
+
+		entries = pmap_len/sizeof(*pmap24);
+
+		for (i = 0; i < entries; ++i) {
+			if ((sess->port_name[0] == pmap24[i].port_name[0]) &&
+			    (sess->port_name[1] == pmap24[i].port_name[1]) &&
+			    (sess->port_name[2] == pmap24[i].port_name[2]) &&
+			    (sess->port_name[3] == pmap24[i].port_name[3]) &&
+			    (sess->port_name[4] == pmap24[i].port_name[4]) &&
+			    (sess->port_name[5] == pmap24[i].port_name[5]) &&
+			    (sess->port_name[6] == pmap24[i].port_name[6]) &&
+			    (sess->port_name[7] == pmap24[i].port_name[7])) {
+				loop_id = le16_to_cpu(pmap24[i].loop_id);
+				found = true;
+				break;
+			}
+		}
+	} else {
+		struct qla_port23_data *pmap2x = pmap;
+
+		entries = pmap_len/sizeof(*pmap2x);
+
+		for (i = 0; i < entries; ++i) {
+			if ((sess->port_name[0] == pmap2x[i].port_name[0]) &&
+			    (sess->port_name[1] == pmap2x[i].port_name[1]) &&
+			    (sess->port_name[2] == pmap2x[i].port_name[2]) &&
+			    (sess->port_name[3] == pmap2x[i].port_name[3]) &&
+			    (sess->port_name[4] == pmap2x[i].port_name[4]) &&
+			    (sess->port_name[5] == pmap2x[i].port_name[5]) &&
+			    (sess->port_name[6] == pmap2x[i].port_name[6]) &&
+			    (sess->port_name[7] == pmap2x[i].port_name[7])) {
+				loop_id = le16_to_cpu(pmap2x[i].loop_id);
+				found = true;
+				break;
+			}
+		}
+	}
+
+	kfree(pmap);
+
+	if (!found) {
+		res = false;
+		goto out;
+	}
+
+	printk(KERN_INFO "q2t_check_fcport_exist(): loop_id %d", loop_id);
+
+	fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+	if (fcport == NULL) {
+		printk(KERN_ERR "qla2x00t(%d): Allocation of tmp FC port failed",
+			vha->vp_idx);
+		res = false;
+		goto out;
+	}
+
+	fcport->loop_id = loop_id;
+
+	rc = qla2x00_get_port_database(vha, fcport, 0);
+	if (rc != QLA_SUCCESS) {
+		printk(KERN_ERR "qla2x00t(%d): Failed to retrieve fcport "
+			"information -- get_port_database() returned %x "
+			"(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
+		res = false;
+		goto out_free_fcport;
+	}
+
+	if (global_resets != atomic_read(&ha->q2t_tgt->tgt_global_resets_count)) {
+		TRACE_MGMT_DBG("qla2x00t(%d): global reset during session "
+			"discovery (counter was %d, new %d), retrying",
+			vha->vp_idx, global_resets,
+			atomic_read(&ha->q2t_tgt->tgt_global_resets_count));
+		goto retry;
+	}
+
+	TRACE_MGMT_DBG("Updating sess %p s_id %x:%x:%x, "
+		"loop_id %d) to d_id %x:%x:%x, loop_id %d", sess,
+		sess->s_id.b.domain, sess->s_id.b.al_pa,
+		sess->s_id.b.area, sess->loop_id, fcport->d_id.b.domain,
+		fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->loop_id);
+
+	sess->s_id = fcport->d_id;
+	sess->loop_id = fcport->loop_id;
+	sess->conf_compl_supported = fcport->conf_compl_supported;
+
+	res = true;
+
+out_free_fcport:
+	kfree(fcport);
+
+out:
+	return res;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void q2t_undelete_sess(struct q2t_sess *sess)
+{
+	BUG_ON(!sess->deleted);
+
+	list_del(&sess->del_list_entry);
+	sess->deleted = 0;
+}
+
+static void q2t_del_sess_work_fn(struct delayed_work *work)
+{
+	struct q2t_tgt *tgt = container_of(work, struct q2t_tgt,
+					sess_del_work);
+	scsi_qla_host_t *vha = tgt->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_sess *sess;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	while (!list_empty(&tgt->del_sess_list)) {
+		sess = list_entry(tgt->del_sess_list.next, typeof(*sess),
+				del_list_entry);
+		if (time_after_eq(jiffies, sess->expires)) {
+			bool cancel;
+
+			q2t_undelete_sess(sess);
+
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+			cancel = q2t_check_fcport_exist(vha, sess);
+			spin_lock_irqsave(&ha->hardware_lock, flags);
+
+			if (cancel) {
+				if (sess->deleted) {
+					/*
+					 * sess was again deleted while we were
+					 * discovering it
+					 */
+					continue;
+				}
+
+				printk(KERN_INFO "qla2x00t(%d): cancel deletion of "
+					"session for port %02x:%02x:%02x:%02x:%02x:"
+					"%02x:%02x:%02x (loop ID %d), because it isn't"
+					" deleted by firmware", vha->vp_idx,
+					sess->port_name[0], sess->port_name[1],
+					sess->port_name[2], sess->port_name[3],
+					sess->port_name[4], sess->port_name[5],
+					sess->port_name[6], sess->port_name[7],
+					sess->loop_id);
+			} else {
+				TRACE_MGMT_DBG("Timeout: sess %p about to be "
+					"deleted\n", sess);
+				q2t_sess_put(sess);
+			}
+		} else {
+			schedule_delayed_work(&tgt->sess_del_work,
+				jiffies - sess->expires);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/*
+ * 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 *vha,
+	fc_port_t *fcport,
+	bool local)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_sess *sess;
+	unsigned char be_sid[3];
+
+	/* Check to avoid double sessions */
+#if 0
+	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.domain,
+				sess->s_id.b.al_pa, sess->s_id.b.area,
+				sess->loop_id, fcport->d_id.b.domain,
+				fcport->d_id.b.al_pa, fcport->d_id.b.area,
+				fcport->loop_id);
+
+			if (sess->deleted)
+				q2t_undelete_sess(sess);
+
+			q2t_sess_get(sess);
+			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 = 0;
+			spin_unlock_irq(&ha->hardware_lock);
+			goto out;
+		}
+	}
+	spin_unlock_irq(&ha->hardware_lock);
+#endif
+	/* We are under tgt_mutex, so a new sess can't be added behind us */
+
+	sess = kzalloc(sizeof(*sess), GFP_KERNEL);
+	if (!sess) {
+		printk(KERN_ERR "qla2x00t(%u): session allocation failed, "
+			"all commands from port %02x:%02x:%02x:%02x:"
+			"%02x:%02x:%02x:%02x will be refused", vha->vp_idx,
+			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]);
+
+		return NULL;
+	}
+
+	sess->sess_ref = 2; /* plus 1 extra ref, see above */
+	sess->tgt = ha->q2t_tgt;
+	sess->vha = vha;
+
+	sess->s_id = fcport->d_id;
+	sess->loop_id = fcport->loop_id;
+	sess->local = local;
+
+	TRACE_MGMT_DBG("Adding sess %p to tgt %p via ->set_sess_by_s_id()\n",
+			sess, ha->q2t_tgt);
+
+	be_sid[0] = sess->s_id.b.domain;
+	be_sid[1] = sess->s_id.b.area;
+	be_sid[2] = sess->s_id.b.al_pa;
+	/*
+	 * Determine if this fc_port->port_name is allowed to access
+	 * target mode using explict NodeACLs+MappedLUNs, or using
+	 * TPG demo mode.  If this is successful a target mode FC nexus
+	 * is created.
+	 */
+	if (ha->qla2x_tmpl->check_initiator_node_acl(vha, sess, &be_sid[0],
+				fcport->loop_id) < 0) {
+		kfree(sess);
+		return NULL;
+	}
+
+	sess->conf_compl_supported = fcport->conf_compl_supported;
+	BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name));
+	memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name));
+
+	spin_lock_irq(&ha->hardware_lock);
+	list_add_tail(&sess->sess_list_entry, &ha->q2t_tgt->sess_list);
+	ha->q2t_tgt->sess_count++;
+	spin_unlock_irq(&ha->hardware_lock);
+
+	printk(KERN_INFO "qla2x00t(%d): %ssession for wwn %02x:%02x:%02x:%02x:"
+		"%02x:%02x:%02x:%02x (loop_id %d, s_id %x:%x:%x, confirmed"
+		" completion %ssupported) added\n", vha->vp_idx, 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],
+		fcport->loop_id, sess->s_id.b.domain, sess->s_id.b.area,
+		sess->s_id.b.al_pa, sess->conf_compl_supported ? "" : "not ");
+
+	return sess;
+}
+
+/*
+ * Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port()
+ */
+void q2t_fc_port_added(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_tgt *tgt = ha->q2t_tgt;
+	struct q2t_sess *sess;
+	unsigned char s_id[3];
+
+	if (!tgt || (fcport->port_type != FCT_INITIATOR))
+		return;
+#warning FIXME: Convert to atomic_t
+	if (tgt->tgt_stop)
+		return;
+
+	spin_lock_irq(&ha->hardware_lock);
+	sess = q2t_find_sess_by_port_name(tgt, fcport->port_name);
+	if (!sess) {
+		spin_unlock_irq(&ha->hardware_lock);
+
+		memset(&s_id, 0, 3);
+		s_id[0] = fcport->d_id.b.domain;
+		s_id[1] = fcport->d_id.b.area;
+		s_id[2] = fcport->d_id.b.al_pa;
+
+		mutex_lock(&ha->tgt_mutex);
+		sess = q2t_create_sess(vha, fcport, false);
+		mutex_unlock(&ha->tgt_mutex);
+
+		spin_lock_irq(&ha->hardware_lock);
+		if (sess != NULL)
+			q2t_sess_put(sess); /* put the extra creation ref */
+	} else {
+		if (sess->deleted) {
+			q2t_undelete_sess(sess);
+
+			printk(KERN_INFO "qla2x00t(%u): %ssession for port %02x:"
+				"%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
+				"reappeared\n", vha->vp_idx,
+				sess->local ? "local " : "", sess->port_name[0],
+				sess->port_name[1], sess->port_name[2],
+				sess->port_name[3], sess->port_name[4],
+				sess->port_name[5], sess->port_name[6],
+				sess->port_name[7], sess->loop_id);
+
+			TRACE_MGMT_DBG("Reappeared sess %p\n", sess);
+		}
+		sess->s_id = fcport->d_id;
+		sess->loop_id = fcport->loop_id;
+		sess->conf_compl_supported = fcport->conf_compl_supported;
+	}
+
+	if (sess->local) {
+		printk(KERN_INFO "qla2x00t(%u): local session for "
+			"port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
+			"(loop ID %d) became global\n", vha->vp_idx,
+			fcport->port_name[0], fcport->port_name[1],
+			fcport->port_name[2], fcport->port_name[3],
+			fcport->port_name[4], fcport->port_name[5],
+			fcport->port_name[6], fcport->port_name[7],
+			sess->loop_id);
+		sess->local = 0;
+	}
+	spin_unlock_irq(&ha->hardware_lock);
+}
+
+void q2t_fc_port_deleted(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_tgt *tgt = ha->q2t_tgt;
+	struct q2t_sess *sess;
+
+	if (!tgt || (fcport->port_type != FCT_INITIATOR))
+		return;
+#warning FIXME: Convert to atomic_t
+	if (tgt->tgt_stop)
+		return;
+
+	spin_lock_irq(&ha->hardware_lock);
+	sess = q2t_find_sess_by_port_name(tgt, fcport->port_name);
+	if (!sess) {
+		spin_unlock_irq(&ha->hardware_lock);
+		return;
+	}
+
+	TRACE_MGMT_DBG("q2t_fc_port_deleted %p", sess);
+
+	sess->local = 1;
+	q2t_schedule_sess_for_deletion(sess);
+	spin_unlock_irq(&ha->hardware_lock);
+}
+
+static inline int test_tgt_sess_count(struct q2t_tgt *tgt)
+{
+	struct qla_hw_data *ha = tgt->ha;
+	unsigned long flags;
+	int res;
+	/*
+	 * We need to protect against race, when tgt is freed before or
+	 * inside wake_up()
+	 */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	TRACE_DBG("tgt %p, empty(sess_list)=%d sess_count=%d\n",
+	      tgt, list_empty(&tgt->sess_list), tgt->sess_count);
+	res = (tgt->sess_count == 0);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+
+/* Called by tcm_qla2xxx configfs code */
+void q2t_target_stop(struct q2t_tgt *tgt)
+{
+	scsi_qla_host_t *vha = tgt->vha;
+	struct qla_hw_data *ha = tgt->ha;
+
+	if (tgt->tgt_stop || tgt->tgt_stopped) {
+		printk(KERN_ERR "Already in tgt->tgt_stop or tgt_stopped state\n");
+		dump_stack();
+		return;
+	}
+
+	TRACE_DBG("Stopping target for host %ld(%p)\n", vha->host_no, vha);
+
+	/*
+	 * Mutex needed to sync with q2t_fc_port_[added,deleted].
+	 * Lock is needed, because we still can get an incoming packet.
+	 */
+
+	mutex_lock(&ha->tgt_mutex);
+	spin_lock_irq(&ha->hardware_lock);
+	tgt->tgt_stop = 1;
+	q2t_clear_tgt_db(tgt, false);
+	spin_unlock_irq(&ha->hardware_lock);
+	mutex_unlock(&ha->tgt_mutex);
+
+	cancel_delayed_work_sync(&tgt->sess_del_work);
+
+	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_MGMT_DBG("Waiting for tgt %p: list_empty(sess_list)=%d "
+		"sess_count=%d\n", tgt, list_empty(&tgt->sess_list),
+		tgt->sess_count);
+
+	wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+
+	/* Big hammer */
+	if (!ha->host_shutting_down && qla_tgt_mode_enabled(vha))
+		qla2x00_disable_tgt_mode(vha);
+
+	/* Wait for sessions to clear out (just in case) */
+	wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+
+	TRACE_MGMT_DBG("Waiting for %d IRQ commands to complete (tgt %p)",
+		tgt->irq_cmd_count, tgt);
+
+	mutex_lock(&ha->tgt_mutex);
+	spin_lock_irq(&ha->hardware_lock);
+	while (tgt->irq_cmd_count != 0) {
+		spin_unlock_irq(&ha->hardware_lock);
+		udelay(2);
+		spin_lock_irq(&ha->hardware_lock);
+	}
+	tgt->tgt_stop = 0;
+	tgt->tgt_stopped = 1;
+	spin_unlock_irq(&ha->hardware_lock);
+	mutex_unlock(&ha->tgt_mutex);
+
+	TRACE_MGMT_DBG("Stop of tgt %p finished", tgt);
+}
+EXPORT_SYMBOL(q2t_target_stop);
+
+int q2t_target_release(struct q2t_tgt *tgt)
+{
+	struct qla_hw_data *ha = tgt->ha;
+
+	if ((ha->q2t_tgt != NULL) && !tgt->tgt_stopped)
+		q2t_target_stop(tgt);
+
+	ha->q2t_tgt = NULL;
+
+	TRACE_MGMT_DBG("Release of tgt %p finished\n", tgt);
+
+	kfree(tgt);
+	return 0;
+}
+EXPORT_SYMBOL(q2t_target_release);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int q2t_sched_sess_work(struct q2t_tgt *tgt, int type,
+	const void *param, unsigned int param_size)
+{
+	struct q2t_sess_work_param *prm;
+	unsigned long flags;
+
+	prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
+	if (!prm ) {
+		printk(KERN_ERR "qla2x00t(%d): Unable to create session "
+			"work, command will be refused", 0);
+		return -ENOMEM;
+	}
+
+	TRACE_MGMT_DBG("Scheduling work (type %d, prm %p) to find session for "
+		"param %p (size %d, tgt %p)\n", type, prm, param, param_size, tgt);
+
+	BUG_ON(param_size > (sizeof(*prm) -
+		offsetof(struct q2t_sess_work_param, cmd)));
+
+	prm->type = type;
+	memcpy(&prm->cmd, param, param_size);
+
+	spin_lock_irqsave(&tgt->sess_work_lock, flags);
+	if (!tgt->sess_works_pending)
+		tgt->tm_to_unknown = 0;
+	list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list);
+	tgt->sess_works_pending = 1;
+	spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+	schedule_work(&tgt->sess_work);
+
+	return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q2x_modify_command_count(scsi_qla_host_t *vha, int cmd_count,
+	int imm_count)
+{
+	struct qla_hw_data *ha = vha->hw;
+	modify_lun_entry_t *pkt;
+
+	printk(KERN_INFO "Sending MODIFY_LUN (ha=%p, cmd=%d, imm=%d)\n",
+		  ha, cmd_count, imm_count);
+
+	/* Sending marker isn't necessary, since we called from ISR */
+
+	pkt = (modify_lun_entry_t *)q2t_req_pkt(vha);
+	if (!pkt) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		return;
+	}
+
+	ha->q2t_tgt->modify_lun_expected++;
+
+	pkt->entry_type = MODIFY_LUN_TYPE;
+	pkt->entry_count = 1;
+	if (cmd_count < 0) {
+		pkt->operators = MODIFY_LUN_CMD_SUB;	/* Subtract from command count */
+		pkt->command_count = -cmd_count;
+	} else if (cmd_count > 0) {
+		pkt->operators = MODIFY_LUN_CMD_ADD;	/* Add to command count */
+		pkt->command_count = cmd_count;
+	}
+
+	if (imm_count < 0) {
+		pkt->operators |= MODIFY_LUN_IMM_SUB;
+		pkt->immed_notify_count = -imm_count;
+	} else if (imm_count > 0) {
+		pkt->operators |= MODIFY_LUN_IMM_ADD;
+		pkt->immed_notify_count = imm_count;
+	}
+
+	pkt->timeout = 0;	/* Use default */
+
+	TRACE_BUFFER("MODIFY LUN packet data", pkt, REQUEST_ENTRY_SIZE);
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q2x_send_notify_ack(scsi_qla_host_t *vha, 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)
+{
+	struct qla_hw_data *ha = vha->hw;
+	nack_entry_t *ntfy;
+
+	TRACE_DBG("Sending NOTIFY_ACK (ha=%p)\n", ha);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	ntfy = (nack_entry_t *)q2t_req_pkt(vha);
+	if (!ntfy) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		return;
+	}
+
+	if (ha->q2t_tgt != NULL)
+		ha->q2t_tgt->notify_ack_expected++;
+
+	ntfy->entry_type = NOTIFY_ACK_TYPE;
+	ntfy->entry_count = 1;
+	SET_TARGET_ID(ha, ntfy->target, GET_TARGET_ID(ha, iocb));
+	ntfy->status = iocb->status;
+	ntfy->task_flags = iocb->task_flags;
+	ntfy->seq_id = iocb->seq_id;
+	/* Do not increment here, the chip isn't decrementing */
+	/* ntfy->flags = __constant_cpu_to_le16(NOTIFY_ACK_RES_COUNT); */
+	ntfy->flags |= cpu_to_le16(add_flags);
+	ntfy->srr_rx_id = iocb->srr_rx_id;
+	ntfy->srr_rel_offs = iocb->srr_rel_offs;
+	ntfy->srr_ui = iocb->srr_ui;
+	ntfy->srr_flags = cpu_to_le16(srr_flags);
+	ntfy->srr_reject_code = cpu_to_le16(srr_reject_code);
+	ntfy->srr_reject_code_expl = srr_explan;
+	ntfy->ox_id = iocb->ox_id;
+
+	if (resp_code_valid) {
+		ntfy->resp_code = cpu_to_le16(resp_code);
+		ntfy->flags |= __constant_cpu_to_le16(
+			NOTIFY_ACK_TM_RESP_CODE_VALID);
+	}
+
+	TRACE_SCSI("qla2x00t(%d): Sending Notify Ack Seq %#x -> I %#x "
+		"St %#x RC %#x\n", vha->vp_idx,
+		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);
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q24_send_abts_resp(scsi_qla_host_t *vha,
+	const abts24_recv_entry_t *abts, uint32_t status, bool ids_reversed)
+{
+	struct qla_hw_data *ha = vha->hw;
+	abts24_resp_entry_t *resp;
+	uint32_t f_ctl;
+	uint8_t *p;
+
+	TRACE_DBG("Sending task mgmt ABTS response (ha=%p, atio=%p, "
+		"status=%x\n", ha, abts, status);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	resp = (abts24_resp_entry_t *)q2t_req_pkt(vha);
+	if (!resp) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet", vha->vp_idx, __func__);
+		return;
+	}
+
+	resp->entry_type = ABTS_RESP_24XX;
+	resp->entry_count = 1;
+	resp->nport_handle = abts->nport_handle;
+	resp->vp_index = vha->vp_idx;
+	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 == FCP_TMF_CMPL) {
+		resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC;
+		resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID;
+		resp->payload.ba_acct.low_seq_cnt = 0x0000;
+		resp->payload.ba_acct.high_seq_cnt = 0xFFFF;
+		resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id;
+		resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id;
+	} else {
+		resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT;
+		resp->payload.ba_rjt.reason_code =
+			BA_RJT_REASON_CODE_UNABLE_TO_PERFORM;
+		/* Other bytes are zero */
+	}
+
+	TRACE_BUFFER("ABTS RESP packet data", resp, REQUEST_ENTRY_SIZE);
+
+	ha->q2t_tgt->abts_resp_expected++;
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q24_retry_term_exchange(scsi_qla_host_t *vha,
+	abts24_resp_fw_entry_t *entry)
+{
+	ctio7_status1_entry_t *ctio;
+
+	TRACE_DBG("Sending retry TERM EXCH CTIO7 (ha=%p)\n", vha->hw);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	ctio = (ctio7_status1_entry_t *)q2t_req_pkt(vha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		return;
+	}
+
+	/*
+	 * We've got on entrance firmware's response on by us generated
+	 * ABTS response. So, in it ID fields are reversed.
+	 */
+
+	ctio->common.entry_type = CTIO_TYPE7;
+	ctio->common.entry_count = 1;
+	ctio->common.nport_handle = entry->nport_handle;
+	ctio->common.handle = Q2T_SKIP_HANDLE |	CTIO_COMPLETION_HANDLE_MARK;
+	ctio->common.timeout = __constant_cpu_to_le16(Q2T_TIMEOUT);
+	ctio->common.vp_index = vha->vp_idx;
+	ctio->common.initiator_id[0] = entry->fcp_hdr_le.d_id[0];
+	ctio->common.initiator_id[1] = entry->fcp_hdr_le.d_id[1];
+	ctio->common.initiator_id[2] = entry->fcp_hdr_le.d_id[2];
+	ctio->common.exchange_addr = entry->exchange_addr_to_abort;
+	ctio->flags = __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE);
+	ctio->ox_id = entry->fcp_hdr_le.ox_id;
+
+	TRACE_BUFFER("CTIO7 retry TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE);
+
+	qla2x00_isp_cmd(vha, vha->req);
+
+	q24_send_abts_resp(vha, (abts24_recv_entry_t *)entry,
+		FCP_TMF_CMPL, true);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int __q24_handle_abts(scsi_qla_host_t *vha, abts24_recv_entry_t *abts,
+	struct q2t_sess *sess)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_mgmt_cmd *mcmd;
+	uint32_t lun;
+	int rc;
+
+	TRACE_MGMT_DBG("qla2x00t(%d): task abort (tag=%d)\n", vha->vp_idx,
+		abts->exchange_addr_to_abort);
+
+	mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
+	if (mcmd == NULL) {
+		printk(KERN_ERR "qla2x00t(%d): %s: Allocation of ABORT cmd failed",
+			vha->vp_idx, __func__);
+		return -ENOMEM;
+	}
+	memset(mcmd, 0, sizeof(*mcmd));
+
+	mcmd->sess = sess;
+	memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
+
+#warning FIXME: Hardcoded LUN for __q24_handle_abts()
+	lun = 0;
+
+	rc = ha->qla2x_tmpl->handle_tmr(mcmd, lun, ABORT_TASK);
+	if (rc != 0) {
+		printk(KERN_ERR "qla2x00t(%d):  qla2x_tmpl->handle_tmr()"
+				" failed: %d", vha->vp_idx, rc);
+		mempool_free(mcmd, q2t_mgmt_cmd_mempool);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q24_handle_abts(scsi_qla_host_t *vha, abts24_recv_entry_t *abts)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_sess *sess;
+	uint32_t tag = abts->exchange_addr_to_abort, s_id;
+	int rc;
+
+	if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) {
+		printk(KERN_ERR "qla2x00t(%d): ABTS: Abort Sequence not "
+			"supported\n", vha->vp_idx);
+		q24_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		return;
+	}
+
+	if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
+		TRACE_MGMT_DBG("qla2x00t(%d): ABTS: Unknown Exchange "
+			"Address received\n", vha->vp_idx);
+		q24_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		return;
+	}
+
+	TRACE_MGMT_DBG("qla2x00t(%d): task abort (s_id=%x:%x:%x, "
+		"tag=%d, param=%x)\n", vha->vp_idx, abts->fcp_hdr_le.s_id[2],
+		abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[0], tag,
+		le32_to_cpu(abts->fcp_hdr_le.parameter));
+
+	memset(&s_id, 0, 3);
+	s_id = (abts->fcp_hdr_le.s_id[0] << 16) | (abts->fcp_hdr_le.s_id[1] << 8) |
+		abts->fcp_hdr_le.s_id[2];
+
+	sess = ha->qla2x_tmpl->find_sess_by_s_id(vha, (unsigned char *)&s_id);
+	if (!sess) {
+		TRACE_MGMT_DBG("qla2x00t(%d): task abort for unexisting "
+			"session\n", vha->vp_idx);
+		rc = q2t_sched_sess_work(ha->q2t_tgt, Q2T_SESS_WORK_ABORT,
+					abts, sizeof(*abts));
+		if (rc != 0) {
+			ha->q2t_tgt->tm_to_unknown = 1;
+			q24_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		}
+		return;
+	}
+
+	rc = __q24_handle_abts(vha, abts, sess);
+	if (rc != 0) {
+		printk(KERN_ERR "qla2x00t(%d): __q24_handle_abts() failed: %d\n",
+			    vha->vp_idx, rc);
+		q24_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		return;
+	}
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha,
+	struct q2t_mgmt_cmd *mcmd, uint32_t resp_code)
+{
+	const atio7_entry_t *atio = &mcmd->orig_iocb.atio7;
+	ctio7_status1_entry_t *ctio;
+
+	TRACE_DBG("Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x\n",
+		  ha, atio, resp_code);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(ha, 1) != QLA_SUCCESS)
+		return;
+
+	ctio = (ctio7_status1_entry_t *)q2t_req_pkt(ha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", ha->vp_idx, __func__);
+		return;
+	}
+
+	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.vp_index = ha->vp_idx;
+	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 = __constant_cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID);
+	ctio->response_len = __constant_cpu_to_le16(8);
+	((uint32_t *)ctio->sense_data)[0] = cpu_to_be32(resp_code);
+
+	TRACE_BUFFER("CTIO7 TASK MGMT packet data", ctio, REQUEST_ENTRY_SIZE);
+
+	qla2x00_isp_cmd(ha, ha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void q24_send_notify_ack(scsi_qla_host_t *vha,
+	notify24xx_entry_t *iocb, uint16_t srr_flags,
+	uint8_t srr_reject_code, uint8_t srr_explan)
+{
+	struct qla_hw_data *ha = vha->hw;
+	nack24xx_entry_t *nack;
+
+	TRACE_DBG("Sending NOTIFY_ACK24 (ha=%p)\n", ha);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	if (ha->q2t_tgt != NULL)
+		ha->q2t_tgt->notify_ack_expected++;
+
+	nack = (nack24xx_entry_t *)q2t_req_pkt(vha);
+	if (!nack) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		return;
+	}
+
+	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;
+	nack->vp_index = iocb->vp_index;
+
+	TRACE_SCSI("qla2x00t(%d): Sending 24xx Notify Ack %d\n",
+		vha->vp_idx, nack->status);
+	TRACE_BUFFER("24xx Notify Ack packet data", nack, sizeof(*nack));
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+/* callback from target fabric module code */
+void q2t_xmit_tm_rsp(struct q2t_mgmt_cmd *mcmd)
+{
+	scsi_qla_host_t *vha;
+	struct qla_hw_data *ha;
+	unsigned long flags;
+
+	TRACE_MGMT_DBG("TM response mcmd (%p) status %#x state %#x", mcmd,
+		mcmd->se_tmr_req->response, mcmd->flags);
+
+	vha = mcmd->sess->vha;
+	ha = vha->hw;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (IS_FWI2_CAPABLE(ha)) {
+		if (mcmd->flags == Q24_MGMT_SEND_NACK) {
+			q24_send_notify_ack(vha,
+				&mcmd->orig_iocb.notify_entry24, 0, 0, 0);
+		} else {
+			if (mcmd->se_tmr_req->function == ABORT_TASK)
+				q24_send_abts_resp(vha, &mcmd->orig_iocb.abts,
+					mcmd->fc_tm_rsp, false);
+			else
+				q24_send_task_mgmt_ctio(vha, mcmd, mcmd->fc_tm_rsp);
+		}
+	} else {
+		q2x_send_notify_ack(vha, &mcmd->orig_iocb.notify_entry, 0,
+			mcmd->fc_tm_rsp, 1, 0, 0, 0);
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	mempool_free(mcmd, q2t_mgmt_cmd_mempool);
+}
+EXPORT_SYMBOL(q2t_xmit_tm_rsp);
+
+/* No locks */
+static int q2t_pci_map_calc_cnt(struct q2t_prm *prm)
+{
+	BUG_ON(prm->cmd->sg_cnt == 0);
+
+	prm->sg = (struct scatterlist *)prm->cmd->sg;
+	prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, prm->cmd->sg,
+		prm->cmd->sg_cnt, prm->cmd->dma_data_direction);
+	if (unlikely(prm->seg_cnt == 0))
+		goto out_err;
+
+	prm->cmd->sg_mapped = 1;
+
+	/*
+	 * 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++;
+	}
+
+	TRACE_DBG("seg_cnt=%d, req_cnt=%d\n", prm->seg_cnt, prm->req_cnt);
+	return 0;
+
+out_err:
+	printk(KERN_ERR "qla2x00t(%d): PCI mapping failed: sg_cnt=%d",
+		0, prm->cmd->sg_cnt);
+	return -1;
+}
+
+static inline void q2t_unmap_sg(scsi_qla_host_t *vha, struct q2t_cmd *cmd)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	BUG_ON(!cmd->sg_mapped);
+	pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
+	cmd->sg_mapped = 0;
+}
+
+static int q2t_check_reserve_free_req(scsi_qla_host_t *vha, uint32_t req_cnt)
+{
+	struct qla_hw_data *ha = vha->hw;
+	device_reg_t __iomem *reg = ha->iobase;
+	uint32_t cnt;
+
+	if (vha->req->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, "
+			"vha->->ring_index=%d, vha->req->cnt=%d, req_cnt=%d\n",
+			cnt, vha->req->ring_index, vha->req->cnt, req_cnt);
+		if  (vha->req->ring_index < cnt)
+			vha->req->cnt = cnt - vha->req->ring_index;
+		else
+			vha->req->cnt = vha->req->length -
+			    (vha->req->ring_index - cnt);
+	}
+
+	if (unlikely(vha->req->cnt < (req_cnt + 2))) {
+		printk(KERN_INFO "qla2x00t(%d): There is no room in the "
+			"request ring: vha->req->ring_index=%d, vha->req->cnt=%d, "
+			"req_cnt=%d\n", vha->vp_idx, vha->req->ring_index,
+			vha->req->cnt, req_cnt);
+		return -ENOMEM;
+	}
+	vha->req->cnt -= req_cnt;
+
+	return 0;
+}
+
+/*
+ * 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 *vha)
+{
+	/* Adjust ring index. */
+	vha->req->ring_index++;
+	if (vha->req->ring_index == vha->req->length) {
+		vha->req->ring_index = 0;
+		vha->req->ring_ptr = vha->req->ring;
+	} else {
+		vha->req->ring_ptr++;
+	}
+	return (cont_entry_t *)vha->req->ring_ptr;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline uint32_t q2t_make_handle(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	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) {
+			printk(KERN_INFO "qla2x00t(%d): Ran out of "
+				"empty cmd slots in ha %p\n", vha->vp_idx, 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, scsi_qla_host_t *vha)
+{
+	uint32_t h;
+	ctio_entry_t *pkt;
+	struct qla_hw_data *ha = vha->hw;
+
+	pkt = (ctio_entry_t *)vha->req->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(vha);
+	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_SCSI("qla2x00t(%d): handle(se_cmd) -> %08x, "
+		"timeout %d L %#x -> I %#x E %#x\n", vha->vp_idx,
+		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, scsi_qla_host_t *vha)
+{
+	uint32_t h;
+	ctio7_status0_entry_t *pkt;
+	struct qla_hw_data *ha = vha->hw;
+	atio7_entry_t *atio = &prm->cmd->atio.atio7;
+
+	pkt = (ctio7_status0_entry_t *)vha->req->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;
+	pkt->common.vp_index = vha->vp_idx;
+
+	h = q2t_make_handle(vha);
+	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.
+		 */
+		dump_stack();
+		return -ENOMEM;
+	} 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);
+
+	TRACE_SCSI("qla2x00t(%d): handle(cmd) -> %08x, "
+		"timeout %d, ox_id %#x\n", vha->vp_idx, pkt->common.handle,
+		Q2T_TIMEOUT, le16_to_cpu(pkt->ox_id));
+	return 0;
+}
+
+/*
+ * 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, scsi_qla_host_t *vha)
+{
+	int cnt;
+	uint32_t *dword_ptr;
+	int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr;
+
+	/* Build continuation packets */
+	while (prm->seg_cnt > 0) {
+		cont_a64_entry_t *cont_pkt64 =
+			(cont_a64_entry_t *)q2t_get_req_pkt(vha);
+
+		/*
+		 * 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);
+	}
+}
+
+/*
+ * 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, scsi_qla_host_t *vha)
+{
+	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_SCSI("iocb->scsi_status=%x, iocb->flags=%x\n",
+	      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);
+		return;
+	}
+
+	/* 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\n",
+		      (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, vha);
+}
+
+/*
+ * 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, scsi_qla_host_t *vha)
+{
+	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\n",
+	      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);
+		return;
+	}
+
+	/* 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\n",
+		      (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, vha);
+}
+
+static inline int q2t_has_data(struct q2t_cmd *cmd)
+{
+	return cmd->bufflen > 0;
+}
+
+/*
+ * Called without ha->hardware_lock held
+ */
+static int q2t_pre_xmit_response(struct q2t_cmd *cmd, struct q2t_prm *prm,
+			int xmit_type, uint8_t scsi_status, uint32_t *full_req_cnt)
+{
+	struct q2t_tgt *tgt = cmd->tgt;
+	scsi_qla_host_t *vha = tgt->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+
+	if (unlikely(cmd->aborted)) {
+		TRACE_MGMT_DBG("qla2x00t(%d): terminating exchange "
+			"for aborted cmd=%p (se_cmd=%p, tag=%d)",
+			vha->vp_idx, cmd, se_cmd, cmd->tag);
+
+		cmd->state = Q2T_STATE_ABORTED;
+
+		if (IS_FWI2_CAPABLE(ha))
+			q24_send_term_exchange(vha, cmd, &cmd->atio.atio7, 0);
+		else
+			q2x_send_term_exchange(vha, cmd, &cmd->atio.atio2x, 0);
+		/* !! At this point cmd could be already freed !! */
+		return Q2T_PRE_XMIT_RESP_CMD_ABORTED;
+	}
+
+	TRACE_SCSI("qla2x00t(%d): tag=%u\n", vha->vp_idx, cmd->tag);
+
+	prm->cmd = cmd;
+	prm->tgt = tgt;
+	prm->rq_result = scsi_status;
+	prm->sense_buffer = &cmd->sense_buffer[0];
+	prm->sense_buffer_len = TRANSPORT_SENSE_BUFFER;
+	prm->sg = NULL;
+	prm->seg_cnt = -1;
+	prm->req_cnt = 1;
+	prm->add_status_pkt = 0;
+
+	TRACE_DBG("rq_result=%x, xmit_type=%x\n", 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(vha, 0) != QLA_SUCCESS)
+		return -EFAULT;
+
+	TRACE_DBG("CTIO start: vha(%d)\n", vha->vp_idx);
+
+	if ((xmit_type & Q2T_XMIT_DATA) && q2t_has_data(cmd)) {
+		if  (q2t_pci_map_calc_cnt(prm) != 0)
+			return -EAGAIN;
+	}
+
+	*full_req_cnt = prm->req_cnt;
+
+	if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+		prm->residual = se_cmd->residual_count;
+		TRACE_DBG("Residual underflow: %d (tag %d, "
+			"op %x, bufflen %d, rq_result %x)\n",
+			prm->residual, cmd->tag,
+			T_TASK(se_cmd)->t_task_cdb[0], cmd->bufflen,
+			prm->rq_result);
+		prm->rq_result |= SS_RESIDUAL_UNDER;
+	} else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+		prm->residual = se_cmd->residual_count;
+		TRACE_DBG("Residual overflow: %d (tag %d, "
+			"op %x, bufflen %d, rq_result %x)\n",
+			prm->residual, cmd->tag,
+			T_TASK(se_cmd)->t_task_cdb[0], cmd->bufflen,
+			prm->rq_result);
+		prm->rq_result |= SS_RESIDUAL_OVER;
+		prm->residual = -prm->residual;
+	}
+
+	if (xmit_type & Q2T_XMIT_STATUS) {
+		/*
+		 * If Q2T_XMIT_DATA is not set, add_status_pkt will be ignored
+		 * in *xmit_response() below
+		 */
+		if (q2t_has_data(cmd)) {
+			if (Q2T_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\n",
+		prm->req_cnt, *full_req_cnt, prm->add_status_pkt);
+
+	return 0;
+}
+
+static inline int q2t_need_explicit_conf(struct qla_hw_data *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, scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	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(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 (Q2T_SENSE_VALID(prm->sense_buffer)) {
+		if (q2t_need_explicit_conf(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 ??? */
+
+	return;
+}
+
+static int __q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type, uint8_t scsi_status)
+{
+	scsi_qla_host_t *vha = cmd->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_prm prm;
+	ctio_common_entry_t *pkt;
+	unsigned long flags;
+	uint32_t full_req_cnt = 0;
+	int res;
+
+	memset(&prm, 0, sizeof(prm));
+
+	res = q2t_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, &full_req_cnt);
+	if (unlikely(res != 0)) {
+		if (res == Q2T_PRE_XMIT_RESP_CMD_ABORTED)
+			return 0;
+
+		return res;
+	}
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	/* Does F/W have an IOCBs for this request */
+	res = q2t_check_reserve_free_req(vha, full_req_cnt);
+	if (unlikely(res != 0) && (xmit_type & Q2T_XMIT_DATA))
+		goto out_unmap_unlock;
+
+	q2x_build_ctio_pkt(&prm, cmd->vha);
+	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, vha);
+
+		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(vha);
+
+			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, cmd->vha);
+			TRACE_BUFFER("Status CTIO packet data", ctio_m1,
+				REQUEST_ENTRY_SIZE);
+		}
+	} else
+		q2x_init_ctio_ret_entry((ctio_ret_entry_t *)pkt, &prm, cmd->vha);
+
+	cmd->state = Q2T_STATE_PROCESSED; /* Mid-level is done processing */
+
+	TRACE_DBG("Xmitting CTIO7 response pkt for 2xxx: %p scsi_status:"
+			" 0x%02x\n", pkt, scsi_status);
+
+	qla2x00_isp_cmd(vha, vha->req);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return 0;
+
+out_unmap_unlock:
+	if (cmd->sg_mapped)
+		q2t_unmap_sg(vha, cmd);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+
+#ifdef CONFIG_QLA_TGT_DEBUG_SRR
+/*
+ *  Original taken from the XFS code
+ */
+static unsigned long q2t_srr_random(void)
+{
+	static int Inited;
+	static unsigned long RandomValue;
+	static DEFINE_SPINLOCK(lock);
+	/* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */
+	register long rv;
+	register long lo;
+	register long hi;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lock, flags);
+	if (!Inited) {
+		RandomValue = jiffies;
+		Inited = 1;
+	}
+	rv = RandomValue;
+	hi = rv / 127773;
+	lo = rv % 127773;
+	rv = 16807 * lo - 2836 * hi;
+	if (rv <= 0)
+		rv += 2147483647;
+	RandomValue = rv;
+	spin_unlock_irqrestore(&lock, flags);
+	return rv;
+}
+
+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) && (q2t_srr_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) &&
+	    ((q2t_srr_random() % 100) == 20)) {
+		int i, leave = 0;
+		unsigned int tot_len = 0;
+
+		while (leave == 0)
+			leave = q2t_srr_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) && ((q2t_srr_random() % 100) == 70)) {
+		unsigned int offset = q2t_srr_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
+
+int q2x_xmit_response(struct q2t_cmd *cmd, int xmit_type, uint8_t scsi_status)
+{
+#if 0
+	q2t_check_srr_debug(cmd, &xmit_type);
+#endif
+
+	TRACE_DBG("is_send_status=%d, cmd->bufflen=%d, cmd->sg_cnt=%d, "
+		"cmd->dma_data_direction=%d", (xmit_type & Q2T_XMIT_STATUS) ?
+		1 : 0, cmd->bufflen, cmd->sg_cnt, cmd->dma_data_direction);
+
+	return (IS_FWI2_CAPABLE(cmd->tgt->ha)) ?
+		__q24_xmit_response(cmd, xmit_type, scsi_status) :
+		__q2x_xmit_response(cmd, xmit_type, scsi_status);
+}
+EXPORT_SYMBOL(q2x_xmit_response);
+
+static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
+	struct q2t_prm *prm)
+{
+	ctio7_status1_entry_t *ctio1;
+
+	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 (Q2T_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) {
+				printk(KERN_INFO "qla2x00t(%d): %d bytes of sense "
+					"lost", prm->tgt->ha->vp_idx,
+					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 ??? */
+}
+
+/*
+ * Callback to setup response of xmit_type of Q2T_XMIT_DATA and * Q2T_XMIT_STATUS
+ * for >= 24xx silicon
+ */
+static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type, uint8_t scsi_status)
+{
+	scsi_qla_host_t *vha = cmd->vha;
+	struct qla_hw_data *ha = vha->hw;
+	ctio7_status0_entry_t *pkt;
+	struct q2t_prm prm;
+	uint32_t full_req_cnt = 0;
+	unsigned long flags;
+	int res;
+
+	memset(&prm, 0, sizeof(prm));
+
+	res = q2t_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, &full_req_cnt);
+	if (unlikely(res != 0)) {
+		if (res == Q2T_PRE_XMIT_RESP_CMD_ABORTED)
+			return 0;
+
+		return res;
+	}
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+        /* Does F/W have an IOCBs for this request */
+	res = q2t_check_reserve_free_req(vha, full_req_cnt);
+	if (unlikely(res != 0) && (xmit_type & Q2T_XMIT_DATA))
+		goto out_unmap_unlock;
+
+	res = q24_build_ctio_pkt(&prm, vha);
+	if (unlikely(res != 0))
+		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, vha);
+
+		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(vha);
+
+			TRACE_DBG("Building additional status packet\n");
+
+			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);
+			printk("Status CTIO7: %p\n", ctio);
+		}
+	} else
+		q24_init_ctio_ret_entry(pkt, &prm);
+
+
+	cmd->state = Q2T_STATE_PROCESSED; /* Mid-level is done processing */
+
+	TRACE_DBG("Xmitting CTIO7 response pkt for 24xx: %p scsi_status:"
+			" 0x%02x\n", pkt, scsi_status);
+
+	qla2x00_isp_cmd(vha, vha->req);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return 0;
+
+out_unmap_unlock:
+	if (cmd->sg_mapped)
+		q2t_unmap_sg(vha, cmd);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+
+int q2t_rdy_to_xfer(struct q2t_cmd *cmd)
+{
+	scsi_qla_host_t *vha = cmd->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_tgt *tgt = cmd->tgt;
+	struct q2t_prm prm;
+	void *p;
+	unsigned long flags;
+	int res = 0;
+
+	memset(&prm, 0, sizeof(prm));
+	prm.cmd = cmd;
+	prm.tgt = tgt;
+	prm.sg = NULL;
+	prm.req_cnt = 1;
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, 0) != QLA_SUCCESS)
+		return -EIO;
+
+	TRACE_DBG("CTIO_start: vha(%d)", (int)vha->vp_idx);
+
+	/* Calculate number of entries and segments required */
+	if (q2t_pci_map_calc_cnt(&prm) != 0)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	/* Does F/W have an IOCBs for this request */
+	res = q2t_check_reserve_free_req(vha, prm.req_cnt);
+	if (res != 0)
+		goto out_unlock_free_unmap;
+
+	if (IS_FWI2_CAPABLE(ha)) {
+		ctio7_status0_entry_t *pkt;
+		res = q24_build_ctio_pkt(&prm, vha);
+		if (unlikely(res != 0))
+			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, vha);
+		p = pkt;
+	} else {
+		ctio_common_entry_t *pkt;
+		q2x_build_ctio_pkt(&prm, vha);
+		pkt = (ctio_common_entry_t *)prm.pkt;
+		pkt->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_OUT);
+		q2x_load_data_segments(&prm, vha);
+		p = pkt;
+	}
+
+	cmd->state = Q2T_STATE_NEED_DATA;
+
+	TRACE_BUFFER("Xfering", p, REQUEST_ENTRY_SIZE);
+
+	qla2x00_isp_cmd(vha, vha->req);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+
+out_unlock_free_unmap:
+	if (cmd->sg_mapped)
+		q2t_unmap_sg(vha, cmd);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+EXPORT_SYMBOL(q2t_rdy_to_xfer);
+
+/* If hardware_lock held on entry, might drop it, then reaquire */
+static void q2x_send_term_exchange(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
+	atio_entry_t *atio, int ha_locked)
+{
+	struct qla_hw_data *ha = vha->hw;
+	ctio_ret_entry_t *ctio;
+	unsigned long flags = 0; /* to stop compiler's warning */
+	int do_tgt_cmd_done = 0;
+
+	TRACE_DBG("Sending TERM EXCH CTIO (ha=%p)\n", ha);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, ha_locked) != QLA_SUCCESS)
+		return;
+
+	if (!ha_locked)
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	ctio = (ctio_ret_entry_t *)q2t_req_pkt(vha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		goto out_unlock;
+	}
+
+	ctio->entry_type = CTIO_RET_TYPE;
+	ctio->entry_count = 1;
+	if (cmd != NULL) {
+		if (cmd->state < Q2T_STATE_PROCESSED) {
+			printk(KERN_ERR "qla2x00t(%d): Terminating cmd %p with "
+				"incorrect state %d\n", vha->vp_idx, 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);
+
+	qla2x00_isp_cmd(vha, vha->req);
+
+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 */
+
+		ha->qla2x_tmpl->free_cmd(cmd);
+	}
+}
+
+/* If hardware_lock held on entry, might drop it, then reaquire */
+static void q24_send_term_exchange(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
+	atio7_entry_t *atio, int ha_locked)
+{
+	struct qla_hw_data *ha = vha->hw;
+	ctio7_status1_entry_t *ctio;
+	unsigned long flags = 0; /* to stop compiler's warning */
+	int do_tgt_cmd_done = 0;
+
+	TRACE_DBG("Sending TERM EXCH CTIO7 (ha=%p)\n", ha);
+
+	/* Send marker if required */
+	if (q2t_issue_marker(vha, ha_locked) != QLA_SUCCESS)
+		return;
+
+	if (!ha_locked)
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	ctio = (ctio7_status1_entry_t *)q2t_req_pkt(vha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla2x00t(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __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) {
+			printk(KERN_ERR "qla2x00t(%d): Terminating cmd %p with "
+				"incorrect state %d\n", vha->vp_idx, 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.vp_index = vha->vp_idx;
+	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 = get_unaligned((uint32_t *)
+			&atio->fcp_cmnd.add_cdb[atio->fcp_cmnd.add_cdb_len]);
+	if (ctio->residual != 0)
+		ctio->scsi_status |= SS_RESIDUAL_UNDER;
+
+	TRACE_BUFFER("CTIO7 TERM EXCH packet data", ctio, REQUEST_ENTRY_SIZE);
+
+	qla2x00_isp_cmd(vha, vha->req);
+
+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 */
+
+		ha->qla2x_tmpl->free_cmd(cmd);
+	}
+}
+
+void q2t_free_cmd(struct q2t_cmd *cmd)
+{
+	BUG_ON(cmd->sg_mapped);
+
+	if (unlikely(cmd->free_sg))
+		kfree(cmd->sg);
+	kmem_cache_free(q2t_cmd_cachep, cmd);
+}
+EXPORT_SYMBOL(q2t_free_cmd);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int q2t_prepare_srr_ctio(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
+	void *ctio)
+{
+	struct srr_ctio *sc;
+	struct qla_hw_data *ha = vha->hw;
+	struct q2t_tgt *tgt = ha->q2t_tgt;
+	struct srr_imm *imm;
+
+	tgt->ctio_srr_id++;
+
+	TRACE_MGMT_DBG("qla2x00t(%d): CTIO with SRR "
+		"status received\n", vha->vp_idx);
+
+	if (!ctio) {
+		printk(KERN_ERR "qla2x00t(%d): SRR CTIO, "
+			"but ctio is NULL\n", vha->vp_idx);
+		return EINVAL;
+	}
+
+	dump_stack();
+
+	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)\n",
+			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\n");
+				schedule_work(&tgt->srr_work);
+			} else {
+				printk(KERN_ERR "qla2x00t(%d): imm_srr_id "
+					"== ctio_srr_id (%d), but there is no "
+					"corresponding SRR IMM, deleting CTIO "
+					"SRR %p\n", vha->vp_idx, tgt->ctio_srr_id,
+					sc);
+				list_del(&sc->srr_list_entry);
+				spin_unlock(&tgt->srr_lock);
+
+				kfree(sc);
+				return -EINVAL;
+			}
+		}
+		spin_unlock(&tgt->srr_lock);
+	} else {
+		struct srr_imm *ti;
+
+		printk(KERN_ERR "qla2x00t(%d): Unable to allocate SRR CTIO entry\n",
+			vha->vp_idx);
+		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)\n", imm, imm->srr_id);
+				list_del(&imm->srr_list_entry);
+				q2t_reject_free_srr_imm(vha, imm, 1);
+			}
+		}
+		spin_unlock(&tgt->srr_lock);
+
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static int q2t_term_ctio_exchange(scsi_qla_host_t *vha, void *ctio,
+	struct q2t_cmd *cmd, uint32_t status)
+{
+	struct qla_hw_data *ha = vha->hw;
+	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(vha, cmd,
+				&cmd->atio.atio7, 1);
+		}
+	} else {
+		if (status != CTIO_SUCCESS)
+			q2x_modify_command_count(vha, 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(vha, 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 *vha, uint32_t handle)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	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 *vha, uint32_t handle,
+	void *ctio)
+{
+	struct qla_hw_data *ha = vha->hw;
+	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\n");
+			return NULL;
+		}
+		/* handle-1 is actually used */
+		if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) {
+			printk(KERN_ERR "qla2x00t(%d): Wrong handle %x "
+				"received\n", vha->vp_idx, handle);
+			return NULL;
+		}
+		cmd = q2t_get_cmd(vha, handle);
+		if (unlikely(cmd == NULL)) {
+			printk(KERN_WARNING "qla2x00t(%d): Suspicious: unable to "
+				   "find the command with handle %x\n",
+				   vha->vp_idx, handle);
+			return NULL;
+		}
+	} else if (ctio != NULL) {
+		struct q2t_sess *sess;
+		int tag;
+		uint16_t loop_id;
+
+		if (IS_FWI2_CAPABLE(ha)) {
+			/* We can't get loop ID from CTIO7 */
+			printk(KERN_ERR "qla2x00t(%d): Wrong CTIO received: "
+				"QLA24xx doesn't support NULL handles\n",
+				vha->vp_idx);
+			return NULL;
+		} else {
+			ctio_common_entry_t *c = (ctio_common_entry_t *)ctio;
+			loop_id = GET_TARGET_ID(ha, c);
+			tag = c->rx_id;
+		}
+
+		sess = ha->qla2x_tmpl->find_sess_by_loop_id(vha, loop_id);
+		if (!sess) {
+			printk(KERN_WARNING "qla2x00t(%d): Suspicious: "
+				   "ctio_completion for non-existing session "
+				   "(loop_id %d, tag %d)\n",
+				   vha->vp_idx, loop_id, tag);
+			return NULL;
+		}
+	}
+
+	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 *vha, uint32_t handle,
+	uint32_t status, void *ctio)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct se_cmd *se_cmd;
+	struct target_core_fabric_ops *tfo;
+	struct q2t_cmd *cmd;
+
+	TRACE_SCSI("qla2x00t(%d): handle(ctio %p status %#x) <- %08x\n",
+			vha->vp_idx, 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)\n",
+				status);
+		}
+		return;
+	}
+
+	cmd = q2t_ctio_to_cmd(vha, handle, ctio);
+	if (cmd == NULL) {
+		if (status != CTIO_SUCCESS)
+			q2t_term_ctio_exchange(vha, ctio, NULL, status);
+		return;
+	}
+	se_cmd = &cmd->se_cmd;
+	tfo = se_cmd->se_tfo;
+
+	if (cmd->sg_mapped)
+		q2t_unmap_sg(vha, 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 */
+			printk(KERN_INFO "qla2x00t(%d): CTIO with "
+				"status %#x received, state %x, se_cmd %p, "
+				"(LIP_RESET=e, ABORTED=2, TARGET_RESET=17, "
+				"TIMEOUT=b, INVALID_RX_ID=8)\n", vha->vp_idx,
+				status, cmd->state, se_cmd);
+			break;
+
+		case CTIO_PORT_LOGGED_OUT:
+		case CTIO_PORT_UNAVAILABLE:
+			printk(KERN_INFO "qla2x00t(%d): CTIO with PORT LOGGED "
+				"OUT (29) or PORT UNAVAILABLE (28) status %x "
+				"received (state %x, se_cmd %p)\n",
+				vha->vp_idx, status, cmd->state, se_cmd);
+			break;
+
+		case CTIO_SRR_RECEIVED:
+			printk(KERN_INFO "qla2x00t(%d): CTIO with SRR_RECEIVED"
+				" status %x received (state %x, se_cmd %p)\n",
+				vha->vp_idx, status, cmd->state, se_cmd);
+			if (q2t_prepare_srr_ctio(vha, cmd, ctio) != 0)
+				break;
+			else
+				return;
+
+		default:
+			printk(KERN_ERR "qla2x00t(%d): CTIO with error status "
+				"0x%x received (state %x, se_cmd %p\n",
+				vha->vp_idx, status, cmd->state, se_cmd);
+			break;
+		}
+
+		if (cmd->state != Q2T_STATE_NEED_DATA)
+			if (q2t_term_ctio_exchange(vha, ctio, cmd, status))
+				return;
+	}
+
+	if (cmd->state == Q2T_STATE_PROCESSED) {
+		TRACE_DBG("Command %p finished\n", cmd);
+	} else if (cmd->state == Q2T_STATE_NEED_DATA) {
+		int rx_status = 0;
+
+		cmd->state = Q2T_STATE_DATA_IN;
+
+		if (unlikely(status != CTIO_SUCCESS))
+			rx_status = -EIO;
+		else
+			cmd->write_data_transferred = 1;
+
+		TRACE_DBG("Data received, context %x, rx_status %d\n",
+		      0x0, rx_status);
+
+		ha->qla2x_tmpl->handle_data(cmd);
+		return;
+	} else if (cmd->state == Q2T_STATE_ABORTED) {
+		TRACE_MGMT_DBG("Aborted command %p (tag %d) finished\n", cmd,
+			cmd->tag);
+	} else {
+		printk(KERN_ERR "qla2x00t(%d): A command in state (%d) should "
+			"not return a CTIO complete\n", vha->vp_idx, cmd->state);
+	}
+
+	if (unlikely(status != CTIO_SUCCESS)) {
+		TRACE_MGMT_DBG("%s", "Finishing failed CTIO\n");
+		dump_stack();
+	}
+
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

drivers/scsi/qla2xxx/qla2x_target.h new file +290 -0

290 lines not on this page · raw message

drivers/scsi/qla2xxx/qla2x_tgt.h new file +140 -0

140 lines not on this page · raw message

drivers/scsi/qla2xxx/qla2x_tgt_def.h new file +717 -0

717 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
1.7.3.3

Part of the diff is not shown here. View the raw message.

#2 lore raw

RFC 2/3 qla2xxx: Enable 2xxx series LLD target mode support

In reply to Nicholas A. Bellinger

From: Nicholas Bellinger <[email protected]>

This patch enables target mode support with the qla2xxx SCSI LLD using qla24_target.c logic introduced in commit d718f0344b. This includes:

*) Addition of target mode specific members to existing data structures in qla_def.h and struct qla_hw_data->qla2x_tmpl using qla2x_tgt_def.h:struct qla_target_template.

*) Addition of qla_hw_data->qla2x_tmpl checks for API calls via struct qla_target_template and/or direct calls into qla2x_target.c logic w/ q2t_* prefixed functions.

*) Addition of qla24xx_process_atio_queue(), qla2x00_req_pkt() for ring processing, and qla2x00_issue_marker() for handling request/response queue processing for target mode.

*) Addition of various qla_tgt_mode_enabled() logic checks in qla24xx_nvram_config(), qla2x00_initialize_adapter(), qla2x00_rff_id(), qla2x00_abort_isp(), qla24xx_modify_vp_config(), and qla2x00_vp_abort_isp().

For the specific checks for qla_hw_data->qla2x_tmpl this includes:

*) control plane:

qla_init.c:qla2x00_rport_del() -> q2t_fc_port_deleted() qla_init.c:qla2x00_reg_remote_port() -> q2t_fc_port_added() qla_init.c:qla2x00_device_resync() -> qla2x00_mark_device_lost()

*) I/O path:

qla_isr.c:qla2x00_async_event() -> q2t_async_event() qla_isr.c:qla2x00_process_response_queue() -> q2t_response_pkt_all_vps() qla_isr.c:qla24xx_process_response_queue() -> q2t_response_pkt_all_vps()

---
 drivers/scsi/qla2xxx/Makefile   |    4 +-
 drivers/scsi/qla2xxx/qla_attr.c |   14 ++-
 drivers/scsi/qla2xxx/qla_def.h  |   68 ++++++++-
 drivers/scsi/qla2xxx/qla_fw.h   |   10 ++
 drivers/scsi/qla2xxx/qla_gbl.h  |   11 ++
 drivers/scsi/qla2xxx/qla_gs.c   |   14 ++-
 drivers/scsi/qla2xxx/qla_init.c |  329 +++++++++++++++++++++++++++++++++++++--
 drivers/scsi/qla2xxx/qla_iocb.c |  105 ++++++++++++-
 drivers/scsi/qla2xxx/qla_isr.c  |  232 ++++++++++++++++++++++++++--
 drivers/scsi/qla2xxx/qla_mbx.c  |  172 +++++++++++++++++++-
 drivers/scsi/qla2xxx/qla_mid.c  |   35 ++++-
 drivers/scsi/qla2xxx/qla_os.c   |  250 ++++++++++++++++++++++++++++--
 12 files changed, 1188 insertions(+), 56 deletions(-)
drivers/scsi/qla2xxx/Makefile +3 -1
index 5df782f..9268f4f 100644
@@ -1,5 +1,7 @@
 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 qla_bsg.o \
-        qla_nx.o
+        qla_nx.o qla2x_target.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
+
+EXTRA_CFLAGS    := -Idrivers/scsi/qla2xxx/ -Idrivers/target/ -Idrivers/target/tcm_qla2xxx/
drivers/scsi/qla2xxx/qla_attr.c +13 -1
index bc8194f..794f47b 100644
@@ -11,6 +11,8 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 
+#include "qla2x_tgt.h"
+
 static int qla24xx_vport_disable(struct fc_vport *, bool);
 
 /* SYSFS attributes --------------------------------------------------------- */
@@ -1788,6 +1790,15 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
 	fc_host_supported_speeds(vha->host) =
 		fc_host_supported_speeds(base_vha->host);
 
+	mutex_init(&ha->tgt_mutex);
+	mutex_init(&ha->tgt_host_action_mutex);
+	qla_clear_tgt_mode(vha);
+	qla2x00_send_enable_lun(vha, false);
+	if (IS_QLA24XX_TYPE(ha))
+		ha->atio_q_length = ATIO_ENTRY_CNT_24XX;
+	else if (IS_QLA25XX(ha))
+		ha->atio_q_length = ATIO_ENTRY_CNT_24XX;
+
 	qla24xx_vport_disable(fc_vport, disable);
 
 	if (ha->flags.cpu_affinity_enabled) {
@@ -1992,7 +2003,8 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha)
 	fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
 	fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
 	fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
-	fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+	fc_host_supported_classes(vha->host) = ha->enable_class_2 ?
+			(FC_COS_CLASS2|FC_COS_CLASS3) : FC_COS_CLASS3;
 	fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
 	fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
 
drivers/scsi/qla2xxx/qla_def.h +65 -3

134 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_fw.h +10 -0
index 631fefc..9a1f8b8b 100644
@@ -26,6 +26,16 @@
 #define PDO_FORCE_ADISC		BIT_1
 #define PDO_FORCE_PLOGI		BIT_0
 
+struct qla_port23_data {
+	uint8_t port_name[WWN_SIZE];
+	uint16_t loop_id;
+};
+
+struct qla_port24_data {
+	uint8_t port_name[WWN_SIZE];
+	uint16_t loop_id;
+	uint16_t reserved;
+};
 
 #define	PORT_DATABASE_24XX_SIZE		64
 struct port_database_24xx {
drivers/scsi/qla2xxx/qla_gbl.h +11 -0
index 9382a81..46b0c5c 100644
@@ -51,6 +51,9 @@ extern void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *);
 extern void qla2x00_alloc_fw_dump(scsi_qla_host_t *);
 extern void qla2x00_try_to_stop_firmware(scsi_qla_host_t *);
 
+extern void qla2x00_enable_tgt_mode(struct scsi_qla_host *);
+extern void qla2x00_disable_tgt_mode(struct scsi_qla_host *);
+
 extern void qla84xx_put_chip(struct scsi_qla_host *);
 
 extern int qla2x00_async_login(struct scsi_qla_host *, fc_port_t *,
@@ -168,6 +171,11 @@ extern int  qla2x00_vp_abort_isp(scsi_qla_host_t *);
 /*
  * Global Function Prototypes in qla_iocb.c source file.
  */
+
+extern void *qla2x00_alloc_iocbs(scsi_qla_host_t *, srb_t *);
+extern void qla2x00_isp_cmd(struct scsi_qla_host *, struct req_que *);
+extern int qla2x00_issue_marker(scsi_qla_host_t *, int);
+
 extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
 extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
 extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
@@ -233,6 +241,9 @@ extern int
 qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
 
 extern int
+qla2x00_get_node_name_list(scsi_qla_host_t *, void **, int *);
+
+extern int
 qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
 
 extern int
drivers/scsi/qla2xxx/qla_gs.c +13 -1
index 4c08392..4794bf1 100644
@@ -6,6 +6,8 @@
  */
 #include "qla_def.h"
 
+#include "qla2x_tgt.h"
+
 static int qla2x00_sns_ga_nxt(scsi_qla_host_t *, fc_port_t *);
 static int qla2x00_sns_gid_pt(scsi_qla_host_t *, sw_info_t *);
 static int qla2x00_sns_gpn_id(scsi_qla_host_t *, sw_info_t *);
@@ -540,8 +542,18 @@ qla2x00_rff_id(scsi_qla_host_t *vha)
 	ct_req->req.rff_id.port_id[0] = vha->d_id.b.domain;
 	ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
 	ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
+	/*
+	 * FC-4 Feature bit 0 indicates target functionality to the name server.
+	 */
+	if (qla_tgt_mode_enabled(vha)) {
+		if (qla_ini_mode_enabled(vha))
+			ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1;
+		else
+			ct_req->req.rff_id.fc4_feature = BIT_0;
+	} else if (qla_ini_mode_enabled(vha)) {
+		ct_req->req.rff_id.fc4_feature = BIT_1;
+	}
 
-	ct_req->req.rff_id.fc4_feature = BIT_1;
 	ct_req->req.rff_id.fc4_type = 0x08;		/* SCSI - FCP */
 
 	/* Execute MS IOCB */
drivers/scsi/qla2xxx/qla_init.c +319 -10

489 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_iocb.c +103 -2

135 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_isr.c +222 -10

385 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_mbx.c +164 -8

250 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_mid.c +30 -5
index 2b69392..a2f1b35 100644
@@ -16,6 +16,8 @@
 #include <scsi/scsicam.h>
 #include <linux/delay.h>
 
+#include "qla2x_tgt.h"
+
 void
 qla2x00_vp_stop_timer(scsi_qla_host_t *vha)
 {
@@ -48,6 +50,7 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
 	list_add_tail(&vha->list, &ha->vp_list);
+	ha->tgt_vp_map[vp_id].vha = vha;
 	spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 	mutex_unlock(&ha->vport_lock);
@@ -78,6 +81,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
 		spin_lock_irqsave(&ha->vport_slock, flags);
 	}
 	list_del(&vha->list);
+	ha->tgt_vp_map[vha->vp_idx].vha = NULL;
 	spin_unlock_irqrestore(&ha->vport_slock, flags);
 
 	vp_id = vha->vp_idx;
@@ -143,12 +147,16 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
 int
 qla24xx_disable_vp(scsi_qla_host_t *vha)
 {
+	struct qla_hw_data *ha = vha->hw;
 	int ret;
 
 	ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
 	atomic_set(&vha->loop_state, LOOP_DOWN);
 	atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
 
+	/* Remove port id from vp target map */
+	ha->tgt_vp_map[vha->d_id.b.al_pa].idx = 0;
+
 	qla2x00_mark_vp_devices_dead(vha);
 	atomic_set(&vha->vp_state, VP_FAILED);
 	vha->flags.management_server_logged_in = 0;
@@ -266,6 +274,8 @@ qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb)
 int
 qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
 {
+	int ret;
+
 	/*
 	 * Physical port will do most of the abort and recovery work. We can
 	 * just treat it as a loop down
@@ -288,7 +298,17 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
 
 	DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n",
 	    vha->host_no, vha->vp_idx));
-	return qla24xx_enable_vp(vha);
+	ret = qla24xx_enable_vp(vha);
+	if (ret)
+		return ret;
+
+	/* Enable target response to SCSI bus. */
+	if (qla_tgt_mode_enabled(vha)) {
+		printk("qla2x00_vp_abort_isp() calling qla2x00_send_enable_lun()\n");
+		qla2x00_send_enable_lun(vha, true);
+	}
+
+	return 0;
 }
 
 static int
@@ -375,17 +395,22 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
 	scsi_qla_host_t *vha;
 	uint8_t port_name[WWN_SIZE];
 
-	if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR)
+	if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR) {
+		printk("fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR\n");
 		return VPCERR_UNSUPPORTED;
+	}
 
 	/* Check up the F/W and H/W support NPIV */
-	if (!ha->flags.npiv_supported)
+	if (!ha->flags.npiv_supported) {
+		printk("!ha->flags.npiv_supported\n");
 		return VPCERR_UNSUPPORTED;
+	}
 
 	/* Check up whether npiv supported switch presented */
-	if (!(ha->switch_cap & FLOGI_MID_SUPPORT))
+	if (!(ha->switch_cap & FLOGI_MID_SUPPORT)) {
+		printk("!(ha->switch_cap & FLOGI_MID_SUPPORT))\n");
 		return VPCERR_NO_FABRIC_SUPP;
-
+	}
 	/* Check up unique WWPN */
 	u64_to_wwn(fc_vport->port_name, port_name);
 	if (!memcmp(port_name, base_vha->port_name, WWN_SIZE))
drivers/scsi/qla2xxx/qla_os.c +235 -15

429 lines not on this page · raw message

Signature · 1 line
1.7.3.3
#3 lore raw

RFC 3/3 tcm_qla2xxx: Add HW target mode I/O, control and TMR path code

In reply to Nicholas A. Bellinger

From: Nicholas Bellinger <[email protected]>

This patch adds initial support for functioning READ and WRITE I/O into tcm_qla2xxx fabric module code and TCM backends using new qla2x_target.c LLD logic introduced into commit 3d21d3e3a9e2. This includes support for demo-mode TPG access and explict NodeACLs via configfs using tcm_qla2xxx_setup_nacl_from_rport() from libfc struct fc_host->rports.

This patch also adds support for using tcm_qla2xxx_lport->lport_fcport_map and ->lport_loopid_map to track struct se_node_acl pointers for individual 24-bit Port ID and 16-bit Loop ID values for qla_target_template ->find_sess_by_s_id() and ->find_sess_by_loop_id() used in a number of locations into the primary I/O dispatch logic in qla2x_target.c LLD code.

The main piece to setup a FC Nexus is done in tcm_qla2xxx_check_initiator_node_acl(), which calls tcm_qla2xxx_set_sess_by_[s_id,loop_id]() to setup our lport->lport_fcport_map and lport_loopid_map pointers respectively, and register the new nexus with TCM via __transport_register_session(). The FC nexus release and reset path is done via a qla2x_target.c LLD callback to tcm_qla2xxx_free_session() which calls transport_deregister_session_configfs(), then to clear the tcm_qla2xxx_set_sess_by_[s_id,loop_id]() struct se_nacl pointer, and finally transport_deregister_session() to release our struct se_session.

For the functioning non NPIV case in tcm_qla2xxx_configfs.c:tcm_qla2xxx_make_tpg() we use a single lport->tpg_1 pointer, and only allow a single scsi_qla_host_t * per struct tcm_qla2xxx_lport. The NPIV configfs code is still a WIP and is using NOPs for in tcm_qla2xxx_configfs.c at this point.

---
 drivers/target/tcm_qla2xxx/Kbuild                 |    6 +
 drivers/target/tcm_qla2xxx/Kconfig                |    6 +
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h     |   91 ++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c | 1319 +++++++++++++++++++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c   |  766 ++++++++++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h   |   48 +
 6 files changed, 2236 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/tcm_qla2xxx/Kbuild
 create mode 100644 drivers/target/tcm_qla2xxx/Kconfig
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c
 create mode 100644 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h
drivers/target/tcm_qla2xxx/Kbuild new file +6 -0
new file mode 100644
index 0000000..067cca1
@@ -0,0 +1,6 @@
+EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/ -I$(srctree)/drivers/scsi/qla2xxx/ -I$(srctree)/include/scsi/ -I$(srctree)/drivers/target/tcm_qla2xxx/
+
+tcm_qla2xxx-objs		:= tcm_qla2xxx_fabric.o \
+				   tcm_qla2xxx_configfs.o \
+
+obj-$(CONFIG_TCM_QLA2XXX)	+= tcm_qla2xxx.o
drivers/target/tcm_qla2xxx/Kconfig new file +6 -0
new file mode 100644
index 0000000..b9002fc
@@ -0,0 +1,6 @@
+config TCM_QLA2XXX
+	tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs"
+	depends on TARGET_CORE && CONFIGFS_FS
+	default n
+	---help---
+	Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs
drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h new file +91 -0

91 lines not on this page · raw message

drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c new file +1319 -0

1,319 lines not on this page · raw message

drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c new file +766 -0

766 lines not on this page · raw message

drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h new file +48 -0

48 lines not on this page · raw message

Signature · 1 line
1.7.3.3