Skip to contentSkip to conversation

qla2xxx LLD target mode + tcm_qla2xxx fabric module

RFC Superseded Nicholas A. Bellinger 2 participants 6 messages Apr 5, 2011 – Apr 12, 2011

Subsystems (3)

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v3 cover qla2xxx LLD target mode + tcm_qla2xxx fabric module

From: Nicholas Bellinger <[email protected]>

Greetings James, Christoph, Hannes, and Co,

Attached is the third RFC series for adding qla2xxx LLD target mode support into mainline v8.03.07.00 @ .39-rc1, along with the accompanying tcm_qla2xxx.ko fabric module cut against mainline target v4.0 infrastructure. This series has been broken up into reviewable sections and should be considered a 'for-40' item.

Many thanks to Andrew Vasquez for his initial off-list review of RFC-v2. This updated series contains a number of cleanups + removal of unnecessary code recommended by Andrew for enabling target mode support, and other misc code maintainibility improvements.

So at this point with the functioning code, we would like to come to a consensus with linux-scsi folks about how the explict qla2xxx target mode LLD and tcm_qla2xxx fabric module 'split' should look, and how it will be accepted into distros and maintained for mainline moving forward. The Qlogic folks (Madhu and Andrew) have expressed interest that they would like to eventually own maintainence for the target pieces inside of qla2xxx, but this currently ends up being decent chunk of code in qla_target.c that may otherwise better reside in tcm_qla2xxx code.

So that said, I would really like to get opinions from Christoph, Hannes, and Mike about how the 'existing LLD / tcm_fabric.ko split' can best done for mainline, and some input from kernel distro folks would also be useful here. Comments here please upstream linux-scsi folks..?

An overview of the current status, todo list, mini-howto and running layout from /sys/kernel/config/target/qla2xxx/ are available here:

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

The next major item will be proper target mode NPIV support for >= 24xx series adapters using the included qla2xxx_npiv fabric skeleton, and which will be enabled in the next round of review in RFC-v4.

Thanks!

--nab

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/qla_attr.c                   |    5 +-
 drivers/scsi/qla2xxx/qla_dbg.h                    |   34 +
 drivers/scsi/qla2xxx/qla_def.h                    |   63 +-
 drivers/scsi/qla2xxx/qla_gbl.h                    |    7 +
 drivers/scsi/qla2xxx/qla_gs.c                     |    4 +-
 drivers/scsi/qla2xxx/qla_init.c                   |   79 +-
 drivers/scsi/qla2xxx/qla_iocb.c                   |  105 +-
 drivers/scsi/qla2xxx/qla_isr.c                    |   83 +-
 drivers/scsi/qla2xxx/qla_mbx.c                    |  122 +-
 drivers/scsi/qla2xxx/qla_mid.c                    |   21 +-
 drivers/scsi/qla2xxx/qla_os.c                     |  107 +-
 drivers/scsi/qla2xxx/qla_target.c                 | 5556 +++++++++++++++++++++
 drivers/scsi/qla2xxx/qla_target.h                 | 1137 +++++
 drivers/target/Kconfig                            |    1 +
 drivers/target/Makefile                           |    1 +
 drivers/target/tcm_qla2xxx/Kconfig                |    7 +
 drivers/target/tcm_qla2xxx/Makefile               |    6 +
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h     |  102 +
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c | 1439 ++++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c   |  853 ++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h   |   53 +
 22 files changed, 9750 insertions(+), 39 deletions(-)
 create mode 100644 drivers/scsi/qla2xxx/qla_target.c
 create mode 100644 drivers/scsi/qla2xxx/qla_target.h
 create mode 100644 drivers/target/tcm_qla2xxx/Kconfig
 create mode 100644 drivers/target/tcm_qla2xxx/Makefile
 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.4.3
#1 lore raw

RFC v3 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 qla_target.c using modern 8.03.07.00 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 qla_target.h here:

struct qla_target_template {

       int (*handle_cmd)(struct scsi_qla_host *, struct qla_tgt_cmd *, uint32_t,
                       uint32_t, int, int, int);
       int (*handle_data)(struct qla_tgt_cmd *);
       int (*handle_tmr)(struct qla_tgt_mgmt_cmd *, uint32_t, uint8_t);
       void (*free_cmd)(struct qla_tgt_cmd *);
       void (*free_session)(struct qla_tgt_sess *);

       int (*check_initiator_node_acl)(struct scsi_qla_host *, unsigned char *,
                                       void *, uint8_t *, uint16_t);
       struct qla_tgt_sess *(*find_sess_by_loop_id)(struct scsi_qla_host *,
                                               const uint16_t);
       struct qla_tgt_sess *(*find_sess_by_s_id)(struct scsi_qla_host *,
                                               const uint8_t *);
};

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

There are still a handful of TODO items including proper FC SRR support (service retransmission request) and properly handling nexus reset and tcm_qla2xxx module shutdown+restart w/o having to reload qla2xxx.ko

---
 drivers/scsi/qla2xxx/qla_target.c | 5556 +++++++++++++++++++++++++++++++++++++
 drivers/scsi/qla2xxx/qla_target.h | 1137 ++++++++
 2 files changed, 6693 insertions(+), 0 deletions(-)
 create mode 100644 drivers/scsi/qla2xxx/qla_target.c
 create mode 100644 drivers/scsi/qla2xxx/qla_target.h
drivers/scsi/qla2xxx/qla_target.c new file +5556 -0
new file mode 100644
index 0000000..713e684
@@ -0,0 +1,5556 @@
+/*
+ *  qla_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-2011 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 <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.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 "qla_target.h"
+
+static char *qlini_mode = QLA2X_INI_MODE_STR_EXCLUSIVE;
+module_param(qlini_mode, charp, S_IRUGO);
+MODULE_PARM_DESC(qlini_mode,
+	"Determines when initiator mode will be enabled. Possible values: "
+	"\"exclusive\" (default) - initiator mode will be enabled on load, "
+	"disabled on enabling target mode and then on disabling target mode "
+	"enabled back; "
+	"\"disabled\" - initiator mode will never be enabled; "
+	"\"enabled\" - initiator mode will always stay enabled.");
+
+static int ql2x_ini_mode = QLA2X_INI_MODE_EXCLUSIVE;
+
+/*
+ * 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 qla_tgt doesn't
+ *     cross those functions boundaries, except tgt_stop, which
+ *     additionally protected by irq_cmd_count.
+ */
+
+static int __qla24xx_xmit_response(struct qla_tgt_cmd *, int, uint8_t);
+
+/* Predefs for callbacks handed to qla2xxx LLD */
+static void qla24xx_atio_pkt(struct scsi_qla_host *ha, atio7_entry_t *pkt);
+static void qla_tgt_response_pkt(struct scsi_qla_host *ha, response_t *pkt);
+static int qla_tgt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
+	int fn, void *iocb, int flags);
+static void qla2xxx_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd *cmd,
+	atio_entry_t *atio, int ha_locked);
+static void qla24xx_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd *cmd,
+	atio7_entry_t *atio, int ha_locked);
+static void qla_tgt_reject_free_srr_imm(struct scsi_qla_host *ha, struct srr_imm *imm,
+	int ha_lock);
+static int qla_tgt_cut_cmd_data_head(struct qla_tgt_cmd *cmd, unsigned int offset);
+static void qla_tgt_clear_tgt_db(struct qla_tgt *tgt, bool local_only);
+static int qla_tgt_unreg_sess(struct qla_tgt_sess *sess);
+
+/* Used by qla_target.c code to decode SCSI LUN to TCM unpacked_lun */
+static uint32_t qla_tgt_unpack_lun(unsigned char *p);
+
+/*
+ * Global Variables
+ */
+static struct kmem_cache *qla_tgt_cmd_cachep;
+static struct kmem_cache *qla_tgt_mgmt_cmd_cachep;
+static mempool_t *qla_tgt_mgmt_cmd_mempool;
+
+static DECLARE_RWSEM(qla_tgt_unreg_rwsem);
+
+/*
+ * From qla2xxx/qla_iobc.c and used by various qla_target.c logic
+ */
+extern request_t *qla2x00_req_pkt(struct scsi_qla_host *);
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline void qla_tgt_sess_get(struct qla_tgt_sess *sess)
+{
+	sess->sess_ref++;
+	DEBUG21(qla_printk(KERN_INFO, sess->vha->hw, "sess %p, new sess_ref %d\n",
+			sess, sess->sess_ref));
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+void qla_tgt_sess_put(struct qla_tgt_sess *sess)
+{
+	DEBUG21(qla_printk(KERN_INFO, sess->vha->hw, "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)
+		qla_tgt_unreg_sess(sess);
+}
+EXPORT_SYMBOL(qla_tgt_sess_put);
+
+/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
+static struct qla_tgt_sess *qla_tgt_find_sess_by_port_name(
+	struct qla_tgt *tgt,
+	const uint8_t *port_name)
+{
+	struct qla_tgt_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;
+}
+
+/* Might release hw lock, then reaquire!! */
+static inline int qla_tgt_issue_marker(struct scsi_qla_host *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 "qla_target(%d): issue_marker() "
+				"failed\n", vha->vp_idx);
+		}
+		return rc;
+	}
+	return QLA_SUCCESS;
+}
+
+static inline
+struct scsi_qla_host *qla_tgt_find_host_by_d_id(struct scsi_qla_host *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
+struct scsi_qla_host *qla_tgt_find_host_by_vp_idx(struct scsi_qla_host *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 qla24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, atio7_entry_t *atio)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	switch (atio->entry_type) {
+	case ATIO_TYPE7:
+	{
+		struct scsi_qla_host *host = qla_tgt_find_host_by_d_id(vha, atio->fcp_hdr.d_id);
+		if (unlikely(NULL == host)) {
+			printk(KERN_ERR "qla_target(%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;
+		}
+		qla24xx_atio_pkt(host, atio);
+		break;
+	}
+
+	case IMMED_NOTIFY_TYPE:
+	{
+		struct scsi_qla_host *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 = qla_tgt_find_host_by_vp_idx(vha,
+							entry->vp_index);
+				if (unlikely(!host)) {
+					printk(KERN_ERR "qla_target(%d): Received "
+						"ATIO (IMMED_NOTIFY_TYPE) "
+						"with unknown vp_index %d\n",
+						vha->vp_idx, entry->vp_index);
+					break;
+				}
+			}
+		}
+		qla24xx_atio_pkt(host, atio);
+		break;
+	}
+
+	default:
+		printk(KERN_ERR "qla_target(%d): Received unknown ATIO atio "
+		     "type %x\n", vha->vp_idx, atio->entry_type);
+		break;
+	}
+
+	return;
+}
+
+void qla_tgt_response_pkt_all_vps(struct scsi_qla_host *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;
+		struct scsi_qla_host *host = qla_tgt_find_host_by_vp_idx(vha,
+						entry->vp_index);
+		if (unlikely(!host)) {
+			printk(KERN_ERR "qla_target(%d): Response pkt (CTIO_TYPE7) "
+				"received, with unknown vp_index %d\n",
+				vha->vp_idx, entry->vp_index);
+			break;
+		}
+		qla_tgt_response_pkt(host, pkt);
+		break;
+	}
+
+	case IMMED_NOTIFY_TYPE:
+	{
+		struct scsi_qla_host *host = vha;
+		if (IS_FWI2_CAPABLE(ha)) {
+			notify24xx_entry_t *entry = (notify24xx_entry_t *)pkt;
+			host = qla_tgt_find_host_by_vp_idx(vha, entry->vp_index);
+			if (unlikely(!host)) {
+				printk(KERN_ERR "qla_target(%d): Response pkt "
+					"(IMMED_NOTIFY_TYPE) received, "
+					"with unknown vp_index %d\n",
+					vha->vp_idx, entry->vp_index);
+				break;
+			}
+		}
+		qla_tgt_response_pkt(host, pkt);
+		break;
+	}
+
+	case NOTIFY_ACK_TYPE:
+	{
+		struct scsi_qla_host *host = vha;
+		if (IS_FWI2_CAPABLE(ha)) {
+			nack24xx_entry_t *entry = (nack24xx_entry_t *)pkt;
+			if (0xFF != entry->vp_index) {
+				host = qla_tgt_find_host_by_vp_idx(vha,
+						entry->vp_index);
+				if (unlikely(!host)) {
+					printk(KERN_ERR "qla_target(%d): Response "
+						"pkt (NOTIFY_ACK_TYPE) "
+						"received, with unknown "
+						"vp_index %d\n", vha->vp_idx,
+						entry->vp_index);
+					break;
+				}
+			}
+		}
+		qla_tgt_response_pkt(host, pkt);
+		break;
+	}
+
+	case ABTS_RECV_24XX:
+	{
+		abts24_recv_entry_t *entry = (abts24_recv_entry_t *)pkt;
+		struct scsi_qla_host *host = qla_tgt_find_host_by_vp_idx(vha,
+						entry->vp_index);
+		if (unlikely(!host)) {
+			printk(KERN_ERR "qla_target(%d): Response pkt "
+				"(ABTS_RECV_24XX) received, with unknown "
+				"vp_index %d\n", vha->vp_idx, entry->vp_index);
+			break;
+		}
+		qla_tgt_response_pkt(host, pkt);
+		break;
+	}
+
+	case ABTS_RESP_24XX:
+	{
+		abts24_resp_entry_t *entry = (abts24_resp_entry_t *)pkt;
+		struct scsi_qla_host *host = qla_tgt_find_host_by_vp_idx(vha,
+						entry->vp_index);
+		if (unlikely(!host)) {
+			printk(KERN_ERR "qla_target(%d): Response pkt "
+				"(ABTS_RECV_24XX) received, with unknown "
+				"vp_index %d\n", vha->vp_idx, entry->vp_index);
+			break;
+		}
+		qla_tgt_response_pkt(host, pkt);
+		break;
+	}
+
+	default:
+		qla_tgt_response_pkt(vha, pkt);
+		break;
+	}
+
+}
+
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qla_tgt_free_session_done(struct qla_tgt_sess *sess)
+{
+	struct qla_tgt *tgt;
+	struct scsi_qla_host *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);
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "Unregistration of"
+		" sess %p finished\n", sess));
+
+	kfree(sess);
+
+	if (!tgt)
+		return;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "empty(sess_list) %d"
+		" sess_count %d\n", list_empty(&tgt->sess_list), tgt->sess_count));
+	/*
+	 * 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 qla_tgt_unreg_sess(struct qla_tgt_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 "qla_target(%d): %ssession for loop_id %d deleted\n",
+		sess->vha->vp_idx, sess->local ? "local " : "",
+		sess->loop_id);
+
+	qla_tgt_free_session_done(sess);
+
+	return res;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qla_tgt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_sess *sess = NULL;
+	uint32_t unpacked_lun, lun = 0;
+	uint16_t 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 qla_tgt_reset with loop_id=0xffff global event............\n");
+		atomic_inc(&ha->qla_tgt->tgt_global_resets_count);
+		qla_tgt_clear_tgt_db(ha->qla_tgt, 1);
+		if (!list_empty(&ha->qla_tgt->sess_list)) {
+			sess = list_entry(ha->qla_tgt->sess_list.next,
+				typeof(*sess), sess_list_entry);
+			switch (mcmd) {
+			case QLA_TGT_NEXUS_LOSS_SESS:
+				mcmd = QLA_TGT_NEXUS_LOSS;
+				break;
+			case QLA_TGT_ABORT_ALL_SESS:
+				mcmd = QLA_TGT_ABORT_ALL;
+				break;
+			case QLA_TGT_NEXUS_LOSS:
+			case QLA_TGT_ABORT_ALL:
+				break;
+			default:
+				printk(KERN_ERR "qla_target(%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);
+	}
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Using sess for qla_tgt_reset: %p\n", sess));
+	if (!sess) {
+		res = -ESRCH;
+		ha->qla_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);
+
+	if (IS_FWI2_CAPABLE(ha)) {
+		atio7_entry_t *a = (atio7_entry_t *)iocb;
+		lun = a->fcp_cmnd.lun;
+	} else {
+		notify_entry_t *n = (notify_entry_t *)iocb;
+		lun = swab16(le16_to_cpu(n->lun));
+	}
+	unpacked_lun = qla_tgt_unpack_lun((unsigned char *)&lun);
+
+	return qla_tgt_issue_task_mgmt(sess, unpacked_lun, mcmd,
+				iocb, Q24_MGMT_SEND_NACK);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qla_tgt_schedule_sess_for_deletion(struct qla_tgt_sess *sess)
+{
+	struct qla_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);
+
+	DEBUG21(qla_printk(KERN_INFO, sess->vha->hw, "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 "qla_target(%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 qla_tgt_clear_tgt_db(struct qla_tgt *tgt, bool local_only)
+{
+	struct qla_tgt_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;
+			qla_tgt_schedule_sess_for_deletion(sess);
+		} else
+			qla_tgt_sess_put(sess);
+	}
+
+	/* At this point tgt could be already dead */
+}
+
+static int qla24xx_get_loop_id(struct scsi_qla_host *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 "qla_target(%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 "qla_target(%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 qla_tgt_check_fcport_exist(struct scsi_qla_host *vha, struct qla_tgt_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->qla_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 "qla_tgt_check_fcport_exist(): loop_id %d", loop_id);
+
+	fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
+	if (fcport == NULL) {
+		printk(KERN_ERR "qla_target(%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 "qla_target(%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->qla_tgt->tgt_global_resets_count)) {
+		DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%d): global reset"
+			" during session discovery (counter was %d, new %d),"
+			" retrying", vha->vp_idx, global_resets,
+			atomic_read(&ha->qla_tgt->tgt_global_resets_count)));
+		goto retry;
+	}
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "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 qla_tgt_undelete_sess(struct qla_tgt_sess *sess)
+{
+	BUG_ON(!sess->deleted);
+
+	list_del(&sess->del_list_entry);
+	sess->deleted = 0;
+}
+
+static void qla_tgt_del_sess_work_fn(struct delayed_work *work)
+{
+	struct qla_tgt *tgt = container_of(work, struct qla_tgt,
+					sess_del_work);
+	struct scsi_qla_host *vha = tgt->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_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;
+
+			qla_tgt_undelete_sess(sess);
+
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+			cancel = qla_tgt_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 "qla_target(%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 {
+				DEBUG22(qla_printk(KERN_INFO, ha, "Timeout: sess %p"
+					" about to be deleted\n", sess));
+				qla_tgt_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 qla_tgt_sess *qla_tgt_create_sess(
+	struct scsi_qla_host *vha,
+	fc_port_t *fcport,
+	bool local)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_sess *sess;
+	unsigned long flags;
+	unsigned char be_sid[3];
+
+	/* Check to avoid double sessions */
+#if 0
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	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])) {
+			DEBUG22(qla_printk(KERN_INFO, "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)
+				qla_tgt_undelete_sess(sess);
+
+			qla_tgt_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_irqrestore(&ha->hardware_lock, flags);
+			goto out;
+		}
+	}
+	spin_unlock_irq_restore(&ha->hardware_lock, flags);
+#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 "qla_target(%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->qla_tgt;
+	sess->vha = vha;
+
+	sess->s_id = fcport->d_id;
+	sess->loop_id = fcport->loop_id;
+	sess->local = local;
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "Adding sess %p to tgt %p via"
+		" ->check_initiator_node_acl()\n", sess, ha->qla_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, &fcport->port_name[0],
+				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_irqsave(&ha->hardware_lock, flags);
+	list_add_tail(&sess->sess_list_entry, &ha->qla_tgt->sess_list);
+	ha->qla_tgt->sess_count++;
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	printk(KERN_INFO "qla_target(%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 qla_tgt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt *tgt = ha->qla_tgt;
+	struct qla_tgt_sess *sess;
+	unsigned long flags;
+	unsigned char s_id[3];
+
+	if (!vha->hw->qla2x_tmpl)
+		return;
+
+	if (!tgt || (fcport->port_type != FCT_INITIATOR))
+		return;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (tgt->tgt_stop) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		return;
+	}
+	sess = qla_tgt_find_sess_by_port_name(tgt, fcport->port_name);
+	if (!sess) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+		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 = qla_tgt_create_sess(vha, fcport, false);
+		mutex_unlock(&ha->tgt_mutex);
+
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		if (sess != NULL)
+			qla_tgt_sess_put(sess); /* put the extra creation ref */
+	} else {
+		if (sess->deleted) {
+			qla_tgt_undelete_sess(sess);
+
+			printk(KERN_INFO "qla_target(%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);
+
+			DEBUG22(qla_printk(KERN_INFO, ha, "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 && sess->local) {
+		printk(KERN_INFO "qla_target(%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_irqrestore(&ha->hardware_lock, flags);
+}
+
+void qla_tgt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt *tgt = ha->qla_tgt;
+	struct qla_tgt_sess *sess;
+	unsigned long flags;
+
+	if (!vha->hw->qla2x_tmpl)
+		return;
+
+	if (!tgt || (fcport->port_type != FCT_INITIATOR))
+		return;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (tgt->tgt_stop) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		return;
+	}
+	sess = qla_tgt_find_sess_by_port_name(tgt, fcport->port_name);
+	if (!sess) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		return;
+	}
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "qla_tgt_fc_port_deleted %p", sess));
+
+	sess->local = 1;
+	qla_tgt_schedule_sess_for_deletion(sess);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static inline int test_tgt_sess_count(struct qla_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);
+	DEBUG21(qla_printk(KERN_INFO, ha, "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 qla_tgt_stop_phase1(struct qla_tgt *tgt)
+{
+	struct scsi_qla_host *vha = tgt->vha;
+	struct qla_hw_data *ha = tgt->ha;
+	unsigned long flags;
+
+	if (tgt->tgt_stop || tgt->tgt_stopped) {
+		printk(KERN_ERR "Already in tgt->tgt_stop or tgt_stopped state\n");
+		dump_stack();
+		return;
+	}
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Stopping target for host %ld(%p)\n",
+				vha->host_no, vha));
+	/*
+	 * Mutex needed to sync with qla_tgt_fc_port_[added,deleted].
+	 * Lock is needed, because we still can get an incoming packet.
+	 */
+	mutex_lock(&ha->tgt_mutex);
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	tgt->tgt_stop = 1;
+	qla_tgt_clear_tgt_db(tgt, false);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	mutex_unlock(&ha->tgt_mutex);
+
+	cancel_delayed_work_sync(&tgt->sess_del_work);
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "Waiting for sess works (tgt %p)", tgt));
+	spin_lock_irqsave(&tgt->sess_work_lock, flags);
+	while (!list_empty(&tgt->sess_works_list)) {
+		spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+		flush_scheduled_work();
+		spin_lock_irqsave(&tgt->sess_work_lock, flags);
+	}
+	spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "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))
+		qla_tgt_disable_vha(vha);
+
+	/* Wait for sessions to clear out (just in case) */
+	wait_event(tgt->waitQ, test_tgt_sess_count(tgt));
+}
+EXPORT_SYMBOL(qla_tgt_stop_phase1);
+
+/* Called by tcm_qla2xxx configfs code */
+void qla_tgt_stop_phase2(struct qla_tgt *tgt)
+{
+	struct qla_hw_data *ha = tgt->ha;
+	unsigned long flags;
+
+	if (tgt->tgt_stopped) {
+		printk(KERN_ERR "Already in tgt->tgt_stopped state\n");
+		dump_stack();
+		return;
+	}
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "Waiting for %d IRQ commands to"
+		" complete (tgt %p)", tgt->irq_cmd_count, tgt));
+
+	mutex_lock(&ha->tgt_mutex);
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	while (tgt->irq_cmd_count != 0) {
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		udelay(2);
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+	}
+	tgt->tgt_stop = 0;
+	tgt->tgt_stopped = 1;
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	mutex_unlock(&ha->tgt_mutex);
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "Stop of tgt %p finished", tgt));
+}
+EXPORT_SYMBOL(qla_tgt_stop_phase2);
+
+/* Called from qla_tgt_remove_target() -> qla2x00_remove_one() */
+void qla_tgt_release(struct qla_tgt *tgt)
+{
+	struct qla_hw_data *ha = tgt->ha;
+
+	if ((ha->qla_tgt != NULL) && !tgt->tgt_stopped)
+		qla_tgt_stop_phase2(tgt);
+
+	ha->qla_tgt = NULL;
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "Release of tgt %p finished\n", tgt));
+
+	kfree(tgt);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qla_tgt_sched_sess_work(struct qla_tgt *tgt, int type,
+	const void *param, unsigned int param_size)
+{
+	struct qla_tgt_sess_work_param *prm;
+	unsigned long flags;
+
+	prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
+	if (!prm ) {
+		printk(KERN_ERR "qla_target(%d): Unable to create session "
+			"work, command will be refused", 0);
+		return -ENOMEM;
+	}
+
+	DEBUG22(qla_printk(KERN_INFO, tgt->vha->hw, "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 qla_tgt_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 qla_tgt_modify_command_count(struct scsi_qla_host *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 *)qla2x00_req_pkt(vha);
+	if (!pkt) {
+		printk(KERN_ERR "qla_target(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		return;
+	}
+
+	ha->qla_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 */
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qla2xxx_send_notify_ack(struct scsi_qla_host *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;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Sending NOTIFY_ACK (ha=%p)\n", ha));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	ntfy = (nack_entry_t *)qla2x00_req_pkt(vha);
+	if (!ntfy) {
+		printk(KERN_ERR "qla_target(%d): %s failed: unable to allocate "
+			"request packet\n", vha->vp_idx, __func__);
+		return;
+	}
+
+	if (ha->qla_tgt != NULL)
+		ha->qla_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);
+	}
+
+	DEBUG23(qla_printk(KERN_INFO, ha, "qla_target(%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)));
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qla24xx_send_abts_resp(struct scsi_qla_host *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;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Sending task mgmt ABTS response"
+		" (ha=%p, atio=%p, status=%x\n", ha, abts, status));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	resp = (abts24_resp_entry_t *)qla2x00_req_pkt(vha);
+	if (!resp) {
+		printk(KERN_ERR "qla_target(%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 */
+	}
+
+	ha->qla_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 qla24xx_retry_term_exchange(struct scsi_qla_host *vha,
+	abts24_resp_fw_entry_t *entry)
+{
+	ctio7_status1_entry_t *ctio;
+
+	DEBUG21(qla_printk(KERN_INFO, vha->hw, "Sending retry TERM EXCH CTIO7"
+			" (ha=%p)\n", vha->hw));
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(vha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla_target(%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 = QLA_TGT_SKIP_HANDLE |	CTIO_COMPLETION_HANDLE_MARK;
+	ctio->common.timeout = __constant_cpu_to_le16(QLA_TGT_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;
+
+	qla2x00_isp_cmd(vha, vha->req);
+
+	qla24xx_send_abts_resp(vha, (abts24_recv_entry_t *)entry,
+		FCP_TMF_CMPL, true);
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static int __qla24xx_handle_abts(struct scsi_qla_host *vha, abts24_recv_entry_t *abts,
+	struct qla_tgt_sess *sess)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_mgmt_cmd *mcmd;
+	int rc;
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%d): task abort (tag=%d)\n",
+		vha->vp_idx, abts->exchange_addr_to_abort));
+
+	mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
+	if (mcmd == NULL) {
+		printk(KERN_ERR "qla_target(%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));
+
+	rc = ha->qla2x_tmpl->handle_tmr(mcmd, 0, ABORT_TASK);
+	if (rc != 0) {
+		printk(KERN_ERR "qla_target(%d):  qla2x_tmpl->handle_tmr()"
+				" failed: %d", vha->vp_idx, rc);
+		mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qla24xx_handle_abts(struct scsi_qla_host *vha, abts24_recv_entry_t *abts)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_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 "qla_target(%d): ABTS: Abort Sequence not "
+			"supported\n", vha->vp_idx);
+		qla24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		return;
+	}
+
+	if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
+		DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%d): ABTS: Unknown Exchange "
+			"Address received\n", vha->vp_idx));
+		qla24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		return;
+	}
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%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) {
+		DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%d): task abort for"
+			" non-existant session\n", vha->vp_idx));
+		rc = qla_tgt_sched_sess_work(ha->qla_tgt, QLA_TGT_SESS_WORK_ABORT,
+					abts, sizeof(*abts));
+		if (rc != 0) {
+			ha->qla_tgt->tm_to_unknown = 1;
+			qla24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
+		}
+		return;
+	}
+
+	rc = __qla24xx_handle_abts(vha, abts, sess);
+	if (rc != 0) {
+		printk(KERN_ERR "qla_target(%d): __qla24xx_handle_abts() failed: %d\n",
+			    vha->vp_idx, rc);
+		qla24xx_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 qla24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha,
+	struct qla_tgt_mgmt_cmd *mcmd, uint32_t resp_code)
+{
+	const atio7_entry_t *atio = &mcmd->orig_iocb.atio7;
+	ctio7_status1_entry_t *ctio;
+
+	DEBUG21(qla_printk(KERN_INFO, ha->hw, "Sending task mgmt CTIO7 (ha=%p,"
+		" atio=%p, resp_code=%x\n", ha, atio, resp_code));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(ha, 1) != QLA_SUCCESS)
+		return;
+
+	ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla_target(%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 = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
+	ctio->common.nport_handle = mcmd->sess->loop_id;
+	ctio->common.timeout = __constant_cpu_to_le16(QLA_TGT_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);
+
+	qla2x00_isp_cmd(ha, ha->req);
+}
+
+/*
+ * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ */
+static void qla24xx_send_notify_ack(struct scsi_qla_host *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;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Sending NOTIFY_ACK24 (ha=%p)\n", ha));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, 1) != QLA_SUCCESS)
+		return;
+
+	if (ha->qla_tgt != NULL)
+		ha->qla_tgt->notify_ack_expected++;
+
+	nack = (nack24xx_entry_t *)qla2x00_req_pkt(vha);
+	if (!nack) {
+		printk(KERN_ERR "qla_target(%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;
+
+	DEBUG23(qla_printk(KERN_INFO, ha, "qla_target(%d): Sending 24xx Notify Ack %d\n",
+		vha->vp_idx, nack->status));
+
+	qla2x00_isp_cmd(vha, vha->req);
+}
+
+void qla_tgt_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
+{
+	mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+}
+EXPORT_SYMBOL(qla_tgt_free_mcmd);
+
+/* callback from target fabric module code */
+void qla_tgt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd)
+{
+	struct scsi_qla_host *vha;
+	struct qla_hw_data *ha;
+	unsigned long flags;
+
+	DEBUG22(qla_printk(KERN_INFO, mcmd->sess->vha->hw, "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) {
+			qla24xx_send_notify_ack(vha,
+				&mcmd->orig_iocb.notify_entry24, 0, 0, 0);
+		} else {
+			if (mcmd->se_tmr_req->function == ABORT_TASK)
+				qla24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts,
+					mcmd->fc_tm_rsp, false);
+			else
+				qla24xx_send_task_mgmt_ctio(vha, mcmd, mcmd->fc_tm_rsp);
+		}
+	} else {
+		qla2xxx_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);
+}
+EXPORT_SYMBOL(qla_tgt_xmit_tm_rsp);
+
+/* No locks */
+static int qla_tgt_pci_map_calc_cnt(struct qla_tgt_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++;
+	}
+
+	DEBUG21(qla_printk(KERN_INFO, prm->cmd->vha->hw, "seg_cnt=%d, req_cnt=%d\n",
+			prm->seg_cnt, prm->req_cnt));
+	return 0;
+
+out_err:
+	printk(KERN_ERR "qla_target(%d): PCI mapping failed: sg_cnt=%d",
+		0, prm->cmd->sg_cnt);
+	return -1;
+}
+
+static inline void qla_tgt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_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 qla_tgt_check_reserve_free_req(struct scsi_qla_host *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));
+		DEBUG21(qla_printk(KERN_INFO, ha, "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 "qla_target(%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 *qla_tgt_get_req_pkt(struct scsi_qla_host *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 qla_tgt_make_handle(struct scsi_qla_host *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 QLA_TGT_NULL_HANDLE */
+		if (h == ha->current_handle) {
+			printk(KERN_INFO "qla_target(%d): Ran out of "
+				"empty cmd slots in ha %p\n", vha->vp_idx, ha);
+			h = QLA_TGT_NULL_HANDLE;
+			break;
+		}
+	} while ((h == QLA_TGT_NULL_HANDLE) ||
+		 (h == QLA_TGT_SKIP_HANDLE) ||
+		 (ha->cmds[h-1] != NULL));
+
+	if (h != QLA_TGT_NULL_HANDLE)
+		ha->current_handle = h;
+
+	return h;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static void qla2xxx_build_ctio_pkt(struct qla_tgt_prm *prm, struct scsi_qla_host *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 = qla_tgt_make_handle(vha);
+	if (h != QLA_TGT_NULL_HANDLE)
+		ha->cmds[h-1] = prm->cmd;
+
+	pkt->common.handle = h | CTIO_COMPLETION_HANDLE_MARK;
+	pkt->common.timeout = __constant_cpu_to_le16(QLA_TGT_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);
+
+	DEBUG23(qla_printk(KERN_INFO, ha, "qla_target(%d): handle(se_cmd) -> %08x, "
+		"timeout %d L %#x -> I %#x E %#x\n", vha->vp_idx,
+		pkt->common.handle, QLA_TGT_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 qla24xx_build_ctio_pkt(struct qla_tgt_prm *prm, struct scsi_qla_host *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 = qla_tgt_make_handle(vha);
+	if (unlikely(h == QLA_TGT_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(QLA_TGT_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);
+
+	DEBUG23(qla_printk(KERN_INFO, ha, "qla_target(%d): handle(cmd) -> %08x, "
+		"timeout %d, ox_id %#x\n", vha->vp_idx, pkt->common.handle,
+		QLA_TGT_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 qla_tgt_load_cont_data_segments(struct qla_tgt_prm *prm, struct scsi_qla_host *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 *)qla_tgt_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));
+
+			DEBUG24(qla_printk(KERN_INFO, vha->hw, "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++;
+		}
+	}
+}
+
+/*
+ * 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 qla2xxx_load_data_segments(struct qla_tgt_prm *prm, struct scsi_qla_host *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;
+
+	DEBUG23(qla_printk(KERN_INFO, vha->hw, "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;
+		return;
+	}
+
+	/* Set total data segment count */
+	pkt->dseg_count = cpu_to_le16(prm->seg_cnt);
+
+	/* If scatter gather */
+	DEBUG24(qla_printk(KERN_INFO, vha->hw, "%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));
+
+		DEBUG24(qla_printk(KERN_INFO, vha->hw, "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++;
+	}
+
+	qla_tgt_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 qla24xx_load_data_segments(struct qla_tgt_prm *prm, struct scsi_qla_host *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;
+
+	DEBUG21(qla_printk(KERN_INFO, vha->hw, "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;
+		return;
+	}
+
+	/* Set total data segment count */
+	pkt->common.dseg_count = cpu_to_le16(prm->seg_cnt);
+
+	/* If scatter gather */
+	DEBUG24(qla_printk(KERN_INFO, vha->hw, "%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));
+
+		DEBUG24(qla_printk(KERN_INFO, vha->hw, "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++;
+	}
+
+	qla_tgt_load_cont_data_segments(prm, vha);
+}
+
+static inline int qla_tgt_has_data(struct qla_tgt_cmd *cmd)
+{
+	return cmd->bufflen > 0;
+}
+
+/*
+ * Called without ha->hardware_lock held
+ */
+static int qla_tgt_pre_xmit_response(struct qla_tgt_cmd *cmd, struct qla_tgt_prm *prm,
+			int xmit_type, uint8_t scsi_status, uint32_t *full_req_cnt)
+{
+	struct qla_tgt *tgt = cmd->tgt;
+	struct scsi_qla_host *vha = tgt->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct se_cmd *se_cmd = &cmd->se_cmd;
+
+	if (unlikely(cmd->aborted)) {
+		DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%d): terminating exchange "
+			"for aborted cmd=%p (se_cmd=%p, tag=%d)",
+			vha->vp_idx, cmd, se_cmd, cmd->tag));
+
+		cmd->state = QLA_TGT_STATE_ABORTED;
+
+		if (IS_FWI2_CAPABLE(ha))
+			qla24xx_send_term_exchange(vha, cmd, &cmd->atio.atio7, 0);
+		else
+			qla2xxx_send_term_exchange(vha, cmd, &cmd->atio.atio2x, 0);
+		/* !! At this point cmd could be already freed !! */
+		return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED;
+	}
+
+	DEBUG23(qla_printk(KERN_INFO, ha, "qla_target(%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;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "rq_result=%x, xmit_type=%x\n",
+				prm->rq_result, xmit_type));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, 0) != QLA_SUCCESS)
+		return -EFAULT;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "CTIO start: vha(%d)\n", vha->vp_idx));
+
+	if ((xmit_type & QLA_TGT_XMIT_DATA) && qla_tgt_has_data(cmd)) {
+		if  (qla_tgt_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;
+		DEBUG21(qla_printk(KERN_INFO, ha, "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;
+		DEBUG21(qla_printk(KERN_INFO, ha, "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 & QLA_TGT_XMIT_STATUS) {
+		/*
+		 * If QLA_TGT_XMIT_DATA is not set, add_status_pkt will be ignored
+		 * in *xmit_response() below
+		 */
+		if (qla_tgt_has_data(cmd)) {
+			if (QLA_TGT_SENSE_VALID(prm->sense_buffer) ||
+			    (IS_FWI2_CAPABLE(ha) &&
+			     (prm->rq_result != 0))) {
+				prm->add_status_pkt = 1;
+				(*full_req_cnt)++;
+			}
+		}
+	}
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "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 qla_tgt_need_explicit_conf(struct qla_hw_data *ha,
+	struct qla_tgt_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 qla_tgt_init_ctio_ret_entry(ctio_ret_entry_t *ctio_m1,
+	struct qla_tgt_prm *prm, struct scsi_qla_host *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 (qla_tgt_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 (QLA_TGT_SENSE_VALID(prm->sense_buffer)) {
+		if (qla_tgt_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 __qla2xxx_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, uint8_t scsi_status)
+{
+	struct scsi_qla_host *vha = cmd->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_prm prm;
+	ctio_common_entry_t *pkt;
+	unsigned long flags = 0;
+	uint32_t full_req_cnt = 0;
+	int res;
+
+	memset(&prm, 0, sizeof(prm));
+
+	res = qla_tgt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, &full_req_cnt);
+	if (unlikely(res != 0)) {
+		if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED)
+			return 0;
+
+		return res;
+	}
+
+	if (cmd->locked_rsp)
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	/* Does F/W have an IOCBs for this request */
+	res = qla_tgt_check_reserve_free_req(vha, full_req_cnt);
+	if (unlikely(res != 0) && (xmit_type & QLA_TGT_XMIT_DATA))
+		goto out_unmap_unlock;
+
+	qla2xxx_build_ctio_pkt(&prm, cmd->vha);
+	pkt = (ctio_common_entry_t *)prm.pkt;
+
+	if (qla_tgt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) {
+		pkt->flags |= __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_IN);
+		pkt->flags |= __constant_cpu_to_le16(OF_INC_RC);
+
+		qla2xxx_load_data_segments(&prm, vha);
+
+		if (prm.add_status_pkt == 0) {
+			if (xmit_type & QLA_TGT_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 (qla_tgt_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 *)qla_tgt_get_req_pkt(vha);
+
+			DEBUG21(qla_printk(KERN_INFO, ha, "%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);
+
+			qla_tgt_init_ctio_ret_entry(ctio_m1, &prm, cmd->vha);
+		}
+	} else
+		qla_tgt_init_ctio_ret_entry((ctio_ret_entry_t *)pkt,
+					&prm, cmd->vha);
+
+	cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Xmitting CTIO7 response pkt for 2xxx:"
+			" %p scsi_status: 0x%02x\n", pkt, scsi_status));
+
+	qla2x00_isp_cmd(vha, vha->req);
+	if (cmd->locked_rsp)
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return 0;
+
+out_unmap_unlock:
+	if (cmd->sg_mapped)
+		qla_tgt_unmap_sg(vha, cmd);
+	if (cmd->locked_rsp)
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+
+#ifdef CONFIG_QLA_TGT_DEBUG_SRR
+/*
+ *  Original taken from the XFS code
+ */
+static unsigned long qla_tgt_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 qla_tgt_check_srr_debug(struct qla_tgt_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 & QLA_TGT_XMIT_STATUS) && (qla_tgt_srr_random() % 200) == 50) {
+		*xmit_type &= ~QLA_TGT_XMIT_STATUS;
+		DEBUG22(qla_printk(KERN_INFO, "Dropping cmd %p (tag %d) status", cmd,
+			cmd->tag));
+	}
+#endif
+
+	if (qla_tgt_has_data(cmd) && (cmd->sg_cnt > 1) &&
+	    ((qla_tgt_srr_random() % 100) == 20)) {
+		int i, leave = 0;
+		unsigned int tot_len = 0;
+
+		while (leave == 0)
+			leave = qla_tgt_srr_random() % cmd->sg_cnt;
+
+		for (i = 0; i < leave; i++)
+			tot_len += cmd->sg[i].length;
+
+		DEBUG22(qla_printk(KERN_INFO, "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 (qla_tgt_has_data(cmd) && ((qla_tgt_srr_random() % 100) == 70)) {
+		unsigned int offset = qla_tgt_srr_random() % cmd->bufflen;
+
+		DEBUG22(qla_printk(KERN_INFO, "Cutting cmd %p (tag %d) buffer head "
+			"to offset %d (cmd->bufflen %d)", cmd, cmd->tag,
+			offset, cmd->bufflen));
+		if (offset == 0)
+			*xmit_type &= ~QLA_TGT_XMIT_DATA;
+		else if (qla_tgt_cut_cmd_data_head(cmd, offset)) {
+			DEBUG22(qla_printk(KERN_INFO, "qla_tgt_cut_cmd_data_head() failed (tag %d)",
+				cmd->tag));
+		}
+	}
+}
+#else
+static inline void qla_tgt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) {}
+#endif
+
+int qla2xxx_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, uint8_t scsi_status)
+{
+#if 0
+	qla_tgt_check_srr_debug(cmd, &xmit_type);
+#endif
+
+	DEBUG21(qla_printk(KERN_INFO, cmd->vha->hw, "is_send_status=%d,"
+		" cmd->bufflen=%d, cmd->sg_cnt=%d, cmd->dma_data_direction=%d",
+		(xmit_type & QLA_TGT_XMIT_STATUS) ? 1 : 0, cmd->bufflen,
+		cmd->sg_cnt, cmd->dma_data_direction));
+
+	return (IS_FWI2_CAPABLE(cmd->tgt->ha)) ?
+		__qla24xx_xmit_response(cmd, xmit_type, scsi_status) :
+		__qla2xxx_xmit_response(cmd, xmit_type, scsi_status);
+}
+EXPORT_SYMBOL(qla2xxx_xmit_response);
+
+static void qla24xx_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
+	struct qla_tgt_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 (qla_tgt_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 (QLA_TGT_SENSE_VALID(prm->sense_buffer)) {
+		int i;
+
+		ctio1 = (ctio7_status1_entry_t *)ctio;
+		if (qla_tgt_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 "qla_target(%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 QLA_TGT_XMIT_DATA and * QLA_TGT_XMIT_STATUS
+ * for >= 24xx silicon
+ */
+static int __qla24xx_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, uint8_t scsi_status)
+{
+	struct scsi_qla_host *vha = cmd->vha;
+	struct qla_hw_data *ha = vha->hw;
+	ctio7_status0_entry_t *pkt;
+	struct qla_tgt_prm prm;
+	uint32_t full_req_cnt = 0;
+	unsigned long flags = 0;
+	int res;
+
+	memset(&prm, 0, sizeof(prm));
+
+	res = qla_tgt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, &full_req_cnt);
+	if (unlikely(res != 0)) {
+		if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED)
+			return 0;
+
+		return res;
+	}
+
+	if (cmd->locked_rsp)
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+        /* Does F/W have an IOCBs for this request */
+	res = qla_tgt_check_reserve_free_req(vha, full_req_cnt);
+	if (unlikely(res != 0) && (xmit_type & QLA_TGT_XMIT_DATA))
+		goto out_unmap_unlock;
+
+	res = qla24xx_build_ctio_pkt(&prm, vha);
+	if (unlikely(res != 0))
+		goto out_unmap_unlock;
+
+
+	pkt = (ctio7_status0_entry_t *)prm.pkt;
+
+	if (qla_tgt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) {
+		pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN |
+				CTIO7_FLAGS_STATUS_MODE_0);
+
+		qla24xx_load_data_segments(&prm, vha);
+
+		if (prm.add_status_pkt == 0) {
+			if (xmit_type & QLA_TGT_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 (qla_tgt_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 *)qla_tgt_get_req_pkt(vha);
+
+			DEBUG21(qla_printk(KERN_INFO, ha, "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);
+			qla24xx_init_ctio_ret_entry((ctio7_status0_entry_t *)ctio,
+							&prm);
+			printk("Status CTIO7: %p\n", ctio);
+		}
+	} else
+		qla24xx_init_ctio_ret_entry(pkt, &prm);
+
+
+	cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Xmitting CTIO7 response pkt for 24xx:"
+			" %p scsi_status: 0x%02x\n", pkt, scsi_status));
+
+	qla2x00_isp_cmd(vha, vha->req);
+	if (cmd->locked_rsp)
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return 0;
+
+out_unmap_unlock:
+	if (cmd->sg_mapped)
+		qla_tgt_unmap_sg(vha, cmd);
+	if (cmd->locked_rsp)
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+
+int qla_tgt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
+{
+	struct scsi_qla_host *vha = cmd->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt *tgt = cmd->tgt;
+	struct qla_tgt_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 (qla_tgt_issue_marker(vha, 0) != QLA_SUCCESS)
+		return -EIO;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "CTIO_start: vha(%d)", (int)vha->vp_idx));
+
+	/* Calculate number of entries and segments required */
+	if (qla_tgt_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 = qla_tgt_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 = qla24xx_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);
+		qla24xx_load_data_segments(&prm, vha);
+		p = pkt;
+	} else {
+		ctio_common_entry_t *pkt;
+		qla2xxx_build_ctio_pkt(&prm, vha);
+		pkt = (ctio_common_entry_t *)prm.pkt;
+		pkt->flags = __constant_cpu_to_le16(OF_FAST_POST | OF_DATA_OUT);
+		qla2xxx_load_data_segments(&prm, vha);
+		p = pkt;
+	}
+
+	cmd->state = QLA_TGT_STATE_NEED_DATA;
+
+	qla2x00_isp_cmd(vha, vha->req);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+
+out_unlock_free_unmap:
+	if (cmd->sg_mapped)
+		qla_tgt_unmap_sg(vha, cmd);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	return res;
+}
+EXPORT_SYMBOL(qla_tgt_rdy_to_xfer);
+
+/* If hardware_lock held on entry, might drop it, then reaquire */
+static void qla2xxx_send_term_exchange(struct scsi_qla_host *vha, struct qla_tgt_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;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Sending TERM EXCH CTIO (ha=%p)\n", ha));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, ha_locked) != QLA_SUCCESS)
+		return;
+
+	if (!ha_locked)
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	ctio = (ctio_ret_entry_t *)qla2x00_req_pkt(vha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla_target(%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 < QLA_TGT_STATE_PROCESSED) {
+			printk(KERN_ERR "qla_target(%d): Terminating cmd %p with "
+				"incorrect state %d\n", vha->vp_idx, cmd,
+				cmd->state);
+		} else
+			do_tgt_cmd_done = 1;
+	}
+	ctio->handle = QLA_TGT_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);
+
+	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 qla24xx_send_term_exchange(struct scsi_qla_host *vha, struct qla_tgt_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;
+
+	DEBUG21(qla_printk(KERN_INFO, ha, "Sending TERM EXCH CTIO7 (ha=%p)\n", ha));
+
+	/* Send marker if required */
+	if (qla_tgt_issue_marker(vha, ha_locked) != QLA_SUCCESS)
+		return;
+
+	if (!ha_locked)
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(vha);
+	if (ctio == NULL) {
+		printk(KERN_ERR "qla_target(%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 < QLA_TGT_STATE_PROCESSED) {
+			printk(KERN_ERR "qla_target(%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 = QLA_TGT_SKIP_HANDLE |	CTIO_COMPLETION_HANDLE_MARK;
+	ctio->common.timeout = __constant_cpu_to_le16(QLA_TGT_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;
+
+	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 qla_tgt_free_cmd(struct qla_tgt_cmd *cmd)
+{
+	BUG_ON(cmd->sg_mapped);
+
+	if (unlikely(cmd->free_sg))
+		kfree(cmd->sg);
+	kmem_cache_free(qla_tgt_cmd_cachep, cmd);
+}
+EXPORT_SYMBOL(qla_tgt_free_cmd);
+
+/* ha->hardware_lock supposed to be held on entry */
+static int qla_tgt_prepare_srr_ctio(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd,
+	void *ctio)
+{
+	struct srr_ctio *sc;
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt *tgt = ha->qla_tgt;
+	struct srr_imm *imm;
+
+	tgt->ctio_srr_id++;
+
+	DEBUG22(qla_printk(KERN_INFO, ha, "qla_target(%d): CTIO with SRR "
+		"status received\n", vha->vp_idx));
+
+	if (!ctio) {
+		printk(KERN_ERR "qla_target(%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);
+		DEBUG22(qla_printk(KERN_INFO, ha, "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) {
+				DEBUG22(qla_printk(KERN_INFO, ha, "%s", "Scheduling srr work\n"));
+				schedule_work(&tgt->srr_work);
+			} else {
+				printk(KERN_ERR "qla_target(%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 "qla_target(%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) {
+				DEBUG22(qla_printk(KERN_INFO, ha, "IMM SRR %p deleted "
+					"(id %d)\n", imm, imm->srr_id));
+				list_del(&imm->srr_list_entry);
+				qla_tgt_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 qla_tgt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio,
+	struct qla_tgt_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) {
+			qla24xx_send_term_exchange(vha, cmd,
+				&cmd->atio.atio7, 1);
+		}
+	} else {
+		if (status != CTIO_SUCCESS)
+			qla_tgt_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) {
+			qla2xxx_send_term_exchange(vha, cmd,
+				&cmd->atio.atio2x, 1);
+		}
+#endif
+	}
+	return term;
+}
+
+/* ha->hardware_lock supposed to be held on entry */
+static inline struct qla_tgt_cmd *qla_tgt_get_cmd(struct scsi_qla_host *vha, uint32_t handle)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	handle--;
+	if (ha->cmds[handle] != NULL) {
+		struct qla_tgt_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 qla_tgt_cmd *qla_tgt_ctio_to_cmd(struct scsi_qla_host *vha, uint32_t handle,
+	void *ctio)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct qla_tgt_cmd *cmd = NULL;
+
+	/* Clear out internal marks */
+	handle &= ~(CTIO_COMPLETION_HANDLE_MARK | CTIO_INTERMEDIATE_HANDLE_MARK);
+
+	if (handle != QLA_TGT_NULL_HANDLE) {
+		if (unlikely(handle == QLA_TGT_SKIP_HANDLE)) {
+			DEBUG21(qla_printk(KERN_INFO, ha, "%s", "SKIP_HANDLE CTIO\n"));
+			return NULL;
+		}
+		/* handle-1 is actually used */
+		if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) {
+			printk(KERN_ERR "qla_target(%d): Wrong handle %x "
+				"received\n", vha->vp_idx, handle);
+			return NULL;
+		}
+		cmd = qla_tgt_get_cmd(vha, handle);
+		if (unlikely(cmd == NULL)) {
+			printk(KERN_WARNING "qla_target(%d): Suspicious: unable to "
+				   "find the command with handle %x\n",
+				   vha->vp_idx, handle);
+			return NULL;
+		}
+	} else if (ctio != NULL) {
+		struct qla_tgt_sess *sess;
+		int tag;
+		uint16_t loop_id;
+
+		if (IS_FWI2_CAPABLE(ha)) {
+			/* We can't get loop ID from CTIO7 */
+			printk(KERN_ERR "qla_target(%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 "qla_target(%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 qla_tgt_do_ctio_completion(struct scsi_qla_host *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 qla_tgt_cmd *cmd;
+
+	DEBUG23(qla_printk(KERN_INFO, ha, "qla_target(%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) {
+			DEBUG22(qla_printk(KERN_INFO, ha, "Intermediate CTIO received"
+				" (status %x)\n", status));
+		}
+		return;
+	}
+
+	cmd = qla_tgt_ctio_to_cmd(vha, handle, ctio);
+	if (cmd == NULL) {
+		if (status != CTIO_SUCCESS)
+			qla_tgt_term_ctio_exchange(vha, ctio, NULL, status);
+		return;
+	}
+	se_cmd = &cmd->se_cmd;
+	tfo = se_cmd->se_tfo;
+
+	if (cmd->sg_mapped)
+		qla_tgt_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 "qla_target(%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 "qla_target(%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 "qla_target(%d): CTIO with SRR_RECEIVED"
+				" status %x received (state %x, se_cmd %p)\n",
+				vha->vp_idx, status, cmd->state, se_cmd);
+			if (qla_tgt_prepare_srr_ctio(vha, cmd, ctio) != 0)
+				break;
+			else
+				return;
+
+		default:
+			printk(KERN_ERR "qla_target(%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 != QLA_TGT_STATE_NEED_DATA)
+			if (qla_tgt_term_ctio_exchange(vha, ctio, cmd, status))
+				return;
+	}
+
+	if (cmd->state == QLA_TGT_STATE_PROCESSED) {
+		DEBUG21(qla_printk(KERN_INFO, ha, "Command %p finished\n", cmd));
+	} else if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
+		int rx_status = 0;
+
+		cmd->state = QLA_TGT_STATE_DATA_IN;
+
+		if (unlikely(status != CTIO_SUCCESS))
+			rx_status = -EIO;
+		else
+			cmd->write_data_transferred = 1;
+
+		DEBUG21(qla_printk(KERN_INFO, ha, "Data received, context %x,"
+				" rx_status %d\n", 0x0, rx_status));
+
+		ha->qla2x_tmpl->handle_data(cmd);
+		return;
+	} else if (cmd->state == QLA_TGT_STATE_ABORTED) {
+		DEBUG22(qla_printk(KERN_INFO, ha, "Aborted command %p (tag %d) finished\n",
+				cmd, cmd->tag));
+	} else {
+		printk(KERN_ERR "qla_target(%d): A command in state (%d) should "
+			"not return a CTIO complete\n", vha->vp_idx, cmd->state);
+	}
+
+	if (unlikely(status != CTIO_SUCCESS)) {
+		DEBUG22(qla_printk(KERN_INFO, ha, "%s", "Finishing failed CTIO\n"));
+		dump_stack();
+	}
+
+	ha->qla2x_tmpl->free_cmd(cmd);
+}
+
[… more lines in the raw message]

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

drivers/scsi/qla2xxx/qla_target.h new file +1137 -0

1,137 lines not on this page · raw message

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

Signature · 1 line
1.7.4.3

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

#2 lore raw

RFC v3 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 qla_target.c logic introduced in commit f86d9fc734. This includes:

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

*) Addition of struct qla_target_template and direct calls into qla_target.c logic w/ qla_tgt_* prefixed functions.

*) Addition of qla_iocb:qla2x00_req_pkt() for ring processing, and qla2x00_issue_marker() for handling request/response queue processing for target mode operation

*) 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() -> qla_tgt_fc_port_deleted() qla_init.c:qla2x00_reg_remote_port() -> qla_tgt_fc_port_added() qla_init.c:qla2x00_device_resync() -> qla2x00_mark_device_lost()

*) I/O path:

qla_isr.c:qla2x00_async_event() -> qla_tgt_async_event() qla_isr.c:qla2x00_process_response_queue() -> qla_tgt_response_pkt_all_vps() qla_isr.c:qla24xx_process_response_queue() -> qla_tgt_response_pkt_all_vps()

---
 drivers/scsi/qla2xxx/Makefile   |    4 +-
 drivers/scsi/qla2xxx/qla_attr.c |    5 +-
 drivers/scsi/qla2xxx/qla_dbg.h  |   34 +++++++++++
 drivers/scsi/qla2xxx/qla_def.h  |   63 +++++++++++++++++++-
 drivers/scsi/qla2xxx/qla_gbl.h  |    7 ++
 drivers/scsi/qla2xxx/qla_gs.c   |    4 +-
 drivers/scsi/qla2xxx/qla_init.c |   79 ++++++++++++++++++++++---
 drivers/scsi/qla2xxx/qla_iocb.c |  105 ++++++++++++++++++++++++++++++++-
 drivers/scsi/qla2xxx/qla_isr.c  |   83 ++++++++++++++++++++++++++-
 drivers/scsi/qla2xxx/qla_mbx.c  |  122 +++++++++++++++++++++++++++++++++++++--
 drivers/scsi/qla2xxx/qla_mid.c  |   21 ++++++-
 drivers/scsi/qla2xxx/qla_os.c   |  107 +++++++++++++++++++++++++++++-----
 12 files changed, 595 insertions(+), 39 deletions(-)
drivers/scsi/qla2xxx/Makefile +3 -1
index 5df782f..4861054 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 qla_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 +4 -1
index d3e58d7..5318478 100644
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.h"
 
 #include <linux/kthread.h>
 #include <linux/vmalloc.h>
@@ -1816,6 +1817,7 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
 	fc_host_supported_speeds(vha->host) =
 		fc_host_supported_speeds(base_vha->host);
 
+	qla_tgt_vport_create(vha, ha);
 	qla24xx_vport_disable(fc_vport, disable);
 
 	if (ha->flags.cpu_affinity_enabled) {
@@ -2020,7 +2022,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_dbg.h +34 -0
index b74e6b5..b5b2f95 100644
@@ -28,6 +28,11 @@
 /* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */
 /* #define QL_DEBUG_LEVEL_17 */ /* Output EEH trace messages */
 /* #define QL_DEBUG_LEVEL_18 */ /* Output T10 CRC trace messages */
+/* #define QL_DEBUG_LEVEL_21 */ /* Output for target */
+/* #define QL_DEBUG_LEVEL_22 */ /* Output for target management */
+/* #define QL_DEBUG_LEVEL_23 */ /* Output for target scsi packets */
+/* #define QL_DEBUG_LEVEL_24 */ /* Output for target SG lists */
+/* #define QL_DEBUG_LEVEL_25 */ /* Output for target task management */
 
 /*
 * Macros use for debugging the driver.
@@ -146,6 +151,35 @@
 #define DEBUG18(x)	do {} while (0)
 #endif
 
+#if defined(QL_DEBUG_LEVEL_21)
+#define DEBUG21(x)	do {x;} while (0)
+#else
+#define DEBUG21(x)	do {} while (0)
+#endif
+
+#if defined(QL_DEBUG_LEVEL_22)
+#define DEBUG22(x)	do {x;} while (0)
+#else
+#define DEBUG22(x)	do {} while (0)
+#endif
+
+#if defined(QL_DEBUG_LEVEL_23)
+#define DEBUG23(x)	do {x;} while (0)
+#else
+#define DEBUG23(x)	do {} while (0)
+#endif
+
+#if defined(QL_DEBUG_LEVEL_24)
+#define DEBUG24(x)	do {x;} while (0)
+#else
+#define DEBUG24(x)	do {} while (0)
+#endif
+
+#if defined(QL_DEBUG_LEVEL_25)
+#define DEBUG25(x)	do {x;} while (0)
+#else
+#define DEBUG25(x)	do {} while (0)
+#endif
 
 /*
  * Firmware Dump structure definition
drivers/scsi/qla2xxx/qla_def.h +60 -3

109 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_gbl.h +7 -0
index d48326e..6c0a9e8 100644
@@ -172,6 +172,7 @@ extern int  qla2x00_vp_abort_isp(scsi_qla_host_t *);
 /*
  * Global Function Prototypes in qla_iocb.c source file.
  */
+
 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);
@@ -185,6 +186,9 @@ extern uint16_t qla24xx_calc_iocbs(uint16_t);
 extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t);
 extern int qla24xx_dif_start_scsi(srb_t *);
 
+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);
 
 /*
  * Global Function Prototypes in qla_mbx.c source file.
@@ -237,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 +3 -1
index 74a91b6..e5f33ad 100644
@@ -5,6 +5,7 @@
  * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_target.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 *);
@@ -548,7 +549,8 @@ qla2x00_rff_id(scsi_qla_host_t *vha)
 	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;
 
-	ct_req->req.rff_id.fc4_feature = BIT_1;
+	qla_tgt_rff_id(vha, ct_req);
+
 	ct_req->req.rff_id.fc4_type = 0x08;		/* SCSI - FCP */
 
 	/* Execute MS IOCB */
drivers/scsi/qla2xxx/qla_init.c +71 -8

203 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_iocb.c +102 -3

141 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_isr.c +82 -1

186 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_mbx.c +116 -6

170 lines not on this page · raw message

drivers/scsi/qla2xxx/qla_mid.c +20 -1
index 2b69392..b91a50a 100644
@@ -6,6 +6,7 @@
  */
 #include "qla_def.h"
 #include "qla_gbl.h"
+#include "qla_target.h"
 
 #include <linux/moduleparam.h>
 #include <linux/vmalloc.h>
@@ -48,6 +49,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 +80,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 +146,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 +273,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 +297,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)) {
+		DEBUG15(printk("qla2x00_vp_abort_isp() calling qla2x00_send_enable_lun()\n"));
+		qla2x00_send_enable_lun(vha, true);
+	}
+
+	return 0;
 }
 
 static int
drivers/scsi/qla2xxx/qla_os.c +93 -14

250 lines not on this page · raw message

Signature · 1 line
1.7.4.3
#3 lore raw

Re: [RFC-v3 2/3] qla2xxx: Enable 2xxx series LLD target mode support

Quoted 28 lines · Nicholas A. Bellinger, on 04/05/2011 09:11 AM wrote:

From: Nicholas Bellinger <[email protected]>

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

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

*) Addition of struct qla_target_template and direct calls into qla_target.c logic w/ qla_tgt_* prefixed functions.

*) Addition of qla_iocb:qla2x00_req_pkt() for ring processing, and qla2x00_issue_marker() for handling request/response queue processing for target mode operation

*) 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() -> qla_tgt_fc_port_deleted() qla_init.c:qla2x00_reg_remote_port() -> qla_tgt_fc_port_added() qla_init.c:qla2x00_device_resync() -> qla2x00_mark_device_lost()

Quoted 5 lines

*) I/O path:

qla_isr.c:qla2x00_async_event() -> qla_tgt_async_event() qla_isr.c:qla2x00_process_response_queue() -> qla_tgt_response_pkt_all_vps() qla_isr.c:qla24xx_process_response_queue() -> qla_tgt_response_pkt_all_vps()

Most of this code was written by me and other SCST developers. How about to give proper credits to the original authors?

Vlad

#4 lore raw

Re: [RFC-v3 2/3] qla2xxx: Enable 2xxx series LLD target mode support

Quoted 29 lines · Vladislav Bolkhovitin
Quoted 28 lines · Nicholas A. Bellinger, on 04/05/2011 09:11 AM wrote:

From: Nicholas Bellinger <[email protected]>

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

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

*) Addition of struct qla_target_template and direct calls into qla_target.c logic w/ qla_tgt_* prefixed functions.

*) Addition of qla_iocb:qla2x00_req_pkt() for ring processing, and qla2x00_issue_marker() for handling request/response queue processing for target mode operation

*) 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() -> qla_tgt_fc_port_deleted() qla_init.c:qla2x00_reg_remote_port() -> qla_tgt_fc_port_added() qla_init.c:qla2x00_device_resync() -> qla2x00_mark_device_lost()

Quoted 9 lines
Quoted 5 lines

*) I/O path:

qla_isr.c:qla2x00_async_event() -> qla_tgt_async_event() qla_isr.c:qla2x00_process_response_queue() -> qla_tgt_response_pkt_all_vps() qla_isr.c:qla24xx_process_response_queue() -> qla_tgt_response_pkt_all_vps()

Most of this code was written by me and other SCST developers. How about to give proper credits to the original authors?

Hello Vlad,

I intend to give proper fabric module credit where fabric module credit is due The original copyright from yourself + SCST developers is in place in this patch, as well as my own copyright for the modern LLD refactoring, forward-port and cleanups to work with mainline target infrastructure.

I have no issues with including your's and other original author's names for the pioneering work into the commit log for the next round of review of PATCH #2, and am happy to make sure this is included for a future squashed version of the series for a mainline commit.

--nab

#5 lore raw

RFC v3 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 qla_target.c LLD logic introduced into commit f86d9fc73 and commit 014a230ea. 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 qla_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 qla_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/Kconfig                            |    1 +
 drivers/target/Makefile                           |    1 +
 drivers/target/tcm_qla2xxx/Kconfig                |    7 +
 drivers/target/tcm_qla2xxx/Makefile               |    6 +
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h     |  102 ++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_configfs.c | 1439 +++++++++++++++++++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.c   |  853 ++++++++++++
 drivers/target/tcm_qla2xxx/tcm_qla2xxx_fabric.h   |   53 +
 8 files changed, 2462 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/tcm_qla2xxx/Kconfig
 create mode 100644 drivers/target/tcm_qla2xxx/Makefile
 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/Kconfig +1 -0
index 9ef2dbb..b419bcf 100644
@@ -30,5 +30,6 @@ config TCM_PSCSI
 	passthrough access to Linux/SCSI device
 
 source "drivers/target/loopback/Kconfig"
+source "drivers/target/tcm_qla2xxx/Kconfig"
 
 endif
drivers/target/Makefile +1 -0
index 1178bbf..6310a7a 100644
@@ -24,3 +24,4 @@ obj-$(CONFIG_TCM_PSCSI)		+= target_core_pscsi.o
 
 # Fabric modules
 obj-$(CONFIG_LOOPBACK_TARGET)	+= loopback/
+obj-$(CONFIG_TCM_QLA2XXX)	+= tcm_qla2xxx/
drivers/target/tcm_qla2xxx/Kconfig new file +7 -0
new file mode 100644
index 0000000..da69255
@@ -0,0 +1,7 @@
+config TCM_QLA2XXX
+	tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs"
+	select LIBFC
+	select SCSI_QLA_FC
+	default n
+	---help---
+	Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs
drivers/target/tcm_qla2xxx/Makefile new file +6 -0
new file mode 100644
index 0000000..be2ba13
@@ -0,0 +1,6 @@
+EXTRA_CFLAGS += -I$(srctree)/drivers/scsi/qla2xxx/
+
+tcm_qla2xxx-objs		:= tcm_qla2xxx_fabric.o \
+				   tcm_qla2xxx_configfs.o \
+
+obj-$(CONFIG_TCM_QLA2XXX)	+= tcm_qla2xxx.o
drivers/target/tcm_qla2xxx/tcm_qla2xxx_base.h new file +102 -0

102 lines not on this page · raw message

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

1,439 lines not on this page · raw message

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

853 lines not on this page · raw message

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

53 lines not on this page · raw message

Signature · 6 lines
1.7.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html