162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  qla_target.c SCSI LLD infrastructure for QLogic 22xx/23xx/24xx/25xx
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  based on qla2x00t.c code:
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
862306a36Sopenharmony_ci *  Copyright (C) 2004 - 2005 Leonid Stoljar
962306a36Sopenharmony_ci *  Copyright (C) 2006 Nathaniel Clark <nate@misrule.us>
1062306a36Sopenharmony_ci *  Copyright (C) 2006 - 2010 ID7 Ltd.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  Forward port and refactoring to modern qla2xxx and target/configfs
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *  Copyright (C) 2010-2013 Nicholas A. Bellinger <nab@kernel.org>
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/types.h>
2062306a36Sopenharmony_ci#include <linux/blkdev.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci#include <linux/pci.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/list.h>
2562306a36Sopenharmony_ci#include <linux/workqueue.h>
2662306a36Sopenharmony_ci#include <asm/unaligned.h>
2762306a36Sopenharmony_ci#include <scsi/scsi.h>
2862306a36Sopenharmony_ci#include <scsi/scsi_host.h>
2962306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "qla_def.h"
3262306a36Sopenharmony_ci#include "qla_target.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int ql2xtgt_tape_enable;
3562306a36Sopenharmony_cimodule_param(ql2xtgt_tape_enable, int, S_IRUGO|S_IWUSR);
3662306a36Sopenharmony_ciMODULE_PARM_DESC(ql2xtgt_tape_enable,
3762306a36Sopenharmony_ci		"Enables Sequence level error recovery (aka FC Tape). Default is 0 - no SLER. 1 - Enable SLER.");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic char *qlini_mode = QLA2XXX_INI_MODE_STR_ENABLED;
4062306a36Sopenharmony_cimodule_param(qlini_mode, charp, S_IRUGO);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(qlini_mode,
4262306a36Sopenharmony_ci	"Determines when initiator mode will be enabled. Possible values: "
4362306a36Sopenharmony_ci	"\"exclusive\" - initiator mode will be enabled on load, "
4462306a36Sopenharmony_ci	"disabled on enabling target mode and then on disabling target mode "
4562306a36Sopenharmony_ci	"enabled back; "
4662306a36Sopenharmony_ci	"\"disabled\" - initiator mode will never be enabled; "
4762306a36Sopenharmony_ci	"\"dual\" - Initiator Modes will be enabled. Target Mode can be activated "
4862306a36Sopenharmony_ci	"when ready "
4962306a36Sopenharmony_ci	"\"enabled\" (default) - initiator mode will always stay enabled.");
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint ql2xuctrlirq = 1;
5262306a36Sopenharmony_cimodule_param(ql2xuctrlirq, int, 0644);
5362306a36Sopenharmony_ciMODULE_PARM_DESC(ql2xuctrlirq,
5462306a36Sopenharmony_ci    "User to control IRQ placement via smp_affinity."
5562306a36Sopenharmony_ci    "Valid with qlini_mode=disabled."
5662306a36Sopenharmony_ci    "1(default): enable");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciint ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int qla_sam_status = SAM_STAT_BUSY;
6162306a36Sopenharmony_cistatic int tc_sam_status = SAM_STAT_TASK_SET_FULL; /* target core */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * From scsi/fc/fc_fcp.h
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cienum fcp_resp_rsp_codes {
6762306a36Sopenharmony_ci	FCP_TMF_CMPL = 0,
6862306a36Sopenharmony_ci	FCP_DATA_LEN_INVALID = 1,
6962306a36Sopenharmony_ci	FCP_CMND_FIELDS_INVALID = 2,
7062306a36Sopenharmony_ci	FCP_DATA_PARAM_MISMATCH = 3,
7162306a36Sopenharmony_ci	FCP_TMF_REJECTED = 4,
7262306a36Sopenharmony_ci	FCP_TMF_FAILED = 5,
7362306a36Sopenharmony_ci	FCP_TMF_INVALID_LUN = 9,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * fc_pri_ta from scsi/fc/fc_fcp.h
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_ci#define FCP_PTA_SIMPLE      0   /* simple task attribute */
8062306a36Sopenharmony_ci#define FCP_PTA_HEADQ       1   /* head of queue task attribute */
8162306a36Sopenharmony_ci#define FCP_PTA_ORDERED     2   /* ordered task attribute */
8262306a36Sopenharmony_ci#define FCP_PTA_ACA         4   /* auto. contingent allegiance */
8362306a36Sopenharmony_ci#define FCP_PTA_MASK        7   /* mask for task attribute field */
8462306a36Sopenharmony_ci#define FCP_PRI_SHIFT       3   /* priority field starts in bit 3 */
8562306a36Sopenharmony_ci#define FCP_PRI_RESVD_MASK  0x80        /* reserved bits in priority field */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * This driver calls qla2x00_alloc_iocbs() and qla2x00_issue_marker(), which
8962306a36Sopenharmony_ci * must be called under HW lock and could unlock/lock it inside.
9062306a36Sopenharmony_ci * It isn't an issue, since in the current implementation on the time when
9162306a36Sopenharmony_ci * those functions are called:
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci *   - Either context is IRQ and only IRQ handler can modify HW data,
9462306a36Sopenharmony_ci *     including rings related fields,
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci *   - Or access to target mode variables from struct qla_tgt doesn't
9762306a36Sopenharmony_ci *     cross those functions boundaries, except tgt_stop, which
9862306a36Sopenharmony_ci *     additionally protected by irq_cmd_count.
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_ci/* Predefs for callbacks handed to qla2xxx LLD */
10162306a36Sopenharmony_cistatic void qlt_24xx_atio_pkt(struct scsi_qla_host *ha,
10262306a36Sopenharmony_ci	struct atio_from_isp *pkt, uint8_t);
10362306a36Sopenharmony_cistatic void qlt_response_pkt(struct scsi_qla_host *ha, struct rsp_que *rsp,
10462306a36Sopenharmony_ci	response_t *pkt);
10562306a36Sopenharmony_cistatic int qlt_issue_task_mgmt(struct fc_port *sess, u64 lun,
10662306a36Sopenharmony_ci	int fn, void *iocb, int flags);
10762306a36Sopenharmony_cistatic void qlt_send_term_exchange(struct qla_qpair *, struct qla_tgt_cmd
10862306a36Sopenharmony_ci	*cmd, struct atio_from_isp *atio, int ha_locked, int ul_abort);
10962306a36Sopenharmony_cistatic void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha,
11062306a36Sopenharmony_ci	struct atio_from_isp *atio, uint16_t status, int qfull);
11162306a36Sopenharmony_cistatic void qlt_disable_vha(struct scsi_qla_host *vha);
11262306a36Sopenharmony_cistatic void qlt_clear_tgt_db(struct qla_tgt *tgt);
11362306a36Sopenharmony_cistatic void qlt_send_notify_ack(struct qla_qpair *qpair,
11462306a36Sopenharmony_ci	struct imm_ntfy_from_isp *ntfy,
11562306a36Sopenharmony_ci	uint32_t add_flags, uint16_t resp_code, int resp_code_valid,
11662306a36Sopenharmony_ci	uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan);
11762306a36Sopenharmony_cistatic void qlt_send_term_imm_notif(struct scsi_qla_host *vha,
11862306a36Sopenharmony_ci	struct imm_ntfy_from_isp *imm, int ha_locked);
11962306a36Sopenharmony_cistatic struct fc_port *qlt_create_sess(struct scsi_qla_host *vha,
12062306a36Sopenharmony_ci	fc_port_t *fcport, bool local);
12162306a36Sopenharmony_civoid qlt_unreg_sess(struct fc_port *sess);
12262306a36Sopenharmony_cistatic void qlt_24xx_handle_abts(struct scsi_qla_host *,
12362306a36Sopenharmony_ci	struct abts_recv_from_24xx *);
12462306a36Sopenharmony_cistatic void qlt_send_busy(struct qla_qpair *, struct atio_from_isp *,
12562306a36Sopenharmony_ci    uint16_t);
12662306a36Sopenharmony_cistatic int qlt_check_reserve_free_req(struct qla_qpair *qpair, uint32_t);
12762306a36Sopenharmony_cistatic inline uint32_t qlt_make_handle(struct qla_qpair *);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Global Variables
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistatic struct kmem_cache *qla_tgt_mgmt_cmd_cachep;
13362306a36Sopenharmony_cistruct kmem_cache *qla_tgt_plogi_cachep;
13462306a36Sopenharmony_cistatic mempool_t *qla_tgt_mgmt_cmd_mempool;
13562306a36Sopenharmony_cistatic struct workqueue_struct *qla_tgt_wq;
13662306a36Sopenharmony_cistatic DEFINE_MUTEX(qla_tgt_mutex);
13762306a36Sopenharmony_cistatic LIST_HEAD(qla_tgt_glist);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic const char *prot_op_str(u32 prot_op)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	switch (prot_op) {
14262306a36Sopenharmony_ci	case TARGET_PROT_NORMAL:	return "NORMAL";
14362306a36Sopenharmony_ci	case TARGET_PROT_DIN_INSERT:	return "DIN_INSERT";
14462306a36Sopenharmony_ci	case TARGET_PROT_DOUT_INSERT:	return "DOUT_INSERT";
14562306a36Sopenharmony_ci	case TARGET_PROT_DIN_STRIP:	return "DIN_STRIP";
14662306a36Sopenharmony_ci	case TARGET_PROT_DOUT_STRIP:	return "DOUT_STRIP";
14762306a36Sopenharmony_ci	case TARGET_PROT_DIN_PASS:	return "DIN_PASS";
14862306a36Sopenharmony_ci	case TARGET_PROT_DOUT_PASS:	return "DOUT_PASS";
14962306a36Sopenharmony_ci	default:			return "UNKNOWN";
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* This API intentionally takes dest as a parameter, rather than returning
15462306a36Sopenharmony_ci * int value to avoid caller forgetting to issue wmb() after the store */
15562306a36Sopenharmony_civoid qlt_do_generation_tick(struct scsi_qla_host *vha, int *dest)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev);
15862306a36Sopenharmony_ci	*dest = atomic_inc_return(&base_vha->generation_tick);
15962306a36Sopenharmony_ci	/* memory barrier */
16062306a36Sopenharmony_ci	wmb();
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* Might release hw lock, then reaquire!! */
16462306a36Sopenharmony_cistatic inline int qlt_issue_marker(struct scsi_qla_host *vha, int vha_locked)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	/* Send marker if required */
16762306a36Sopenharmony_ci	if (unlikely(vha->marker_needed != 0)) {
16862306a36Sopenharmony_ci		int rc = qla2x00_issue_marker(vha, vha_locked);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		if (rc != QLA_SUCCESS) {
17162306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe03d,
17262306a36Sopenharmony_ci			    "qla_target(%d): issue_marker() failed\n",
17362306a36Sopenharmony_ci			    vha->vp_idx);
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci		return rc;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	return QLA_SUCCESS;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistruct scsi_qla_host *qla_find_host_by_d_id(struct scsi_qla_host *vha,
18162306a36Sopenharmony_ci					    be_id_t d_id)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct scsi_qla_host *host;
18462306a36Sopenharmony_ci	uint32_t key;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (vha->d_id.b.area == d_id.area &&
18762306a36Sopenharmony_ci	    vha->d_id.b.domain == d_id.domain &&
18862306a36Sopenharmony_ci	    vha->d_id.b.al_pa == d_id.al_pa)
18962306a36Sopenharmony_ci		return vha;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	key = be_to_port_id(d_id).b24;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	host = btree_lookup32(&vha->hw->host_map, key);
19462306a36Sopenharmony_ci	if (!host)
19562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt + ql_dbg_verbose, vha, 0xf005,
19662306a36Sopenharmony_ci		    "Unable to find host %06x\n", key);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return host;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic inline void qlt_incr_num_pend_cmds(struct scsi_qla_host *vha)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	unsigned long flags;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.q_full_lock, flags);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	vha->hw->tgt.num_pend_cmds++;
20862306a36Sopenharmony_ci	if (vha->hw->tgt.num_pend_cmds > vha->qla_stats.stat_max_pend_cmds)
20962306a36Sopenharmony_ci		vha->qla_stats.stat_max_pend_cmds =
21062306a36Sopenharmony_ci			vha->hw->tgt.num_pend_cmds;
21162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_cistatic inline void qlt_decr_num_pend_cmds(struct scsi_qla_host *vha)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	unsigned long flags;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.q_full_lock, flags);
21862306a36Sopenharmony_ci	vha->hw->tgt.num_pend_cmds--;
21962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void qlt_queue_unknown_atio(scsi_qla_host_t *vha,
22462306a36Sopenharmony_ci	struct atio_from_isp *atio, uint8_t ha_locked)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct qla_tgt_sess_op *u;
22762306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
22862306a36Sopenharmony_ci	unsigned long flags;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (tgt->tgt_stop) {
23162306a36Sopenharmony_ci		ql_dbg(ql_dbg_async, vha, 0x502c,
23262306a36Sopenharmony_ci		    "qla_target(%d): dropping unknown ATIO_TYPE7, because tgt is being stopped",
23362306a36Sopenharmony_ci		    vha->vp_idx);
23462306a36Sopenharmony_ci		goto out_term;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	u = kzalloc(sizeof(*u), GFP_ATOMIC);
23862306a36Sopenharmony_ci	if (u == NULL)
23962306a36Sopenharmony_ci		goto out_term;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	u->vha = vha;
24262306a36Sopenharmony_ci	memcpy(&u->atio, atio, sizeof(*atio));
24362306a36Sopenharmony_ci	INIT_LIST_HEAD(&u->cmd_list);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	spin_lock_irqsave(&vha->cmd_list_lock, flags);
24662306a36Sopenharmony_ci	list_add_tail(&u->cmd_list, &vha->unknown_atio_list);
24762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	schedule_delayed_work(&vha->unknown_atio_work, 1);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ciout:
25262306a36Sopenharmony_ci	return;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ciout_term:
25562306a36Sopenharmony_ci	qlt_send_term_exchange(vha->hw->base_qpair, NULL, atio, ha_locked, 0);
25662306a36Sopenharmony_ci	goto out;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void qlt_try_to_dequeue_unknown_atios(struct scsi_qla_host *vha,
26062306a36Sopenharmony_ci	uint8_t ha_locked)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct qla_tgt_sess_op *u, *t;
26362306a36Sopenharmony_ci	scsi_qla_host_t *host;
26462306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
26562306a36Sopenharmony_ci	unsigned long flags;
26662306a36Sopenharmony_ci	uint8_t queued = 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	list_for_each_entry_safe(u, t, &vha->unknown_atio_list, cmd_list) {
26962306a36Sopenharmony_ci		if (u->aborted) {
27062306a36Sopenharmony_ci			ql_dbg(ql_dbg_async, vha, 0x502e,
27162306a36Sopenharmony_ci			    "Freeing unknown %s %p, because of Abort\n",
27262306a36Sopenharmony_ci			    "ATIO_TYPE7", u);
27362306a36Sopenharmony_ci			qlt_send_term_exchange(vha->hw->base_qpair, NULL,
27462306a36Sopenharmony_ci			    &u->atio, ha_locked, 0);
27562306a36Sopenharmony_ci			goto abort;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		host = qla_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id);
27962306a36Sopenharmony_ci		if (host != NULL) {
28062306a36Sopenharmony_ci			ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x502f,
28162306a36Sopenharmony_ci			    "Requeuing unknown ATIO_TYPE7 %p\n", u);
28262306a36Sopenharmony_ci			qlt_24xx_atio_pkt(host, &u->atio, ha_locked);
28362306a36Sopenharmony_ci		} else if (tgt->tgt_stop) {
28462306a36Sopenharmony_ci			ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x503a,
28562306a36Sopenharmony_ci			    "Freeing unknown %s %p, because tgt is being stopped\n",
28662306a36Sopenharmony_ci			    "ATIO_TYPE7", u);
28762306a36Sopenharmony_ci			qlt_send_term_exchange(vha->hw->base_qpair, NULL,
28862306a36Sopenharmony_ci			    &u->atio, ha_locked, 0);
28962306a36Sopenharmony_ci		} else {
29062306a36Sopenharmony_ci			ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x503d,
29162306a36Sopenharmony_ci			    "Reschedule u %p, vha %p, host %p\n", u, vha, host);
29262306a36Sopenharmony_ci			if (!queued) {
29362306a36Sopenharmony_ci				queued = 1;
29462306a36Sopenharmony_ci				schedule_delayed_work(&vha->unknown_atio_work,
29562306a36Sopenharmony_ci				    1);
29662306a36Sopenharmony_ci			}
29762306a36Sopenharmony_ci			continue;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ciabort:
30162306a36Sopenharmony_ci		spin_lock_irqsave(&vha->cmd_list_lock, flags);
30262306a36Sopenharmony_ci		list_del(&u->cmd_list);
30362306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
30462306a36Sopenharmony_ci		kfree(u);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_civoid qlt_unknown_atio_work_fn(struct work_struct *work)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct scsi_qla_host *vha = container_of(to_delayed_work(work),
31162306a36Sopenharmony_ci	    struct scsi_qla_host, unknown_atio_work);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	qlt_try_to_dequeue_unknown_atios(vha, 0);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic bool qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha,
31762306a36Sopenharmony_ci	struct atio_from_isp *atio, uint8_t ha_locked)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe072,
32062306a36Sopenharmony_ci		"%s: qla_target(%d): type %x ox_id %04x\n",
32162306a36Sopenharmony_ci		__func__, vha->vp_idx, atio->u.raw.entry_type,
32262306a36Sopenharmony_ci		be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id));
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	switch (atio->u.raw.entry_type) {
32562306a36Sopenharmony_ci	case ATIO_TYPE7:
32662306a36Sopenharmony_ci	{
32762306a36Sopenharmony_ci		struct scsi_qla_host *host = qla_find_host_by_d_id(vha,
32862306a36Sopenharmony_ci		    atio->u.isp24.fcp_hdr.d_id);
32962306a36Sopenharmony_ci		if (unlikely(NULL == host)) {
33062306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe03e,
33162306a36Sopenharmony_ci			    "qla_target(%d): Received ATIO_TYPE7 "
33262306a36Sopenharmony_ci			    "with unknown d_id %x:%x:%x\n", vha->vp_idx,
33362306a36Sopenharmony_ci			    atio->u.isp24.fcp_hdr.d_id.domain,
33462306a36Sopenharmony_ci			    atio->u.isp24.fcp_hdr.d_id.area,
33562306a36Sopenharmony_ci			    atio->u.isp24.fcp_hdr.d_id.al_pa);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci			qlt_queue_unknown_atio(vha, atio, ha_locked);
33962306a36Sopenharmony_ci			break;
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci		if (unlikely(!list_empty(&vha->unknown_atio_list)))
34262306a36Sopenharmony_ci			qlt_try_to_dequeue_unknown_atios(vha, ha_locked);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		qlt_24xx_atio_pkt(host, atio, ha_locked);
34562306a36Sopenharmony_ci		break;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	case IMMED_NOTIFY_TYPE:
34962306a36Sopenharmony_ci	{
35062306a36Sopenharmony_ci		struct scsi_qla_host *host = vha;
35162306a36Sopenharmony_ci		struct imm_ntfy_from_isp *entry =
35262306a36Sopenharmony_ci		    (struct imm_ntfy_from_isp *)atio;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		qlt_issue_marker(vha, ha_locked);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if ((entry->u.isp24.vp_index != 0xFF) &&
35762306a36Sopenharmony_ci		    (entry->u.isp24.nport_handle != cpu_to_le16(0xFFFF))) {
35862306a36Sopenharmony_ci			host = qla_find_host_by_vp_idx(vha,
35962306a36Sopenharmony_ci			    entry->u.isp24.vp_index);
36062306a36Sopenharmony_ci			if (unlikely(!host)) {
36162306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe03f,
36262306a36Sopenharmony_ci				    "qla_target(%d): Received "
36362306a36Sopenharmony_ci				    "ATIO (IMMED_NOTIFY_TYPE) "
36462306a36Sopenharmony_ci				    "with unknown vp_index %d\n",
36562306a36Sopenharmony_ci				    vha->vp_idx, entry->u.isp24.vp_index);
36662306a36Sopenharmony_ci				break;
36762306a36Sopenharmony_ci			}
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci		qlt_24xx_atio_pkt(host, atio, ha_locked);
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	case VP_RPT_ID_IOCB_TYPE:
37462306a36Sopenharmony_ci		qla24xx_report_id_acquisition(vha,
37562306a36Sopenharmony_ci			(struct vp_rpt_id_entry_24xx *)atio);
37662306a36Sopenharmony_ci		break;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	case ABTS_RECV_24XX:
37962306a36Sopenharmony_ci	{
38062306a36Sopenharmony_ci		struct abts_recv_from_24xx *entry =
38162306a36Sopenharmony_ci			(struct abts_recv_from_24xx *)atio;
38262306a36Sopenharmony_ci		struct scsi_qla_host *host = qla_find_host_by_vp_idx(vha,
38362306a36Sopenharmony_ci			entry->vp_index);
38462306a36Sopenharmony_ci		unsigned long flags;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (unlikely(!host)) {
38762306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe00a,
38862306a36Sopenharmony_ci			    "qla_target(%d): Response pkt (ABTS_RECV_24XX) "
38962306a36Sopenharmony_ci			    "received, with unknown vp_index %d\n",
39062306a36Sopenharmony_ci			    vha->vp_idx, entry->vp_index);
39162306a36Sopenharmony_ci			break;
39262306a36Sopenharmony_ci		}
39362306a36Sopenharmony_ci		if (!ha_locked)
39462306a36Sopenharmony_ci			spin_lock_irqsave(&host->hw->hardware_lock, flags);
39562306a36Sopenharmony_ci		qlt_24xx_handle_abts(host, (struct abts_recv_from_24xx *)atio);
39662306a36Sopenharmony_ci		if (!ha_locked)
39762306a36Sopenharmony_ci			spin_unlock_irqrestore(&host->hw->hardware_lock, flags);
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* case PUREX_IOCB_TYPE: ql2xmvasynctoatio */
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	default:
40462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe040,
40562306a36Sopenharmony_ci		    "qla_target(%d): Received unknown ATIO atio "
40662306a36Sopenharmony_ci		    "type %x\n", vha->vp_idx, atio->u.raw.entry_type);
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return false;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_civoid qlt_response_pkt_all_vps(struct scsi_qla_host *vha,
41462306a36Sopenharmony_ci	struct rsp_que *rsp, response_t *pkt)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	switch (pkt->entry_type) {
41762306a36Sopenharmony_ci	case CTIO_CRC2:
41862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe073,
41962306a36Sopenharmony_ci			"qla_target(%d):%s: CRC2 Response pkt\n",
42062306a36Sopenharmony_ci			vha->vp_idx, __func__);
42162306a36Sopenharmony_ci		fallthrough;
42262306a36Sopenharmony_ci	case CTIO_TYPE7:
42362306a36Sopenharmony_ci	{
42462306a36Sopenharmony_ci		struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt;
42562306a36Sopenharmony_ci		struct scsi_qla_host *host = qla_find_host_by_vp_idx(vha,
42662306a36Sopenharmony_ci		    entry->vp_index);
42762306a36Sopenharmony_ci		if (unlikely(!host)) {
42862306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe041,
42962306a36Sopenharmony_ci			    "qla_target(%d): Response pkt (CTIO_TYPE7) "
43062306a36Sopenharmony_ci			    "received, with unknown vp_index %d\n",
43162306a36Sopenharmony_ci			    vha->vp_idx, entry->vp_index);
43262306a36Sopenharmony_ci			break;
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci		qlt_response_pkt(host, rsp, pkt);
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	case IMMED_NOTIFY_TYPE:
43962306a36Sopenharmony_ci	{
44062306a36Sopenharmony_ci		struct scsi_qla_host *host;
44162306a36Sopenharmony_ci		struct imm_ntfy_from_isp *entry =
44262306a36Sopenharmony_ci		    (struct imm_ntfy_from_isp *)pkt;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		host = qla_find_host_by_vp_idx(vha, entry->u.isp24.vp_index);
44562306a36Sopenharmony_ci		if (unlikely(!host)) {
44662306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe042,
44762306a36Sopenharmony_ci			    "qla_target(%d): Response pkt (IMMED_NOTIFY_TYPE) "
44862306a36Sopenharmony_ci			    "received, with unknown vp_index %d\n",
44962306a36Sopenharmony_ci			    vha->vp_idx, entry->u.isp24.vp_index);
45062306a36Sopenharmony_ci			break;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci		qlt_response_pkt(host, rsp, pkt);
45362306a36Sopenharmony_ci		break;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	case NOTIFY_ACK_TYPE:
45762306a36Sopenharmony_ci	{
45862306a36Sopenharmony_ci		struct scsi_qla_host *host = vha;
45962306a36Sopenharmony_ci		struct nack_to_isp *entry = (struct nack_to_isp *)pkt;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		if (0xFF != entry->u.isp24.vp_index) {
46262306a36Sopenharmony_ci			host = qla_find_host_by_vp_idx(vha,
46362306a36Sopenharmony_ci			    entry->u.isp24.vp_index);
46462306a36Sopenharmony_ci			if (unlikely(!host)) {
46562306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe043,
46662306a36Sopenharmony_ci				    "qla_target(%d): Response "
46762306a36Sopenharmony_ci				    "pkt (NOTIFY_ACK_TYPE) "
46862306a36Sopenharmony_ci				    "received, with unknown "
46962306a36Sopenharmony_ci				    "vp_index %d\n", vha->vp_idx,
47062306a36Sopenharmony_ci				    entry->u.isp24.vp_index);
47162306a36Sopenharmony_ci				break;
47262306a36Sopenharmony_ci			}
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci		qlt_response_pkt(host, rsp, pkt);
47562306a36Sopenharmony_ci		break;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	case ABTS_RECV_24XX:
47962306a36Sopenharmony_ci	{
48062306a36Sopenharmony_ci		struct abts_recv_from_24xx *entry =
48162306a36Sopenharmony_ci		    (struct abts_recv_from_24xx *)pkt;
48262306a36Sopenharmony_ci		struct scsi_qla_host *host = qla_find_host_by_vp_idx(vha,
48362306a36Sopenharmony_ci		    entry->vp_index);
48462306a36Sopenharmony_ci		if (unlikely(!host)) {
48562306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe044,
48662306a36Sopenharmony_ci			    "qla_target(%d): Response pkt "
48762306a36Sopenharmony_ci			    "(ABTS_RECV_24XX) received, with unknown "
48862306a36Sopenharmony_ci			    "vp_index %d\n", vha->vp_idx, entry->vp_index);
48962306a36Sopenharmony_ci			break;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci		qlt_response_pkt(host, rsp, pkt);
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	case ABTS_RESP_24XX:
49662306a36Sopenharmony_ci	{
49762306a36Sopenharmony_ci		struct abts_resp_to_24xx *entry =
49862306a36Sopenharmony_ci		    (struct abts_resp_to_24xx *)pkt;
49962306a36Sopenharmony_ci		struct scsi_qla_host *host = qla_find_host_by_vp_idx(vha,
50062306a36Sopenharmony_ci		    entry->vp_index);
50162306a36Sopenharmony_ci		if (unlikely(!host)) {
50262306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe045,
50362306a36Sopenharmony_ci			    "qla_target(%d): Response pkt "
50462306a36Sopenharmony_ci			    "(ABTS_RECV_24XX) received, with unknown "
50562306a36Sopenharmony_ci			    "vp_index %d\n", vha->vp_idx, entry->vp_index);
50662306a36Sopenharmony_ci			break;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		qlt_response_pkt(host, rsp, pkt);
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci	default:
51262306a36Sopenharmony_ci		qlt_response_pkt(vha, rsp, pkt);
51362306a36Sopenharmony_ci		break;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci/*
51962306a36Sopenharmony_ci * All qlt_plogi_ack_t operations are protected by hardware_lock
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_cistatic int qla24xx_post_nack_work(struct scsi_qla_host *vha, fc_port_t *fcport,
52262306a36Sopenharmony_ci	struct imm_ntfy_from_isp *ntfy, int type)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct qla_work_evt *e;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	e = qla2x00_alloc_work(vha, QLA_EVT_NACK);
52762306a36Sopenharmony_ci	if (!e)
52862306a36Sopenharmony_ci		return QLA_FUNCTION_FAILED;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	e->u.nack.fcport = fcport;
53162306a36Sopenharmony_ci	e->u.nack.type = type;
53262306a36Sopenharmony_ci	memcpy(e->u.nack.iocb, ntfy, sizeof(struct imm_ntfy_from_isp));
53362306a36Sopenharmony_ci	return qla2x00_post_work(vha, e);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void qla2x00_async_nack_sp_done(srb_t *sp, int res)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct scsi_qla_host *vha = sp->vha;
53962306a36Sopenharmony_ci	unsigned long flags;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0x20f2,
54262306a36Sopenharmony_ci	    "Async done-%s res %x %8phC  type %d\n",
54362306a36Sopenharmony_ci	    sp->name, res, sp->fcport->port_name, sp->type);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
54662306a36Sopenharmony_ci	sp->fcport->flags &= ~FCF_ASYNC_SENT;
54762306a36Sopenharmony_ci	sp->fcport->chip_reset = vha->hw->base_qpair->chip_reset;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	switch (sp->type) {
55062306a36Sopenharmony_ci	case SRB_NACK_PLOGI:
55162306a36Sopenharmony_ci		sp->fcport->login_gen++;
55262306a36Sopenharmony_ci		sp->fcport->fw_login_state = DSC_LS_PLOGI_COMP;
55362306a36Sopenharmony_ci		sp->fcport->logout_on_delete = 1;
55462306a36Sopenharmony_ci		sp->fcport->plogi_nack_done_deadline = jiffies + HZ;
55562306a36Sopenharmony_ci		sp->fcport->send_els_logo = 0;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		if (sp->fcport->flags & FCF_FCSP_DEVICE) {
55862306a36Sopenharmony_ci			ql_dbg(ql_dbg_edif, vha, 0x20ef,
55962306a36Sopenharmony_ci			    "%s %8phC edif: PLOGI- AUTH WAIT\n", __func__,
56062306a36Sopenharmony_ci			    sp->fcport->port_name);
56162306a36Sopenharmony_ci			qla2x00_set_fcport_disc_state(sp->fcport,
56262306a36Sopenharmony_ci			    DSC_LOGIN_AUTH_PEND);
56362306a36Sopenharmony_ci			qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE,
56462306a36Sopenharmony_ci			    sp->fcport->d_id.b24);
56562306a36Sopenharmony_ci			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_NEEDED, sp->fcport->d_id.b24,
56662306a36Sopenharmony_ci			    0, sp->fcport);
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci		break;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	case SRB_NACK_PRLI:
57162306a36Sopenharmony_ci		sp->fcport->fw_login_state = DSC_LS_PRLI_COMP;
57262306a36Sopenharmony_ci		sp->fcport->deleted = 0;
57362306a36Sopenharmony_ci		sp->fcport->send_els_logo = 0;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (!sp->fcport->login_succ &&
57662306a36Sopenharmony_ci		    !IS_SW_RESV_ADDR(sp->fcport->d_id)) {
57762306a36Sopenharmony_ci			sp->fcport->login_succ = 1;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci			vha->fcport_count++;
58062306a36Sopenharmony_ci			spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
58162306a36Sopenharmony_ci			qla24xx_sched_upd_fcport(sp->fcport);
58262306a36Sopenharmony_ci			spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
58362306a36Sopenharmony_ci		} else {
58462306a36Sopenharmony_ci			sp->fcport->login_retry = 0;
58562306a36Sopenharmony_ci			qla2x00_set_fcport_disc_state(sp->fcport,
58662306a36Sopenharmony_ci			    DSC_LOGIN_COMPLETE);
58762306a36Sopenharmony_ci			sp->fcport->deleted = 0;
58862306a36Sopenharmony_ci			sp->fcport->logout_on_delete = 1;
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	case SRB_NACK_LOGO:
59362306a36Sopenharmony_ci		sp->fcport->login_gen++;
59462306a36Sopenharmony_ci		sp->fcport->fw_login_state = DSC_LS_PORT_UNAVAIL;
59562306a36Sopenharmony_ci		qlt_logo_completion_handler(sp->fcport, MBS_COMMAND_COMPLETE);
59662306a36Sopenharmony_ci		break;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	kref_put(&sp->cmd_kref, qla2x00_sp_release);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ciint qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport,
60462306a36Sopenharmony_ci	struct imm_ntfy_from_isp *ntfy, int type)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	int rval = QLA_FUNCTION_FAILED;
60762306a36Sopenharmony_ci	srb_t *sp;
60862306a36Sopenharmony_ci	char *c = NULL;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	fcport->flags |= FCF_ASYNC_SENT;
61162306a36Sopenharmony_ci	switch (type) {
61262306a36Sopenharmony_ci	case SRB_NACK_PLOGI:
61362306a36Sopenharmony_ci		fcport->fw_login_state = DSC_LS_PLOGI_PEND;
61462306a36Sopenharmony_ci		c = "PLOGI";
61562306a36Sopenharmony_ci		if (vha->hw->flags.edif_enabled &&
61662306a36Sopenharmony_ci		    (le16_to_cpu(ntfy->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP))
61762306a36Sopenharmony_ci			fcport->flags |= FCF_FCSP_DEVICE;
61862306a36Sopenharmony_ci		break;
61962306a36Sopenharmony_ci	case SRB_NACK_PRLI:
62062306a36Sopenharmony_ci		fcport->fw_login_state = DSC_LS_PRLI_PEND;
62162306a36Sopenharmony_ci		fcport->deleted = 0;
62262306a36Sopenharmony_ci		c = "PRLI";
62362306a36Sopenharmony_ci		break;
62462306a36Sopenharmony_ci	case SRB_NACK_LOGO:
62562306a36Sopenharmony_ci		fcport->fw_login_state = DSC_LS_LOGO_PEND;
62662306a36Sopenharmony_ci		c = "LOGO";
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
63162306a36Sopenharmony_ci	if (!sp)
63262306a36Sopenharmony_ci		goto done;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	sp->type = type;
63562306a36Sopenharmony_ci	sp->name = "nack";
63662306a36Sopenharmony_ci	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
63762306a36Sopenharmony_ci			      qla2x00_async_nack_sp_done);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	sp->u.iocb_cmd.u.nack.ntfy = ntfy;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0x20f4,
64262306a36Sopenharmony_ci	    "Async-%s %8phC hndl %x %s\n",
64362306a36Sopenharmony_ci	    sp->name, fcport->port_name, sp->handle, c);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	rval = qla2x00_start_sp(sp);
64662306a36Sopenharmony_ci	if (rval != QLA_SUCCESS)
64762306a36Sopenharmony_ci		goto done_free_sp;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return rval;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cidone_free_sp:
65262306a36Sopenharmony_ci	kref_put(&sp->cmd_kref, qla2x00_sp_release);
65362306a36Sopenharmony_cidone:
65462306a36Sopenharmony_ci	fcport->flags &= ~FCF_ASYNC_SENT;
65562306a36Sopenharmony_ci	return rval;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_civoid qla24xx_do_nack_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	fc_port_t *t;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	switch (e->u.nack.type) {
66362306a36Sopenharmony_ci	case SRB_NACK_PRLI:
66462306a36Sopenharmony_ci		t = e->u.nack.fcport;
66562306a36Sopenharmony_ci		flush_work(&t->del_work);
66662306a36Sopenharmony_ci		flush_work(&t->free_work);
66762306a36Sopenharmony_ci		mutex_lock(&vha->vha_tgt.tgt_mutex);
66862306a36Sopenharmony_ci		t = qlt_create_sess(vha, e->u.nack.fcport, 0);
66962306a36Sopenharmony_ci		mutex_unlock(&vha->vha_tgt.tgt_mutex);
67062306a36Sopenharmony_ci		if (t) {
67162306a36Sopenharmony_ci			ql_log(ql_log_info, vha, 0xd034,
67262306a36Sopenharmony_ci			    "%s create sess success %p", __func__, t);
67362306a36Sopenharmony_ci			/* create sess has an extra kref */
67462306a36Sopenharmony_ci			vha->hw->tgt.tgt_ops->put_sess(e->u.nack.fcport);
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci	qla24xx_async_notify_ack(vha, e->u.nack.fcport,
67962306a36Sopenharmony_ci	    (struct imm_ntfy_from_isp *)e->u.nack.iocb, e->u.nack.type);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_civoid qla24xx_delete_sess_fn(struct work_struct *work)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	fc_port_t *fcport = container_of(work, struct fc_port, del_work);
68562306a36Sopenharmony_ci	struct qla_hw_data *ha = NULL;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (!fcport || !fcport->vha || !fcport->vha->hw)
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	ha = fcport->vha->hw;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (fcport->se_sess) {
69362306a36Sopenharmony_ci		ha->tgt.tgt_ops->shutdown_sess(fcport);
69462306a36Sopenharmony_ci		ha->tgt.tgt_ops->put_sess(fcport);
69562306a36Sopenharmony_ci	} else {
69662306a36Sopenharmony_ci		qlt_unreg_sess(fcport);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/*
70162306a36Sopenharmony_ci * Called from qla2x00_reg_remote_port()
70262306a36Sopenharmony_ci */
70362306a36Sopenharmony_civoid qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
70662306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
70762306a36Sopenharmony_ci	struct fc_port *sess = fcport;
70862306a36Sopenharmony_ci	unsigned long flags;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (!vha->hw->tgt.tgt_ops)
71162306a36Sopenharmony_ci		return;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
71462306a36Sopenharmony_ci	if (tgt->tgt_stop) {
71562306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
71662306a36Sopenharmony_ci		return;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (fcport->disc_state == DSC_DELETE_PEND) {
72062306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
72162306a36Sopenharmony_ci		return;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (!sess->se_sess) {
72562306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		mutex_lock(&vha->vha_tgt.tgt_mutex);
72862306a36Sopenharmony_ci		sess = qlt_create_sess(vha, fcport, false);
72962306a36Sopenharmony_ci		mutex_unlock(&vha->vha_tgt.tgt_mutex);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
73262306a36Sopenharmony_ci	} else {
73362306a36Sopenharmony_ci		if (fcport->fw_login_state == DSC_LS_PRLI_COMP) {
73462306a36Sopenharmony_ci			spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
73562306a36Sopenharmony_ci			return;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		if (!kref_get_unless_zero(&sess->sess_kref)) {
73962306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0x2107,
74062306a36Sopenharmony_ci			    "%s: kref_get fail sess %8phC \n",
74162306a36Sopenharmony_ci			    __func__, sess->port_name);
74262306a36Sopenharmony_ci			spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
74362306a36Sopenharmony_ci			return;
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c,
74762306a36Sopenharmony_ci		    "qla_target(%u): %ssession for port %8phC "
74862306a36Sopenharmony_ci		    "(loop ID %d) reappeared\n", vha->vp_idx,
74962306a36Sopenharmony_ci		    sess->local ? "local " : "", sess->port_name, sess->loop_id);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007,
75262306a36Sopenharmony_ci		    "Reappeared sess %p\n", sess);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		ha->tgt.tgt_ops->update_sess(sess, fcport->d_id,
75562306a36Sopenharmony_ci		    fcport->loop_id,
75662306a36Sopenharmony_ci		    (fcport->flags & FCF_CONF_COMP_SUPPORTED));
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	if (sess && sess->local) {
76062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d,
76162306a36Sopenharmony_ci		    "qla_target(%u): local session for "
76262306a36Sopenharmony_ci		    "port %8phC (loop ID %d) became global\n", vha->vp_idx,
76362306a36Sopenharmony_ci		    fcport->port_name, sess->loop_id);
76462306a36Sopenharmony_ci		sess->local = 0;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	ha->tgt.tgt_ops->put_sess(sess);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/*
77262306a36Sopenharmony_ci * This is a zero-base ref-counting solution, since hardware_lock
77362306a36Sopenharmony_ci * guarantees that ref_count is not modified concurrently.
77462306a36Sopenharmony_ci * Upon successful return content of iocb is undefined
77562306a36Sopenharmony_ci */
77662306a36Sopenharmony_cistatic struct qlt_plogi_ack_t *
77762306a36Sopenharmony_ciqlt_plogi_ack_find_add(struct scsi_qla_host *vha, port_id_t *id,
77862306a36Sopenharmony_ci		       struct imm_ntfy_from_isp *iocb)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct qlt_plogi_ack_t *pla;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	lockdep_assert_held(&vha->hw->hardware_lock);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	list_for_each_entry(pla, &vha->plogi_ack_list, list) {
78562306a36Sopenharmony_ci		if (pla->id.b24 == id->b24) {
78662306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0x210d,
78762306a36Sopenharmony_ci			    "%s %d %8phC Term INOT due to new INOT",
78862306a36Sopenharmony_ci			    __func__, __LINE__,
78962306a36Sopenharmony_ci			    pla->iocb.u.isp24.port_name);
79062306a36Sopenharmony_ci			qlt_send_term_imm_notif(vha, &pla->iocb, 1);
79162306a36Sopenharmony_ci			memcpy(&pla->iocb, iocb, sizeof(pla->iocb));
79262306a36Sopenharmony_ci			return pla;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	pla = kmem_cache_zalloc(qla_tgt_plogi_cachep, GFP_ATOMIC);
79762306a36Sopenharmony_ci	if (!pla) {
79862306a36Sopenharmony_ci		ql_dbg(ql_dbg_async, vha, 0x5088,
79962306a36Sopenharmony_ci		       "qla_target(%d): Allocation of plogi_ack failed\n",
80062306a36Sopenharmony_ci		       vha->vp_idx);
80162306a36Sopenharmony_ci		return NULL;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	memcpy(&pla->iocb, iocb, sizeof(pla->iocb));
80562306a36Sopenharmony_ci	pla->id = *id;
80662306a36Sopenharmony_ci	list_add_tail(&pla->list, &vha->plogi_ack_list);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return pla;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_civoid qlt_plogi_ack_unref(struct scsi_qla_host *vha,
81262306a36Sopenharmony_ci    struct qlt_plogi_ack_t *pla)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct imm_ntfy_from_isp *iocb = &pla->iocb;
81562306a36Sopenharmony_ci	port_id_t port_id;
81662306a36Sopenharmony_ci	uint16_t loop_id;
81762306a36Sopenharmony_ci	fc_port_t *fcport = pla->fcport;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	BUG_ON(!pla->ref_count);
82062306a36Sopenharmony_ci	pla->ref_count--;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (pla->ref_count)
82362306a36Sopenharmony_ci		return;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0x5089,
82662306a36Sopenharmony_ci	    "Sending PLOGI ACK to wwn %8phC s_id %02x:%02x:%02x loop_id %#04x"
82762306a36Sopenharmony_ci	    " exch %#x ox_id %#x\n", iocb->u.isp24.port_name,
82862306a36Sopenharmony_ci	    iocb->u.isp24.port_id[2], iocb->u.isp24.port_id[1],
82962306a36Sopenharmony_ci	    iocb->u.isp24.port_id[0],
83062306a36Sopenharmony_ci	    le16_to_cpu(iocb->u.isp24.nport_handle),
83162306a36Sopenharmony_ci	    iocb->u.isp24.exchange_address, iocb->ox_id);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	port_id.b.domain = iocb->u.isp24.port_id[2];
83462306a36Sopenharmony_ci	port_id.b.area   = iocb->u.isp24.port_id[1];
83562306a36Sopenharmony_ci	port_id.b.al_pa  = iocb->u.isp24.port_id[0];
83662306a36Sopenharmony_ci	port_id.b.rsvd_1 = 0;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	fcport->loop_id = loop_id;
84162306a36Sopenharmony_ci	fcport->d_id = port_id;
84262306a36Sopenharmony_ci	if (iocb->u.isp24.status_subcode == ELS_PLOGI)
84362306a36Sopenharmony_ci		qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PLOGI);
84462306a36Sopenharmony_ci	else
84562306a36Sopenharmony_ci		qla24xx_post_nack_work(vha, fcport, iocb, SRB_NACK_PRLI);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	list_for_each_entry(fcport, &vha->vp_fcports, list) {
84862306a36Sopenharmony_ci		if (fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] == pla)
84962306a36Sopenharmony_ci			fcport->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL;
85062306a36Sopenharmony_ci		if (fcport->plogi_link[QLT_PLOGI_LINK_CONFLICT] == pla)
85162306a36Sopenharmony_ci			fcport->plogi_link[QLT_PLOGI_LINK_CONFLICT] = NULL;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	list_del(&pla->list);
85562306a36Sopenharmony_ci	kmem_cache_free(qla_tgt_plogi_cachep, pla);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_civoid
85962306a36Sopenharmony_ciqlt_plogi_ack_link(struct scsi_qla_host *vha, struct qlt_plogi_ack_t *pla,
86062306a36Sopenharmony_ci    struct fc_port *sess, enum qlt_plogi_link_t link)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct imm_ntfy_from_isp *iocb = &pla->iocb;
86362306a36Sopenharmony_ci	/* Inc ref_count first because link might already be pointing at pla */
86462306a36Sopenharmony_ci	pla->ref_count++;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf097,
86762306a36Sopenharmony_ci		"Linking sess %p [%d] wwn %8phC with PLOGI ACK to wwn %8phC"
86862306a36Sopenharmony_ci		" s_id %02x:%02x:%02x, ref=%d pla %p link %d\n",
86962306a36Sopenharmony_ci		sess, link, sess->port_name,
87062306a36Sopenharmony_ci		iocb->u.isp24.port_name, iocb->u.isp24.port_id[2],
87162306a36Sopenharmony_ci		iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
87262306a36Sopenharmony_ci		pla->ref_count, pla, link);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (link == QLT_PLOGI_LINK_CONFLICT) {
87562306a36Sopenharmony_ci		switch (sess->disc_state) {
87662306a36Sopenharmony_ci		case DSC_DELETED:
87762306a36Sopenharmony_ci		case DSC_DELETE_PEND:
87862306a36Sopenharmony_ci			pla->ref_count--;
87962306a36Sopenharmony_ci			return;
88062306a36Sopenharmony_ci		default:
88162306a36Sopenharmony_ci			break;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (sess->plogi_link[link])
88662306a36Sopenharmony_ci		qlt_plogi_ack_unref(vha, sess->plogi_link[link]);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (link == QLT_PLOGI_LINK_SAME_WWN)
88962306a36Sopenharmony_ci		pla->fcport = sess;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	sess->plogi_link[link] = pla;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_citypedef struct {
89562306a36Sopenharmony_ci	/* These fields must be initialized by the caller */
89662306a36Sopenharmony_ci	port_id_t id;
89762306a36Sopenharmony_ci	/*
89862306a36Sopenharmony_ci	 * number of cmds dropped while we were waiting for
89962306a36Sopenharmony_ci	 * initiator to ack LOGO initialize to 1 if LOGO is
90062306a36Sopenharmony_ci	 * triggered by a command, otherwise, to 0
90162306a36Sopenharmony_ci	 */
90262306a36Sopenharmony_ci	int cmd_count;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* These fields are used by callee */
90562306a36Sopenharmony_ci	struct list_head list;
90662306a36Sopenharmony_ci} qlt_port_logo_t;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void
90962306a36Sopenharmony_ciqlt_send_first_logo(struct scsi_qla_host *vha, qlt_port_logo_t *logo)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	qlt_port_logo_t *tmp;
91262306a36Sopenharmony_ci	int res;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags)) {
91562306a36Sopenharmony_ci		res = 0;
91662306a36Sopenharmony_ci		goto out;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	mutex_lock(&vha->vha_tgt.tgt_mutex);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	list_for_each_entry(tmp, &vha->logo_list, list) {
92262306a36Sopenharmony_ci		if (tmp->id.b24 == logo->id.b24) {
92362306a36Sopenharmony_ci			tmp->cmd_count += logo->cmd_count;
92462306a36Sopenharmony_ci			mutex_unlock(&vha->vha_tgt.tgt_mutex);
92562306a36Sopenharmony_ci			return;
92662306a36Sopenharmony_ci		}
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	list_add_tail(&logo->list, &vha->logo_list);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	mutex_unlock(&vha->vha_tgt.tgt_mutex);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	res = qla24xx_els_dcmd_iocb(vha, ELS_DCMD_LOGO, logo->id);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	mutex_lock(&vha->vha_tgt.tgt_mutex);
93662306a36Sopenharmony_ci	list_del(&logo->list);
93762306a36Sopenharmony_ci	mutex_unlock(&vha->vha_tgt.tgt_mutex);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ciout:
94062306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf098,
94162306a36Sopenharmony_ci	    "Finished LOGO to %02x:%02x:%02x, dropped %d cmds, res = %#x\n",
94262306a36Sopenharmony_ci	    logo->id.b.domain, logo->id.b.area, logo->id.b.al_pa,
94362306a36Sopenharmony_ci	    logo->cmd_count, res);
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_civoid qlt_free_session_done(struct work_struct *work)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct fc_port *sess = container_of(work, struct fc_port,
94962306a36Sopenharmony_ci	    free_work);
95062306a36Sopenharmony_ci	struct qla_tgt *tgt = sess->tgt;
95162306a36Sopenharmony_ci	struct scsi_qla_host *vha = sess->vha;
95262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
95362306a36Sopenharmony_ci	unsigned long flags;
95462306a36Sopenharmony_ci	bool logout_started = false;
95562306a36Sopenharmony_ci	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
95662306a36Sopenharmony_ci	struct qlt_plogi_ack_t *own =
95762306a36Sopenharmony_ci		sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN];
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0xf084,
96062306a36Sopenharmony_ci		"%s: se_sess %p / sess %p from port %8phC loop_id %#04x"
96162306a36Sopenharmony_ci		" s_id %02x:%02x:%02x logout %d keep %d els_logo %d\n",
96262306a36Sopenharmony_ci		__func__, sess->se_sess, sess, sess->port_name, sess->loop_id,
96362306a36Sopenharmony_ci		sess->d_id.b.domain, sess->d_id.b.area, sess->d_id.b.al_pa,
96462306a36Sopenharmony_ci		sess->logout_on_delete, sess->keep_nport_handle,
96562306a36Sopenharmony_ci		sess->send_els_logo);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (!IS_SW_RESV_ADDR(sess->d_id)) {
96862306a36Sopenharmony_ci		qla2x00_mark_device_lost(vha, sess, 0);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		if (sess->send_els_logo) {
97162306a36Sopenharmony_ci			qlt_port_logo_t logo;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci			logo.id = sess->d_id;
97462306a36Sopenharmony_ci			logo.cmd_count = 0;
97562306a36Sopenharmony_ci			INIT_LIST_HEAD(&logo.list);
97662306a36Sopenharmony_ci			if (!own)
97762306a36Sopenharmony_ci				qlt_send_first_logo(vha, &logo);
97862306a36Sopenharmony_ci			sess->send_els_logo = 0;
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		if (sess->logout_on_delete && sess->loop_id != FC_NO_LOOP_ID) {
98262306a36Sopenharmony_ci			int rc;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci			if (!own ||
98562306a36Sopenharmony_ci			     (own->iocb.u.isp24.status_subcode == ELS_PLOGI)) {
98662306a36Sopenharmony_ci				sess->logout_completed = 0;
98762306a36Sopenharmony_ci				rc = qla2x00_post_async_logout_work(vha, sess,
98862306a36Sopenharmony_ci				    NULL);
98962306a36Sopenharmony_ci				if (rc != QLA_SUCCESS)
99062306a36Sopenharmony_ci					ql_log(ql_log_warn, vha, 0xf085,
99162306a36Sopenharmony_ci					    "Schedule logo failed sess %p rc %d\n",
99262306a36Sopenharmony_ci					    sess, rc);
99362306a36Sopenharmony_ci				else
99462306a36Sopenharmony_ci					logout_started = true;
99562306a36Sopenharmony_ci			} else if (own && (own->iocb.u.isp24.status_subcode ==
99662306a36Sopenharmony_ci				ELS_PRLI) && ha->flags.rida_fmt2) {
99762306a36Sopenharmony_ci				rc = qla2x00_post_async_prlo_work(vha, sess,
99862306a36Sopenharmony_ci				    NULL);
99962306a36Sopenharmony_ci				if (rc != QLA_SUCCESS)
100062306a36Sopenharmony_ci					ql_log(ql_log_warn, vha, 0xf085,
100162306a36Sopenharmony_ci					    "Schedule PRLO failed sess %p rc %d\n",
100262306a36Sopenharmony_ci					    sess, rc);
100362306a36Sopenharmony_ci				else
100462306a36Sopenharmony_ci					logout_started = true;
100562306a36Sopenharmony_ci			}
100662306a36Sopenharmony_ci		} /* if sess->logout_on_delete */
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		if (sess->nvme_flag & NVME_FLAG_REGISTERED &&
100962306a36Sopenharmony_ci		    !(sess->nvme_flag & NVME_FLAG_DELETING)) {
101062306a36Sopenharmony_ci			sess->nvme_flag |= NVME_FLAG_DELETING;
101162306a36Sopenharmony_ci			qla_nvme_unregister_remote_port(sess);
101262306a36Sopenharmony_ci		}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci		if (ha->flags.edif_enabled &&
101562306a36Sopenharmony_ci				(!own || own->iocb.u.isp24.status_subcode == ELS_PLOGI)) {
101662306a36Sopenharmony_ci			sess->edif.authok = 0;
101762306a36Sopenharmony_ci			if (!ha->flags.host_shutting_down) {
101862306a36Sopenharmony_ci				ql_dbg(ql_dbg_edif, vha, 0x911e,
101962306a36Sopenharmony_ci				       "%s wwpn %8phC calling qla2x00_release_all_sadb\n",
102062306a36Sopenharmony_ci				       __func__, sess->port_name);
102162306a36Sopenharmony_ci				qla2x00_release_all_sadb(vha, sess);
102262306a36Sopenharmony_ci			} else {
102362306a36Sopenharmony_ci				ql_dbg(ql_dbg_edif, vha, 0x911e,
102462306a36Sopenharmony_ci				       "%s bypassing release_all_sadb\n",
102562306a36Sopenharmony_ci				       __func__);
102662306a36Sopenharmony_ci			}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci			qla_edif_clear_appdata(vha, sess);
102962306a36Sopenharmony_ci			qla_edif_sess_down(vha, sess);
103062306a36Sopenharmony_ci		}
103162306a36Sopenharmony_ci	}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/*
103462306a36Sopenharmony_ci	 * Release the target session for FC Nexus from fabric module code.
103562306a36Sopenharmony_ci	 */
103662306a36Sopenharmony_ci	if (sess->se_sess != NULL)
103762306a36Sopenharmony_ci		ha->tgt.tgt_ops->free_session(sess);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (logout_started) {
104062306a36Sopenharmony_ci		bool traced = false;
104162306a36Sopenharmony_ci		u16 cnt = 0;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		while (!READ_ONCE(sess->logout_completed)) {
104462306a36Sopenharmony_ci			if (!traced) {
104562306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0xf086,
104662306a36Sopenharmony_ci					"%s: waiting for sess %p logout\n",
104762306a36Sopenharmony_ci					__func__, sess);
104862306a36Sopenharmony_ci				traced = true;
104962306a36Sopenharmony_ci			}
105062306a36Sopenharmony_ci			msleep(100);
105162306a36Sopenharmony_ci			cnt++;
105262306a36Sopenharmony_ci			/*
105362306a36Sopenharmony_ci			 * Driver timeout is set to 22 Sec, update count value to loop
105462306a36Sopenharmony_ci			 * long enough for log-out to complete before advancing. Otherwise,
105562306a36Sopenharmony_ci			 * straddling logout can interfere with re-login attempt.
105662306a36Sopenharmony_ci			 */
105762306a36Sopenharmony_ci			if (cnt > 230)
105862306a36Sopenharmony_ci				break;
105962306a36Sopenharmony_ci		}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0xf087,
106262306a36Sopenharmony_ci		    "%s: sess %p logout completed\n", __func__, sess);
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (sess->logo_ack_needed) {
106662306a36Sopenharmony_ci		sess->logo_ack_needed = 0;
106762306a36Sopenharmony_ci		qla24xx_async_notify_ack(vha, sess,
106862306a36Sopenharmony_ci			(struct imm_ntfy_from_isp *)sess->iocb, SRB_NACK_LOGO);
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
107262306a36Sopenharmony_ci	if (sess->se_sess) {
107362306a36Sopenharmony_ci		sess->se_sess = NULL;
107462306a36Sopenharmony_ci		if (tgt && !IS_SW_RESV_ADDR(sess->d_id))
107562306a36Sopenharmony_ci			tgt->sess_count--;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	qla2x00_set_fcport_disc_state(sess, DSC_DELETED);
107962306a36Sopenharmony_ci	sess->fw_login_state = DSC_LS_PORT_UNAVAIL;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (sess->login_succ && !IS_SW_RESV_ADDR(sess->d_id)) {
108262306a36Sopenharmony_ci		vha->fcport_count--;
108362306a36Sopenharmony_ci		sess->login_succ = 0;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	qla2x00_clear_loop_id(sess);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (sess->conflict) {
108962306a36Sopenharmony_ci		sess->conflict->login_pause = 0;
109062306a36Sopenharmony_ci		sess->conflict = NULL;
109162306a36Sopenharmony_ci		if (!test_bit(UNLOADING, &vha->dpc_flags))
109262306a36Sopenharmony_ci			set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	{
109662306a36Sopenharmony_ci		struct qlt_plogi_ack_t *con =
109762306a36Sopenharmony_ci		    sess->plogi_link[QLT_PLOGI_LINK_CONFLICT];
109862306a36Sopenharmony_ci		struct imm_ntfy_from_isp *iocb;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		own = sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN];
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci		if (con) {
110362306a36Sopenharmony_ci			iocb = &con->iocb;
110462306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf099,
110562306a36Sopenharmony_ci				 "se_sess %p / sess %p port %8phC is gone,"
110662306a36Sopenharmony_ci				 " %s (ref=%d), releasing PLOGI for %8phC (ref=%d)\n",
110762306a36Sopenharmony_ci				 sess->se_sess, sess, sess->port_name,
110862306a36Sopenharmony_ci				 own ? "releasing own PLOGI" : "no own PLOGI pending",
110962306a36Sopenharmony_ci				 own ? own->ref_count : -1,
111062306a36Sopenharmony_ci				 iocb->u.isp24.port_name, con->ref_count);
111162306a36Sopenharmony_ci			qlt_plogi_ack_unref(vha, con);
111262306a36Sopenharmony_ci			sess->plogi_link[QLT_PLOGI_LINK_CONFLICT] = NULL;
111362306a36Sopenharmony_ci		} else {
111462306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09a,
111562306a36Sopenharmony_ci			    "se_sess %p / sess %p port %8phC is gone, %s (ref=%d)\n",
111662306a36Sopenharmony_ci			    sess->se_sess, sess, sess->port_name,
111762306a36Sopenharmony_ci			    own ? "releasing own PLOGI" :
111862306a36Sopenharmony_ci			    "no own PLOGI pending",
111962306a36Sopenharmony_ci			    own ? own->ref_count : -1);
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		if (own) {
112362306a36Sopenharmony_ci			sess->fw_login_state = DSC_LS_PLOGI_PEND;
112462306a36Sopenharmony_ci			qlt_plogi_ack_unref(vha, own);
112562306a36Sopenharmony_ci			sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] = NULL;
112662306a36Sopenharmony_ci		}
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	sess->explicit_logout = 0;
113062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	qla2x00_dfs_remove_rport(vha, sess);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	spin_lock_irqsave(&vha->work_lock, flags);
113562306a36Sopenharmony_ci	sess->flags &= ~FCF_ASYNC_SENT;
113662306a36Sopenharmony_ci	sess->deleted = QLA_SESS_DELETED;
113762306a36Sopenharmony_ci	sess->free_pending = 0;
113862306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->work_lock, flags);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0xf001,
114162306a36Sopenharmony_ci	    "Unregistration of sess %p %8phC finished fcp_cnt %d\n",
114262306a36Sopenharmony_ci		sess, sess->port_name, vha->fcport_count);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (tgt && (tgt->sess_count == 0))
114562306a36Sopenharmony_ci		wake_up_all(&tgt->waitQ);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (!test_bit(PFLG_DRIVER_REMOVING, &base_vha->pci_flags) &&
114862306a36Sopenharmony_ci	    !(vha->vp_idx && test_bit(VPORT_DELETE, &vha->dpc_flags)) &&
114962306a36Sopenharmony_ci	    (!tgt || !tgt->tgt_stop) && !LOOP_TRANSITION(vha)) {
115062306a36Sopenharmony_ci		switch (vha->host->active_mode) {
115162306a36Sopenharmony_ci		case MODE_INITIATOR:
115262306a36Sopenharmony_ci		case MODE_DUAL:
115362306a36Sopenharmony_ci			set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
115462306a36Sopenharmony_ci			qla2xxx_wake_dpc(vha);
115562306a36Sopenharmony_ci			break;
115662306a36Sopenharmony_ci		case MODE_TARGET:
115762306a36Sopenharmony_ci		default:
115862306a36Sopenharmony_ci			/* no-op */
115962306a36Sopenharmony_ci			break;
116062306a36Sopenharmony_ci		}
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (vha->fcport_count == 0)
116462306a36Sopenharmony_ci		wake_up_all(&vha->fcport_waitQ);
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci/* ha->tgt.sess_lock supposed to be held on entry */
116862306a36Sopenharmony_civoid qlt_unreg_sess(struct fc_port *sess)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct scsi_qla_host *vha = sess->vha;
117162306a36Sopenharmony_ci	unsigned long flags;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, sess->vha, 0x210a,
117462306a36Sopenharmony_ci	    "%s sess %p for deletion %8phC\n",
117562306a36Sopenharmony_ci	    __func__, sess, sess->port_name);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	spin_lock_irqsave(&sess->vha->work_lock, flags);
117862306a36Sopenharmony_ci	if (sess->free_pending) {
117962306a36Sopenharmony_ci		spin_unlock_irqrestore(&sess->vha->work_lock, flags);
118062306a36Sopenharmony_ci		return;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci	sess->free_pending = 1;
118362306a36Sopenharmony_ci	/*
118462306a36Sopenharmony_ci	 * Use FCF_ASYNC_SENT flag to block other cmds used in sess
118562306a36Sopenharmony_ci	 * management from being sent.
118662306a36Sopenharmony_ci	 */
118762306a36Sopenharmony_ci	sess->flags |= FCF_ASYNC_SENT;
118862306a36Sopenharmony_ci	sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
118962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sess->vha->work_lock, flags);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (sess->se_sess)
119262306a36Sopenharmony_ci		vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	qla2x00_set_fcport_disc_state(sess, DSC_DELETE_PEND);
119562306a36Sopenharmony_ci	sess->last_rscn_gen = sess->rscn_gen;
119662306a36Sopenharmony_ci	sess->last_login_gen = sess->login_gen;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	queue_work(sess->vha->hw->wq, &sess->free_work);
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_unreg_sess);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
120562306a36Sopenharmony_ci	struct fc_port *sess = NULL;
120662306a36Sopenharmony_ci	uint16_t loop_id;
120762306a36Sopenharmony_ci	int res = 0;
120862306a36Sopenharmony_ci	struct imm_ntfy_from_isp *n = (struct imm_ntfy_from_isp *)iocb;
120962306a36Sopenharmony_ci	unsigned long flags;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	loop_id = le16_to_cpu(n->u.isp24.nport_handle);
121262306a36Sopenharmony_ci	if (loop_id == 0xFFFF) {
121362306a36Sopenharmony_ci		/* Global event */
121462306a36Sopenharmony_ci		atomic_inc(&vha->vha_tgt.qla_tgt->tgt_global_resets_count);
121562306a36Sopenharmony_ci		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
121662306a36Sopenharmony_ci		qlt_clear_tgt_db(vha->vha_tgt.qla_tgt);
121762306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
121862306a36Sopenharmony_ci	} else {
121962306a36Sopenharmony_ci		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
122062306a36Sopenharmony_ci		sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id);
122162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe000,
122562306a36Sopenharmony_ci	    "Using sess for qla_tgt_reset: %p\n", sess);
122662306a36Sopenharmony_ci	if (!sess) {
122762306a36Sopenharmony_ci		res = -ESRCH;
122862306a36Sopenharmony_ci		return res;
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe047,
123262306a36Sopenharmony_ci	    "scsi(%ld): resetting (session %p from port %8phC mcmd %x, "
123362306a36Sopenharmony_ci	    "loop_id %d)\n", vha->host_no, sess, sess->port_name,
123462306a36Sopenharmony_ci	    mcmd, loop_id);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	return qlt_issue_task_mgmt(sess, 0, mcmd, iocb, QLA24XX_MGMT_SEND_NACK);
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic void qla24xx_chk_fcp_state(struct fc_port *sess)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	if (sess->chip_reset != sess->vha->hw->base_qpair->chip_reset) {
124262306a36Sopenharmony_ci		sess->logout_on_delete = 0;
124362306a36Sopenharmony_ci		sess->logo_ack_needed = 0;
124462306a36Sopenharmony_ci		sess->fw_login_state = DSC_LS_PORT_UNAVAIL;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_civoid qlt_schedule_sess_for_deletion(struct fc_port *sess)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct qla_tgt *tgt = sess->tgt;
125162306a36Sopenharmony_ci	unsigned long flags;
125262306a36Sopenharmony_ci	u16 sec;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	switch (sess->disc_state) {
125562306a36Sopenharmony_ci	case DSC_DELETE_PEND:
125662306a36Sopenharmony_ci		return;
125762306a36Sopenharmony_ci	case DSC_DELETED:
125862306a36Sopenharmony_ci		if (!sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] &&
125962306a36Sopenharmony_ci			!sess->plogi_link[QLT_PLOGI_LINK_CONFLICT]) {
126062306a36Sopenharmony_ci			if (tgt && tgt->tgt_stop && tgt->sess_count == 0)
126162306a36Sopenharmony_ci				wake_up_all(&tgt->waitQ);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci			if (sess->vha->fcport_count == 0)
126462306a36Sopenharmony_ci				wake_up_all(&sess->vha->fcport_waitQ);
126562306a36Sopenharmony_ci			return;
126662306a36Sopenharmony_ci		}
126762306a36Sopenharmony_ci		break;
126862306a36Sopenharmony_ci	case DSC_UPD_FCPORT:
126962306a36Sopenharmony_ci		/*
127062306a36Sopenharmony_ci		 * This port is not done reporting to upper layer.
127162306a36Sopenharmony_ci		 * let it finish
127262306a36Sopenharmony_ci		 */
127362306a36Sopenharmony_ci		sess->next_disc_state = DSC_DELETE_PEND;
127462306a36Sopenharmony_ci		sec = jiffies_to_msecs(jiffies -
127562306a36Sopenharmony_ci		    sess->jiffies_at_registration)/1000;
127662306a36Sopenharmony_ci		if (sess->sec_since_registration < sec && sec && !(sec % 5)) {
127762306a36Sopenharmony_ci			sess->sec_since_registration = sec;
127862306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, sess->vha, 0xffff,
127962306a36Sopenharmony_ci			    "%s %8phC : Slow Rport registration(%d Sec)\n",
128062306a36Sopenharmony_ci			    __func__, sess->port_name, sec);
128162306a36Sopenharmony_ci		}
128262306a36Sopenharmony_ci		return;
128362306a36Sopenharmony_ci	default:
128462306a36Sopenharmony_ci		break;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	spin_lock_irqsave(&sess->vha->work_lock, flags);
128862306a36Sopenharmony_ci	if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
128962306a36Sopenharmony_ci		spin_unlock_irqrestore(&sess->vha->work_lock, flags);
129062306a36Sopenharmony_ci		return;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci	sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
129362306a36Sopenharmony_ci	spin_unlock_irqrestore(&sess->vha->work_lock, flags);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	sess->prli_pend_timer = 0;
129662306a36Sopenharmony_ci	qla2x00_set_fcport_disc_state(sess, DSC_DELETE_PEND);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	qla24xx_chk_fcp_state(sess);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	ql_dbg(ql_log_warn, sess->vha, 0xe001,
130162306a36Sopenharmony_ci	    "Scheduling sess %p for deletion %8phC fc4_type %x\n",
130262306a36Sopenharmony_ci	    sess, sess->port_name, sess->fc4_type);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	WARN_ON(!queue_work(sess->vha->hw->wq, &sess->del_work));
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_cistatic void qlt_clear_tgt_db(struct qla_tgt *tgt)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct fc_port *sess;
131062306a36Sopenharmony_ci	scsi_qla_host_t *vha = tgt->vha;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	list_for_each_entry(sess, &vha->vp_fcports, list) {
131362306a36Sopenharmony_ci		if (sess->se_sess)
131462306a36Sopenharmony_ci			qlt_schedule_sess_for_deletion(sess);
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	/* At this point tgt could be already dead */
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic int qla24xx_get_loop_id(struct scsi_qla_host *vha, be_id_t s_id,
132162306a36Sopenharmony_ci	uint16_t *loop_id)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
132462306a36Sopenharmony_ci	dma_addr_t gid_list_dma;
132562306a36Sopenharmony_ci	struct gid_list_info *gid_list, *gid;
132662306a36Sopenharmony_ci	int res, rc, i;
132762306a36Sopenharmony_ci	uint16_t entries;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	gid_list = dma_alloc_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
133062306a36Sopenharmony_ci	    &gid_list_dma, GFP_KERNEL);
133162306a36Sopenharmony_ci	if (!gid_list) {
133262306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf044,
133362306a36Sopenharmony_ci		    "qla_target(%d): DMA Alloc failed of %u\n",
133462306a36Sopenharmony_ci		    vha->vp_idx, qla2x00_gid_list_size(ha));
133562306a36Sopenharmony_ci		return -ENOMEM;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	/* Get list of logged in devices */
133962306a36Sopenharmony_ci	rc = qla24xx_gidlist_wait(vha, gid_list, gid_list_dma, &entries);
134062306a36Sopenharmony_ci	if (rc != QLA_SUCCESS) {
134162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf045,
134262306a36Sopenharmony_ci		    "qla_target(%d): get_id_list() failed: %x\n",
134362306a36Sopenharmony_ci		    vha->vp_idx, rc);
134462306a36Sopenharmony_ci		res = -EBUSY;
134562306a36Sopenharmony_ci		goto out_free_id_list;
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	gid = gid_list;
134962306a36Sopenharmony_ci	res = -ENOENT;
135062306a36Sopenharmony_ci	for (i = 0; i < entries; i++) {
135162306a36Sopenharmony_ci		if (gid->al_pa == s_id.al_pa &&
135262306a36Sopenharmony_ci		    gid->area == s_id.area &&
135362306a36Sopenharmony_ci		    gid->domain == s_id.domain) {
135462306a36Sopenharmony_ci			*loop_id = le16_to_cpu(gid->loop_id);
135562306a36Sopenharmony_ci			res = 0;
135662306a36Sopenharmony_ci			break;
135762306a36Sopenharmony_ci		}
135862306a36Sopenharmony_ci		gid = (void *)gid + ha->gid_list_info_size;
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ciout_free_id_list:
136262306a36Sopenharmony_ci	dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
136362306a36Sopenharmony_ci	    gid_list, gid_list_dma);
136462306a36Sopenharmony_ci	return res;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci/*
136862306a36Sopenharmony_ci * Adds an extra ref to allow to drop hw lock after adding sess to the list.
136962306a36Sopenharmony_ci * Caller must put it.
137062306a36Sopenharmony_ci */
137162306a36Sopenharmony_cistatic struct fc_port *qlt_create_sess(
137262306a36Sopenharmony_ci	struct scsi_qla_host *vha,
137362306a36Sopenharmony_ci	fc_port_t *fcport,
137462306a36Sopenharmony_ci	bool local)
137562306a36Sopenharmony_ci{
137662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
137762306a36Sopenharmony_ci	struct fc_port *sess = fcport;
137862306a36Sopenharmony_ci	unsigned long flags;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	if (vha->vha_tgt.qla_tgt->tgt_stop)
138162306a36Sopenharmony_ci		return NULL;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	if (fcport->se_sess) {
138462306a36Sopenharmony_ci		if (!kref_get_unless_zero(&sess->sess_kref)) {
138562306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0x20f6,
138662306a36Sopenharmony_ci			    "%s: kref_get_unless_zero failed for %8phC\n",
138762306a36Sopenharmony_ci			    __func__, sess->port_name);
138862306a36Sopenharmony_ci			return NULL;
138962306a36Sopenharmony_ci		}
139062306a36Sopenharmony_ci		return fcport;
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci	sess->tgt = vha->vha_tgt.qla_tgt;
139362306a36Sopenharmony_ci	sess->local = local;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	/*
139662306a36Sopenharmony_ci	 * Under normal circumstances we want to logout from firmware when
139762306a36Sopenharmony_ci	 * session eventually ends and release corresponding nport handle.
139862306a36Sopenharmony_ci	 * In the exception cases (e.g. when new PLOGI is waiting) corresponding
139962306a36Sopenharmony_ci	 * code will adjust these flags as necessary.
140062306a36Sopenharmony_ci	 */
140162306a36Sopenharmony_ci	sess->logout_on_delete = 1;
140262306a36Sopenharmony_ci	sess->keep_nport_handle = 0;
140362306a36Sopenharmony_ci	sess->logout_completed = 0;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (ha->tgt.tgt_ops->check_initiator_node_acl(vha,
140662306a36Sopenharmony_ci	    &fcport->port_name[0], sess) < 0) {
140762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf015,
140862306a36Sopenharmony_ci		    "(%d) %8phC check_initiator_node_acl failed\n",
140962306a36Sopenharmony_ci		    vha->vp_idx, fcport->port_name);
141062306a36Sopenharmony_ci		return NULL;
141162306a36Sopenharmony_ci	} else {
141262306a36Sopenharmony_ci		kref_init(&fcport->sess_kref);
141362306a36Sopenharmony_ci		/*
141462306a36Sopenharmony_ci		 * Take an extra reference to ->sess_kref here to handle
141562306a36Sopenharmony_ci		 * fc_port access across ->tgt.sess_lock reaquire.
141662306a36Sopenharmony_ci		 */
141762306a36Sopenharmony_ci		if (!kref_get_unless_zero(&sess->sess_kref)) {
141862306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0x20f7,
141962306a36Sopenharmony_ci			    "%s: kref_get_unless_zero failed for %8phC\n",
142062306a36Sopenharmony_ci			    __func__, sess->port_name);
142162306a36Sopenharmony_ci			return NULL;
142262306a36Sopenharmony_ci		}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
142562306a36Sopenharmony_ci		if (!IS_SW_RESV_ADDR(sess->d_id))
142662306a36Sopenharmony_ci			vha->vha_tgt.qla_tgt->sess_count++;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci		qlt_do_generation_tick(vha, &sess->generation);
142962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006,
143362306a36Sopenharmony_ci	    "Adding sess %p se_sess %p  to tgt %p sess_count %d\n",
143462306a36Sopenharmony_ci	    sess, sess->se_sess, vha->vha_tgt.qla_tgt,
143562306a36Sopenharmony_ci	    vha->vha_tgt.qla_tgt->sess_count);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
143862306a36Sopenharmony_ci	    "qla_target(%d): %ssession for wwn %8phC (loop_id %d, "
143962306a36Sopenharmony_ci	    "s_id %x:%x:%x, confirmed completion %ssupported) added\n",
144062306a36Sopenharmony_ci	    vha->vp_idx, local ?  "local " : "", fcport->port_name,
144162306a36Sopenharmony_ci	    fcport->loop_id, sess->d_id.b.domain, sess->d_id.b.area,
144262306a36Sopenharmony_ci	    sess->d_id.b.al_pa, sess->conf_compl_supported ?  "" : "not ");
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	return sess;
144562306a36Sopenharmony_ci}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci/*
144862306a36Sopenharmony_ci * max_gen - specifies maximum session generation
144962306a36Sopenharmony_ci * at which this deletion requestion is still valid
145062306a36Sopenharmony_ci */
145162306a36Sopenharmony_civoid
145262306a36Sopenharmony_ciqlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
145562306a36Sopenharmony_ci	struct fc_port *sess = fcport;
145662306a36Sopenharmony_ci	unsigned long flags;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	if (!vha->hw->tgt.tgt_ops)
145962306a36Sopenharmony_ci		return;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	if (!tgt)
146262306a36Sopenharmony_ci		return;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
146562306a36Sopenharmony_ci	if (tgt->tgt_stop) {
146662306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
146762306a36Sopenharmony_ci		return;
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci	if (!sess->se_sess) {
147062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
147162306a36Sopenharmony_ci		return;
147262306a36Sopenharmony_ci	}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	if (max_gen - sess->generation < 0) {
147562306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
147662306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf092,
147762306a36Sopenharmony_ci		    "Ignoring stale deletion request for se_sess %p / sess %p"
147862306a36Sopenharmony_ci		    " for port %8phC, req_gen %d, sess_gen %d\n",
147962306a36Sopenharmony_ci		    sess->se_sess, sess, sess->port_name, max_gen,
148062306a36Sopenharmony_ci		    sess->generation);
148162306a36Sopenharmony_ci		return;
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	sess->local = 1;
148762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
148862306a36Sopenharmony_ci	qlt_schedule_sess_for_deletion(sess);
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_cistatic inline int test_tgt_sess_count(struct qla_tgt *tgt)
149262306a36Sopenharmony_ci{
149362306a36Sopenharmony_ci	struct qla_hw_data *ha = tgt->ha;
149462306a36Sopenharmony_ci	unsigned long flags;
149562306a36Sopenharmony_ci	int res;
149662306a36Sopenharmony_ci	/*
149762306a36Sopenharmony_ci	 * We need to protect against race, when tgt is freed before or
149862306a36Sopenharmony_ci	 * inside wake_up()
149962306a36Sopenharmony_ci	 */
150062306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
150162306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002,
150262306a36Sopenharmony_ci	    "tgt %p, sess_count=%d\n",
150362306a36Sopenharmony_ci	    tgt, tgt->sess_count);
150462306a36Sopenharmony_ci	res = (tgt->sess_count == 0);
150562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	return res;
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci/* Called by tcm_qla2xxx configfs code */
151162306a36Sopenharmony_ciint qlt_stop_phase1(struct qla_tgt *tgt)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct scsi_qla_host *vha = tgt->vha;
151462306a36Sopenharmony_ci	struct qla_hw_data *ha = tgt->ha;
151562306a36Sopenharmony_ci	unsigned long flags;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	mutex_lock(&ha->optrom_mutex);
151862306a36Sopenharmony_ci	mutex_lock(&qla_tgt_mutex);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (tgt->tgt_stop || tgt->tgt_stopped) {
152162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04e,
152262306a36Sopenharmony_ci		    "Already in tgt->tgt_stop or tgt_stopped state\n");
152362306a36Sopenharmony_ci		mutex_unlock(&qla_tgt_mutex);
152462306a36Sopenharmony_ci		mutex_unlock(&ha->optrom_mutex);
152562306a36Sopenharmony_ci		return -EPERM;
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xe003, "Stopping target for host %ld(%p)\n",
152962306a36Sopenharmony_ci	    vha->host_no, vha);
153062306a36Sopenharmony_ci	/*
153162306a36Sopenharmony_ci	 * Mutex needed to sync with qla_tgt_fc_port_[added,deleted].
153262306a36Sopenharmony_ci	 * Lock is needed, because we still can get an incoming packet.
153362306a36Sopenharmony_ci	 */
153462306a36Sopenharmony_ci	mutex_lock(&vha->vha_tgt.tgt_mutex);
153562306a36Sopenharmony_ci	tgt->tgt_stop = 1;
153662306a36Sopenharmony_ci	qlt_clear_tgt_db(tgt);
153762306a36Sopenharmony_ci	mutex_unlock(&vha->vha_tgt.tgt_mutex);
153862306a36Sopenharmony_ci	mutex_unlock(&qla_tgt_mutex);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009,
154162306a36Sopenharmony_ci	    "Waiting for sess works (tgt %p)", tgt);
154262306a36Sopenharmony_ci	spin_lock_irqsave(&tgt->sess_work_lock, flags);
154362306a36Sopenharmony_ci	do {
154462306a36Sopenharmony_ci		spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
154562306a36Sopenharmony_ci		flush_work(&tgt->sess_work);
154662306a36Sopenharmony_ci		spin_lock_irqsave(&tgt->sess_work_lock, flags);
154762306a36Sopenharmony_ci	} while (!list_empty(&tgt->sess_works_list));
154862306a36Sopenharmony_ci	spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00a,
155162306a36Sopenharmony_ci	    "Waiting for tgt %p: sess_count=%d\n", tgt, tgt->sess_count);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	wait_event_timeout(tgt->waitQ, test_tgt_sess_count(tgt), 10*HZ);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/* Big hammer */
155662306a36Sopenharmony_ci	if (!ha->flags.host_shutting_down &&
155762306a36Sopenharmony_ci	    (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)))
155862306a36Sopenharmony_ci		qlt_disable_vha(vha);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	/* Wait for sessions to clear out (just in case) */
156162306a36Sopenharmony_ci	wait_event_timeout(tgt->waitQ, test_tgt_sess_count(tgt), 10*HZ);
156262306a36Sopenharmony_ci	mutex_unlock(&ha->optrom_mutex);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	return 0;
156562306a36Sopenharmony_ci}
156662306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_stop_phase1);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci/* Called by tcm_qla2xxx configfs code */
156962306a36Sopenharmony_civoid qlt_stop_phase2(struct qla_tgt *tgt)
157062306a36Sopenharmony_ci{
157162306a36Sopenharmony_ci	scsi_qla_host_t *vha = tgt->vha;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	if (tgt->tgt_stopped) {
157462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04f,
157562306a36Sopenharmony_ci		    "Already in tgt->tgt_stopped state\n");
157662306a36Sopenharmony_ci		dump_stack();
157762306a36Sopenharmony_ci		return;
157862306a36Sopenharmony_ci	}
157962306a36Sopenharmony_ci	if (!tgt->tgt_stop) {
158062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00b,
158162306a36Sopenharmony_ci		    "%s: phase1 stop is not completed\n", __func__);
158262306a36Sopenharmony_ci		dump_stack();
158362306a36Sopenharmony_ci		return;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	mutex_lock(&tgt->ha->optrom_mutex);
158762306a36Sopenharmony_ci	mutex_lock(&vha->vha_tgt.tgt_mutex);
158862306a36Sopenharmony_ci	tgt->tgt_stop = 0;
158962306a36Sopenharmony_ci	tgt->tgt_stopped = 1;
159062306a36Sopenharmony_ci	mutex_unlock(&vha->vha_tgt.tgt_mutex);
159162306a36Sopenharmony_ci	mutex_unlock(&tgt->ha->optrom_mutex);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00c, "Stop of tgt %p finished\n",
159462306a36Sopenharmony_ci	    tgt);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	switch (vha->qlini_mode) {
159762306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_EXCLUSIVE:
159862306a36Sopenharmony_ci		vha->flags.online = 1;
159962306a36Sopenharmony_ci		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
160062306a36Sopenharmony_ci		break;
160162306a36Sopenharmony_ci	default:
160262306a36Sopenharmony_ci		break;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_stop_phase2);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci/* Called from qlt_remove_target() -> qla2x00_remove_one() */
160862306a36Sopenharmony_cistatic void qlt_release(struct qla_tgt *tgt)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	scsi_qla_host_t *vha = tgt->vha;
161162306a36Sopenharmony_ci	void *node;
161262306a36Sopenharmony_ci	u64 key = 0;
161362306a36Sopenharmony_ci	u16 i;
161462306a36Sopenharmony_ci	struct qla_qpair_hint *h;
161562306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	if (!tgt->tgt_stop && !tgt->tgt_stopped)
161862306a36Sopenharmony_ci		qlt_stop_phase1(tgt);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (!tgt->tgt_stopped)
162162306a36Sopenharmony_ci		qlt_stop_phase2(tgt);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	for (i = 0; i < vha->hw->max_qpairs + 1; i++) {
162462306a36Sopenharmony_ci		unsigned long flags;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci		h = &tgt->qphints[i];
162762306a36Sopenharmony_ci		if (h->qpair) {
162862306a36Sopenharmony_ci			spin_lock_irqsave(h->qpair->qp_lock_ptr, flags);
162962306a36Sopenharmony_ci			list_del(&h->hint_elem);
163062306a36Sopenharmony_ci			spin_unlock_irqrestore(h->qpair->qp_lock_ptr, flags);
163162306a36Sopenharmony_ci			h->qpair = NULL;
163262306a36Sopenharmony_ci		}
163362306a36Sopenharmony_ci	}
163462306a36Sopenharmony_ci	kfree(tgt->qphints);
163562306a36Sopenharmony_ci	mutex_lock(&qla_tgt_mutex);
163662306a36Sopenharmony_ci	list_del(&vha->vha_tgt.qla_tgt->tgt_list_entry);
163762306a36Sopenharmony_ci	mutex_unlock(&qla_tgt_mutex);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	btree_for_each_safe64(&tgt->lun_qpair_map, key, node)
164062306a36Sopenharmony_ci		btree_remove64(&tgt->lun_qpair_map, key);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	btree_destroy64(&tgt->lun_qpair_map);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	if (vha->vp_idx)
164562306a36Sopenharmony_ci		if (ha->tgt.tgt_ops &&
164662306a36Sopenharmony_ci		    ha->tgt.tgt_ops->remove_target &&
164762306a36Sopenharmony_ci		    vha->vha_tgt.target_lport_ptr)
164862306a36Sopenharmony_ci			ha->tgt.tgt_ops->remove_target(vha);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	vha->vha_tgt.qla_tgt = NULL;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00d,
165362306a36Sopenharmony_ci	    "Release of tgt %p finished\n", tgt);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	kfree(tgt);
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
165962306a36Sopenharmony_cistatic int qlt_sched_sess_work(struct qla_tgt *tgt, int type,
166062306a36Sopenharmony_ci	const void *param, unsigned int param_size)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	struct qla_tgt_sess_work_param *prm;
166362306a36Sopenharmony_ci	unsigned long flags;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
166662306a36Sopenharmony_ci	if (!prm) {
166762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf050,
166862306a36Sopenharmony_ci		    "qla_target(%d): Unable to create session "
166962306a36Sopenharmony_ci		    "work, command will be refused", 0);
167062306a36Sopenharmony_ci		return -ENOMEM;
167162306a36Sopenharmony_ci	}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00e,
167462306a36Sopenharmony_ci	    "Scheduling work (type %d, prm %p)"
167562306a36Sopenharmony_ci	    " to find session for param %p (size %d, tgt %p)\n",
167662306a36Sopenharmony_ci	    type, prm, param, param_size, tgt);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	prm->type = type;
167962306a36Sopenharmony_ci	memcpy(&prm->tm_iocb, param, param_size);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	spin_lock_irqsave(&tgt->sess_work_lock, flags);
168262306a36Sopenharmony_ci	list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list);
168362306a36Sopenharmony_ci	spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	schedule_work(&tgt->sess_work);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	return 0;
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci/*
169162306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
169262306a36Sopenharmony_ci */
169362306a36Sopenharmony_cistatic void qlt_send_notify_ack(struct qla_qpair *qpair,
169462306a36Sopenharmony_ci	struct imm_ntfy_from_isp *ntfy,
169562306a36Sopenharmony_ci	uint32_t add_flags, uint16_t resp_code, int resp_code_valid,
169662306a36Sopenharmony_ci	uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
169962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
170062306a36Sopenharmony_ci	request_t *pkt;
170162306a36Sopenharmony_ci	struct nack_to_isp *nack;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	if (!ha->flags.fw_started)
170462306a36Sopenharmony_ci		return;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe004, "Sending NOTIFY_ACK (ha=%p)\n", ha);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	pkt = (request_t *)__qla2x00_alloc_iocbs(qpair, NULL);
170962306a36Sopenharmony_ci	if (!pkt) {
171062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe049,
171162306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
171262306a36Sopenharmony_ci		    "request packet\n", vha->vp_idx, __func__);
171362306a36Sopenharmony_ci		return;
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	if (vha->vha_tgt.qla_tgt != NULL)
171762306a36Sopenharmony_ci		vha->vha_tgt.qla_tgt->notify_ack_expected++;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	pkt->entry_type = NOTIFY_ACK_TYPE;
172062306a36Sopenharmony_ci	pkt->entry_count = 1;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	nack = (struct nack_to_isp *)pkt;
172362306a36Sopenharmony_ci	nack->ox_id = ntfy->ox_id;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	nack->u.isp24.handle = QLA_TGT_SKIP_HANDLE;
172662306a36Sopenharmony_ci	nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
172762306a36Sopenharmony_ci	if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
172862306a36Sopenharmony_ci		nack->u.isp24.flags = ntfy->u.isp24.flags &
172962306a36Sopenharmony_ci			cpu_to_le16(NOTIFY24XX_FLAGS_PUREX_IOCB);
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci	nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
173262306a36Sopenharmony_ci	nack->u.isp24.status = ntfy->u.isp24.status;
173362306a36Sopenharmony_ci	nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
173462306a36Sopenharmony_ci	nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle;
173562306a36Sopenharmony_ci	nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
173662306a36Sopenharmony_ci	nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
173762306a36Sopenharmony_ci	nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
173862306a36Sopenharmony_ci	nack->u.isp24.srr_flags = cpu_to_le16(srr_flags);
173962306a36Sopenharmony_ci	nack->u.isp24.srr_reject_code = srr_reject_code;
174062306a36Sopenharmony_ci	nack->u.isp24.srr_reject_code_expl = srr_explan;
174162306a36Sopenharmony_ci	nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	/* TODO qualify this with EDIF enable */
174462306a36Sopenharmony_ci	if (ntfy->u.isp24.status_subcode == ELS_PLOGI &&
174562306a36Sopenharmony_ci	    (le16_to_cpu(ntfy->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP)) {
174662306a36Sopenharmony_ci		nack->u.isp24.flags |= cpu_to_le16(NOTIFY_ACK_FLAGS_FCSP);
174762306a36Sopenharmony_ci	}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe005,
175062306a36Sopenharmony_ci	    "qla_target(%d): Sending 24xx Notify Ack %d\n",
175162306a36Sopenharmony_ci	    vha->vp_idx, nack->u.isp24.status);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	/* Memory Barrier */
175462306a36Sopenharmony_ci	wmb();
175562306a36Sopenharmony_ci	qla2x00_start_iocbs(vha, qpair->req);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic int qlt_build_abts_resp_iocb(struct qla_tgt_mgmt_cmd *mcmd)
175962306a36Sopenharmony_ci{
176062306a36Sopenharmony_ci	struct scsi_qla_host *vha = mcmd->vha;
176162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
176262306a36Sopenharmony_ci	struct abts_resp_to_24xx *resp;
176362306a36Sopenharmony_ci	__le32 f_ctl;
176462306a36Sopenharmony_ci	uint32_t h;
176562306a36Sopenharmony_ci	uint8_t *p;
176662306a36Sopenharmony_ci	int rc;
176762306a36Sopenharmony_ci	struct abts_recv_from_24xx *abts = &mcmd->orig_iocb.abts;
176862306a36Sopenharmony_ci	struct qla_qpair *qpair = mcmd->qpair;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe006,
177162306a36Sopenharmony_ci	    "Sending task mgmt ABTS response (ha=%p, status=%x)\n",
177262306a36Sopenharmony_ci	    ha, mcmd->fc_tm_rsp);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	rc = qlt_check_reserve_free_req(qpair, 1);
177562306a36Sopenharmony_ci	if (rc) {
177662306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe04a,
177762306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate request packet\n",
177862306a36Sopenharmony_ci		    vha->vp_idx, __func__);
177962306a36Sopenharmony_ci		return -EAGAIN;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	resp = (struct abts_resp_to_24xx *)qpair->req->ring_ptr;
178362306a36Sopenharmony_ci	memset(resp, 0, sizeof(*resp));
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	h = qlt_make_handle(qpair);
178662306a36Sopenharmony_ci	if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
178762306a36Sopenharmony_ci		/*
178862306a36Sopenharmony_ci		 * CTIO type 7 from the firmware doesn't provide a way to
178962306a36Sopenharmony_ci		 * know the initiator's LOOP ID, hence we can't find
179062306a36Sopenharmony_ci		 * the session and, so, the command.
179162306a36Sopenharmony_ci		 */
179262306a36Sopenharmony_ci		return -EAGAIN;
179362306a36Sopenharmony_ci	} else {
179462306a36Sopenharmony_ci		qpair->req->outstanding_cmds[h] = (srb_t *)mcmd;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	resp->handle = make_handle(qpair->req->id, h);
179862306a36Sopenharmony_ci	resp->entry_type = ABTS_RESP_24XX;
179962306a36Sopenharmony_ci	resp->entry_count = 1;
180062306a36Sopenharmony_ci	resp->nport_handle = abts->nport_handle;
180162306a36Sopenharmony_ci	resp->vp_index = vha->vp_idx;
180262306a36Sopenharmony_ci	resp->sof_type = abts->sof_type;
180362306a36Sopenharmony_ci	resp->exchange_address = abts->exchange_address;
180462306a36Sopenharmony_ci	resp->fcp_hdr_le = abts->fcp_hdr_le;
180562306a36Sopenharmony_ci	f_ctl = cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP |
180662306a36Sopenharmony_ci	    F_CTL_LAST_SEQ | F_CTL_END_SEQ |
180762306a36Sopenharmony_ci	    F_CTL_SEQ_INITIATIVE);
180862306a36Sopenharmony_ci	p = (uint8_t *)&f_ctl;
180962306a36Sopenharmony_ci	resp->fcp_hdr_le.f_ctl[0] = *p++;
181062306a36Sopenharmony_ci	resp->fcp_hdr_le.f_ctl[1] = *p++;
181162306a36Sopenharmony_ci	resp->fcp_hdr_le.f_ctl[2] = *p;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	resp->fcp_hdr_le.d_id = abts->fcp_hdr_le.s_id;
181462306a36Sopenharmony_ci	resp->fcp_hdr_le.s_id = abts->fcp_hdr_le.d_id;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	resp->exchange_addr_to_abort = abts->exchange_addr_to_abort;
181762306a36Sopenharmony_ci	if (mcmd->fc_tm_rsp == FCP_TMF_CMPL) {
181862306a36Sopenharmony_ci		resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC;
181962306a36Sopenharmony_ci		resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID;
182062306a36Sopenharmony_ci		resp->payload.ba_acct.low_seq_cnt = 0x0000;
182162306a36Sopenharmony_ci		resp->payload.ba_acct.high_seq_cnt = cpu_to_le16(0xFFFF);
182262306a36Sopenharmony_ci		resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id;
182362306a36Sopenharmony_ci		resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id;
182462306a36Sopenharmony_ci	} else {
182562306a36Sopenharmony_ci		resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT;
182662306a36Sopenharmony_ci		resp->payload.ba_rjt.reason_code =
182762306a36Sopenharmony_ci			BA_RJT_REASON_CODE_UNABLE_TO_PERFORM;
182862306a36Sopenharmony_ci		/* Other bytes are zero */
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	vha->vha_tgt.qla_tgt->abts_resp_expected++;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	/* Memory Barrier */
183462306a36Sopenharmony_ci	wmb();
183562306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
183662306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
183762306a36Sopenharmony_ci	else
183862306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	return rc;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci/*
184462306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
184562306a36Sopenharmony_ci */
184662306a36Sopenharmony_cistatic void qlt_24xx_send_abts_resp(struct qla_qpair *qpair,
184762306a36Sopenharmony_ci	struct abts_recv_from_24xx *abts, uint32_t status,
184862306a36Sopenharmony_ci	bool ids_reversed)
184962306a36Sopenharmony_ci{
185062306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
185162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
185262306a36Sopenharmony_ci	struct abts_resp_to_24xx *resp;
185362306a36Sopenharmony_ci	__le32 f_ctl;
185462306a36Sopenharmony_ci	uint8_t *p;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe006,
185762306a36Sopenharmony_ci	    "Sending task mgmt ABTS response (ha=%p, atio=%p, status=%x\n",
185862306a36Sopenharmony_ci	    ha, abts, status);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	resp = (struct abts_resp_to_24xx *)qla2x00_alloc_iocbs_ready(qpair,
186162306a36Sopenharmony_ci	    NULL);
186262306a36Sopenharmony_ci	if (!resp) {
186362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe04a,
186462306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
186562306a36Sopenharmony_ci		    "request packet", vha->vp_idx, __func__);
186662306a36Sopenharmony_ci		return;
186762306a36Sopenharmony_ci	}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	resp->entry_type = ABTS_RESP_24XX;
187062306a36Sopenharmony_ci	resp->handle = QLA_TGT_SKIP_HANDLE;
187162306a36Sopenharmony_ci	resp->entry_count = 1;
187262306a36Sopenharmony_ci	resp->nport_handle = abts->nport_handle;
187362306a36Sopenharmony_ci	resp->vp_index = vha->vp_idx;
187462306a36Sopenharmony_ci	resp->sof_type = abts->sof_type;
187562306a36Sopenharmony_ci	resp->exchange_address = abts->exchange_address;
187662306a36Sopenharmony_ci	resp->fcp_hdr_le = abts->fcp_hdr_le;
187762306a36Sopenharmony_ci	f_ctl = cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP |
187862306a36Sopenharmony_ci	    F_CTL_LAST_SEQ | F_CTL_END_SEQ |
187962306a36Sopenharmony_ci	    F_CTL_SEQ_INITIATIVE);
188062306a36Sopenharmony_ci	p = (uint8_t *)&f_ctl;
188162306a36Sopenharmony_ci	resp->fcp_hdr_le.f_ctl[0] = *p++;
188262306a36Sopenharmony_ci	resp->fcp_hdr_le.f_ctl[1] = *p++;
188362306a36Sopenharmony_ci	resp->fcp_hdr_le.f_ctl[2] = *p;
188462306a36Sopenharmony_ci	if (ids_reversed) {
188562306a36Sopenharmony_ci		resp->fcp_hdr_le.d_id = abts->fcp_hdr_le.d_id;
188662306a36Sopenharmony_ci		resp->fcp_hdr_le.s_id = abts->fcp_hdr_le.s_id;
188762306a36Sopenharmony_ci	} else {
188862306a36Sopenharmony_ci		resp->fcp_hdr_le.d_id = abts->fcp_hdr_le.s_id;
188962306a36Sopenharmony_ci		resp->fcp_hdr_le.s_id = abts->fcp_hdr_le.d_id;
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci	resp->exchange_addr_to_abort = abts->exchange_addr_to_abort;
189262306a36Sopenharmony_ci	if (status == FCP_TMF_CMPL) {
189362306a36Sopenharmony_ci		resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC;
189462306a36Sopenharmony_ci		resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID;
189562306a36Sopenharmony_ci		resp->payload.ba_acct.low_seq_cnt = 0x0000;
189662306a36Sopenharmony_ci		resp->payload.ba_acct.high_seq_cnt = cpu_to_le16(0xFFFF);
189762306a36Sopenharmony_ci		resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id;
189862306a36Sopenharmony_ci		resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id;
189962306a36Sopenharmony_ci	} else {
190062306a36Sopenharmony_ci		resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT;
190162306a36Sopenharmony_ci		resp->payload.ba_rjt.reason_code =
190262306a36Sopenharmony_ci			BA_RJT_REASON_CODE_UNABLE_TO_PERFORM;
190362306a36Sopenharmony_ci		/* Other bytes are zero */
190462306a36Sopenharmony_ci	}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	vha->vha_tgt.qla_tgt->abts_resp_expected++;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	/* Memory Barrier */
190962306a36Sopenharmony_ci	wmb();
191062306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
191162306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
191262306a36Sopenharmony_ci	else
191362306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
191462306a36Sopenharmony_ci}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci/*
191762306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
191862306a36Sopenharmony_ci */
191962306a36Sopenharmony_cistatic void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha,
192062306a36Sopenharmony_ci    struct qla_qpair *qpair, response_t *pkt, struct qla_tgt_mgmt_cmd *mcmd)
192162306a36Sopenharmony_ci{
192262306a36Sopenharmony_ci	struct ctio7_to_24xx *ctio;
192362306a36Sopenharmony_ci	u16 tmp;
192462306a36Sopenharmony_ci	struct abts_recv_from_24xx *entry;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs_ready(qpair, NULL);
192762306a36Sopenharmony_ci	if (ctio == NULL) {
192862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe04b,
192962306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
193062306a36Sopenharmony_ci		    "request packet\n", vha->vp_idx, __func__);
193162306a36Sopenharmony_ci		return;
193262306a36Sopenharmony_ci	}
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (mcmd)
193562306a36Sopenharmony_ci		/* abts from remote port */
193662306a36Sopenharmony_ci		entry = &mcmd->orig_iocb.abts;
193762306a36Sopenharmony_ci	else
193862306a36Sopenharmony_ci		/* abts from this driver.  */
193962306a36Sopenharmony_ci		entry = (struct abts_recv_from_24xx *)pkt;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/*
194262306a36Sopenharmony_ci	 * We've got on entrance firmware's response on by us generated
194362306a36Sopenharmony_ci	 * ABTS response. So, in it ID fields are reversed.
194462306a36Sopenharmony_ci	 */
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	ctio->entry_type = CTIO_TYPE7;
194762306a36Sopenharmony_ci	ctio->entry_count = 1;
194862306a36Sopenharmony_ci	ctio->nport_handle = entry->nport_handle;
194962306a36Sopenharmony_ci	ctio->handle = QLA_TGT_SKIP_HANDLE |	CTIO_COMPLETION_HANDLE_MARK;
195062306a36Sopenharmony_ci	ctio->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
195162306a36Sopenharmony_ci	ctio->vp_index = vha->vp_idx;
195262306a36Sopenharmony_ci	ctio->exchange_addr = entry->exchange_addr_to_abort;
195362306a36Sopenharmony_ci	tmp = (CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	if (mcmd) {
195662306a36Sopenharmony_ci		ctio->initiator_id = entry->fcp_hdr_le.s_id;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci		if (mcmd->flags & QLA24XX_MGMT_ABORT_IO_ATTR_VALID)
195962306a36Sopenharmony_ci			tmp |= (mcmd->abort_io_attr << 9);
196062306a36Sopenharmony_ci		else if (qpair->retry_term_cnt & 1)
196162306a36Sopenharmony_ci			tmp |= (0x4 << 9);
196262306a36Sopenharmony_ci	} else {
196362306a36Sopenharmony_ci		ctio->initiator_id = entry->fcp_hdr_le.d_id;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci		if (qpair->retry_term_cnt & 1)
196662306a36Sopenharmony_ci			tmp |= (0x4 << 9);
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci	ctio->u.status1.flags = cpu_to_le16(tmp);
196962306a36Sopenharmony_ci	ctio->u.status1.ox_id = entry->fcp_hdr_le.ox_id;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe007,
197262306a36Sopenharmony_ci	    "Sending retry TERM EXCH CTIO7 flags %04xh oxid %04xh attr valid %x\n",
197362306a36Sopenharmony_ci	    le16_to_cpu(ctio->u.status1.flags),
197462306a36Sopenharmony_ci	    le16_to_cpu(ctio->u.status1.ox_id),
197562306a36Sopenharmony_ci	    (mcmd && mcmd->flags & QLA24XX_MGMT_ABORT_IO_ATTR_VALID) ? 1 : 0);
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	/* Memory Barrier */
197862306a36Sopenharmony_ci	wmb();
197962306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
198062306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
198162306a36Sopenharmony_ci	else
198262306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	if (mcmd)
198562306a36Sopenharmony_ci		qlt_build_abts_resp_iocb(mcmd);
198662306a36Sopenharmony_ci	else
198762306a36Sopenharmony_ci		qlt_24xx_send_abts_resp(qpair,
198862306a36Sopenharmony_ci		    (struct abts_recv_from_24xx *)entry, FCP_TMF_CMPL, true);
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci/* drop cmds for the given lun
199362306a36Sopenharmony_ci * XXX only looks for cmds on the port through which lun reset was recieved
199462306a36Sopenharmony_ci * XXX does not go through the list of other port (which may have cmds
199562306a36Sopenharmony_ci *     for the same lun)
199662306a36Sopenharmony_ci */
199762306a36Sopenharmony_cistatic void abort_cmds_for_lun(struct scsi_qla_host *vha, u64 lun, be_id_t s_id)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	struct qla_tgt_sess_op *op;
200062306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
200162306a36Sopenharmony_ci	uint32_t key;
200262306a36Sopenharmony_ci	unsigned long flags;
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	key = sid_to_key(s_id);
200562306a36Sopenharmony_ci	spin_lock_irqsave(&vha->cmd_list_lock, flags);
200662306a36Sopenharmony_ci	list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) {
200762306a36Sopenharmony_ci		uint32_t op_key;
200862306a36Sopenharmony_ci		u64 op_lun;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci		op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
201162306a36Sopenharmony_ci		op_lun = scsilun_to_int(
201262306a36Sopenharmony_ci			(struct scsi_lun *)&op->atio.u.isp24.fcp_cmnd.lun);
201362306a36Sopenharmony_ci		if (op_key == key && op_lun == lun)
201462306a36Sopenharmony_ci			op->aborted = true;
201562306a36Sopenharmony_ci	}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
201862306a36Sopenharmony_ci		uint32_t cmd_key;
201962306a36Sopenharmony_ci		u64 cmd_lun;
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci		cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id);
202262306a36Sopenharmony_ci		cmd_lun = scsilun_to_int(
202362306a36Sopenharmony_ci			(struct scsi_lun *)&cmd->atio.u.isp24.fcp_cmnd.lun);
202462306a36Sopenharmony_ci		if (cmd_key == key && cmd_lun == lun)
202562306a36Sopenharmony_ci			cmd->aborted = 1;
202662306a36Sopenharmony_ci	}
202762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
202862306a36Sopenharmony_ci}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_cistatic struct qla_qpair_hint *qlt_find_qphint(struct scsi_qla_host *vha,
203162306a36Sopenharmony_ci    uint64_t unpacked_lun)
203262306a36Sopenharmony_ci{
203362306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
203462306a36Sopenharmony_ci	struct qla_qpair_hint *h = NULL;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	if (vha->flags.qpairs_available) {
203762306a36Sopenharmony_ci		h = btree_lookup64(&tgt->lun_qpair_map, unpacked_lun);
203862306a36Sopenharmony_ci		if (!h)
203962306a36Sopenharmony_ci			h = &tgt->qphints[0];
204062306a36Sopenharmony_ci	} else {
204162306a36Sopenharmony_ci		h = &tgt->qphints[0];
204262306a36Sopenharmony_ci	}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	return h;
204562306a36Sopenharmony_ci}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_cistatic void qlt_do_tmr_work(struct work_struct *work)
204862306a36Sopenharmony_ci{
204962306a36Sopenharmony_ci	struct qla_tgt_mgmt_cmd *mcmd =
205062306a36Sopenharmony_ci		container_of(work, struct qla_tgt_mgmt_cmd, work);
205162306a36Sopenharmony_ci	struct qla_hw_data *ha = mcmd->vha->hw;
205262306a36Sopenharmony_ci	int rc;
205362306a36Sopenharmony_ci	uint32_t tag;
205462306a36Sopenharmony_ci	unsigned long flags;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	switch (mcmd->tmr_func) {
205762306a36Sopenharmony_ci	case QLA_TGT_ABTS:
205862306a36Sopenharmony_ci		tag = le32_to_cpu(mcmd->orig_iocb.abts.exchange_addr_to_abort);
205962306a36Sopenharmony_ci		break;
206062306a36Sopenharmony_ci	default:
206162306a36Sopenharmony_ci		tag = 0;
206262306a36Sopenharmony_ci		break;
206362306a36Sopenharmony_ci	}
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	rc = ha->tgt.tgt_ops->handle_tmr(mcmd, mcmd->unpacked_lun,
206662306a36Sopenharmony_ci	    mcmd->tmr_func, tag);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	if (rc != 0) {
206962306a36Sopenharmony_ci		spin_lock_irqsave(mcmd->qpair->qp_lock_ptr, flags);
207062306a36Sopenharmony_ci		switch (mcmd->tmr_func) {
207162306a36Sopenharmony_ci		case QLA_TGT_ABTS:
207262306a36Sopenharmony_ci			mcmd->fc_tm_rsp = FCP_TMF_REJECTED;
207362306a36Sopenharmony_ci			qlt_build_abts_resp_iocb(mcmd);
207462306a36Sopenharmony_ci			break;
207562306a36Sopenharmony_ci		case QLA_TGT_LUN_RESET:
207662306a36Sopenharmony_ci		case QLA_TGT_CLEAR_TS:
207762306a36Sopenharmony_ci		case QLA_TGT_ABORT_TS:
207862306a36Sopenharmony_ci		case QLA_TGT_CLEAR_ACA:
207962306a36Sopenharmony_ci		case QLA_TGT_TARGET_RESET:
208062306a36Sopenharmony_ci			qlt_send_busy(mcmd->qpair, &mcmd->orig_iocb.atio,
208162306a36Sopenharmony_ci			    qla_sam_status);
208262306a36Sopenharmony_ci			break;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci		case QLA_TGT_ABORT_ALL:
208562306a36Sopenharmony_ci		case QLA_TGT_NEXUS_LOSS_SESS:
208662306a36Sopenharmony_ci		case QLA_TGT_NEXUS_LOSS:
208762306a36Sopenharmony_ci			qlt_send_notify_ack(mcmd->qpair,
208862306a36Sopenharmony_ci			    &mcmd->orig_iocb.imm_ntfy, 0, 0, 0, 0, 0, 0);
208962306a36Sopenharmony_ci			break;
209062306a36Sopenharmony_ci		}
209162306a36Sopenharmony_ci		spin_unlock_irqrestore(mcmd->qpair->qp_lock_ptr, flags);
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, mcmd->vha, 0xf052,
209462306a36Sopenharmony_ci		    "qla_target(%d):  tgt_ops->handle_tmr() failed: %d\n",
209562306a36Sopenharmony_ci		    mcmd->vha->vp_idx, rc);
209662306a36Sopenharmony_ci		mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
210162306a36Sopenharmony_cistatic int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
210262306a36Sopenharmony_ci	struct abts_recv_from_24xx *abts, struct fc_port *sess)
210362306a36Sopenharmony_ci{
210462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
210562306a36Sopenharmony_ci	struct qla_tgt_mgmt_cmd *mcmd;
210662306a36Sopenharmony_ci	struct qla_qpair_hint *h = &vha->vha_tgt.qla_tgt->qphints[0];
210762306a36Sopenharmony_ci	struct qla_tgt_cmd *abort_cmd;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f,
211062306a36Sopenharmony_ci	    "qla_target(%d): task abort (tag=%d)\n",
211162306a36Sopenharmony_ci	    vha->vp_idx, abts->exchange_addr_to_abort);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
211462306a36Sopenharmony_ci	if (mcmd == NULL) {
211562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf051,
211662306a36Sopenharmony_ci		    "qla_target(%d): %s: Allocation of ABORT cmd failed",
211762306a36Sopenharmony_ci		    vha->vp_idx, __func__);
211862306a36Sopenharmony_ci		return -ENOMEM;
211962306a36Sopenharmony_ci	}
212062306a36Sopenharmony_ci	memset(mcmd, 0, sizeof(*mcmd));
212162306a36Sopenharmony_ci	mcmd->cmd_type = TYPE_TGT_TMCMD;
212262306a36Sopenharmony_ci	mcmd->sess = sess;
212362306a36Sopenharmony_ci	memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
212462306a36Sopenharmony_ci	mcmd->reset_count = ha->base_qpair->chip_reset;
212562306a36Sopenharmony_ci	mcmd->tmr_func = QLA_TGT_ABTS;
212662306a36Sopenharmony_ci	mcmd->qpair = h->qpair;
212762306a36Sopenharmony_ci	mcmd->vha = vha;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	/*
213062306a36Sopenharmony_ci	 * LUN is looked up by target-core internally based on the passed
213162306a36Sopenharmony_ci	 * abts->exchange_addr_to_abort tag.
213262306a36Sopenharmony_ci	 */
213362306a36Sopenharmony_ci	mcmd->se_cmd.cpuid = h->cpuid;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	abort_cmd = ha->tgt.tgt_ops->find_cmd_by_tag(sess,
213662306a36Sopenharmony_ci				le32_to_cpu(abts->exchange_addr_to_abort));
213762306a36Sopenharmony_ci	if (!abort_cmd) {
213862306a36Sopenharmony_ci		mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
213962306a36Sopenharmony_ci		return -EIO;
214062306a36Sopenharmony_ci	}
214162306a36Sopenharmony_ci	mcmd->unpacked_lun = abort_cmd->se_cmd.orig_fe_lun;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	if (abort_cmd->qpair) {
214462306a36Sopenharmony_ci		mcmd->qpair = abort_cmd->qpair;
214562306a36Sopenharmony_ci		mcmd->se_cmd.cpuid = abort_cmd->se_cmd.cpuid;
214662306a36Sopenharmony_ci		mcmd->abort_io_attr = abort_cmd->atio.u.isp24.attr;
214762306a36Sopenharmony_ci		mcmd->flags = QLA24XX_MGMT_ABORT_IO_ATTR_VALID;
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	INIT_WORK(&mcmd->work, qlt_do_tmr_work);
215162306a36Sopenharmony_ci	queue_work_on(mcmd->se_cmd.cpuid, qla_tgt_wq, &mcmd->work);
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	return 0;
215462306a36Sopenharmony_ci}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci/*
215762306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
215862306a36Sopenharmony_ci */
215962306a36Sopenharmony_cistatic void qlt_24xx_handle_abts(struct scsi_qla_host *vha,
216062306a36Sopenharmony_ci	struct abts_recv_from_24xx *abts)
216162306a36Sopenharmony_ci{
216262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
216362306a36Sopenharmony_ci	struct fc_port *sess;
216462306a36Sopenharmony_ci	uint32_t tag = le32_to_cpu(abts->exchange_addr_to_abort);
216562306a36Sopenharmony_ci	be_id_t s_id;
216662306a36Sopenharmony_ci	int rc;
216762306a36Sopenharmony_ci	unsigned long flags;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) {
217062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf053,
217162306a36Sopenharmony_ci		    "qla_target(%d): ABTS: Abort Sequence not "
217262306a36Sopenharmony_ci		    "supported\n", vha->vp_idx);
217362306a36Sopenharmony_ci		qlt_24xx_send_abts_resp(ha->base_qpair, abts, FCP_TMF_REJECTED,
217462306a36Sopenharmony_ci		    false);
217562306a36Sopenharmony_ci		return;
217662306a36Sopenharmony_ci	}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
217962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf010,
218062306a36Sopenharmony_ci		    "qla_target(%d): ABTS: Unknown Exchange "
218162306a36Sopenharmony_ci		    "Address received\n", vha->vp_idx);
218262306a36Sopenharmony_ci		qlt_24xx_send_abts_resp(ha->base_qpair, abts, FCP_TMF_REJECTED,
218362306a36Sopenharmony_ci		    false);
218462306a36Sopenharmony_ci		return;
218562306a36Sopenharmony_ci	}
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf011,
218862306a36Sopenharmony_ci	    "qla_target(%d): task abort (s_id=%x:%x:%x, "
218962306a36Sopenharmony_ci	    "tag=%d, param=%x)\n", vha->vp_idx, abts->fcp_hdr_le.s_id.domain,
219062306a36Sopenharmony_ci	    abts->fcp_hdr_le.s_id.area, abts->fcp_hdr_le.s_id.al_pa, tag,
219162306a36Sopenharmony_ci	    le32_to_cpu(abts->fcp_hdr_le.parameter));
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	s_id = le_id_to_be(abts->fcp_hdr_le.s_id);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
219662306a36Sopenharmony_ci	sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
219762306a36Sopenharmony_ci	if (!sess) {
219862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf012,
219962306a36Sopenharmony_ci		    "qla_target(%d): task abort for non-existent session\n",
220062306a36Sopenharmony_ci		    vha->vp_idx);
220162306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci		qlt_24xx_send_abts_resp(ha->base_qpair, abts, FCP_TMF_REJECTED,
220462306a36Sopenharmony_ci			    false);
220562306a36Sopenharmony_ci		return;
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	if (sess->deleted) {
221162306a36Sopenharmony_ci		qlt_24xx_send_abts_resp(ha->base_qpair, abts, FCP_TMF_REJECTED,
221262306a36Sopenharmony_ci		    false);
221362306a36Sopenharmony_ci		return;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	rc = __qlt_24xx_handle_abts(vha, abts, sess);
221762306a36Sopenharmony_ci	if (rc != 0) {
221862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054,
221962306a36Sopenharmony_ci		    "qla_target(%d): __qlt_24xx_handle_abts() failed: %d\n",
222062306a36Sopenharmony_ci		    vha->vp_idx, rc);
222162306a36Sopenharmony_ci		qlt_24xx_send_abts_resp(ha->base_qpair, abts, FCP_TMF_REJECTED,
222262306a36Sopenharmony_ci		    false);
222362306a36Sopenharmony_ci		return;
222462306a36Sopenharmony_ci	}
222562306a36Sopenharmony_ci}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci/*
222862306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
222962306a36Sopenharmony_ci */
223062306a36Sopenharmony_cistatic void qlt_24xx_send_task_mgmt_ctio(struct qla_qpair *qpair,
223162306a36Sopenharmony_ci	struct qla_tgt_mgmt_cmd *mcmd, uint32_t resp_code)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	struct scsi_qla_host *ha = mcmd->vha;
223462306a36Sopenharmony_ci	struct atio_from_isp *atio = &mcmd->orig_iocb.atio;
223562306a36Sopenharmony_ci	struct ctio7_to_24xx *ctio;
223662306a36Sopenharmony_ci	uint16_t temp;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, ha, 0xe008,
223962306a36Sopenharmony_ci	    "Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x\n",
224062306a36Sopenharmony_ci	    ha, atio, resp_code);
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	ctio = (struct ctio7_to_24xx *)__qla2x00_alloc_iocbs(qpair, NULL);
224462306a36Sopenharmony_ci	if (ctio == NULL) {
224562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, ha, 0xe04c,
224662306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
224762306a36Sopenharmony_ci		    "request packet\n", ha->vp_idx, __func__);
224862306a36Sopenharmony_ci		return;
224962306a36Sopenharmony_ci	}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	ctio->entry_type = CTIO_TYPE7;
225262306a36Sopenharmony_ci	ctio->entry_count = 1;
225362306a36Sopenharmony_ci	ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
225462306a36Sopenharmony_ci	ctio->nport_handle = cpu_to_le16(mcmd->sess->loop_id);
225562306a36Sopenharmony_ci	ctio->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
225662306a36Sopenharmony_ci	ctio->vp_index = ha->vp_idx;
225762306a36Sopenharmony_ci	ctio->initiator_id = be_id_to_le(atio->u.isp24.fcp_hdr.s_id);
225862306a36Sopenharmony_ci	ctio->exchange_addr = atio->u.isp24.exchange_addr;
225962306a36Sopenharmony_ci	temp = (atio->u.isp24.attr << 9)|
226062306a36Sopenharmony_ci		CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS;
226162306a36Sopenharmony_ci	ctio->u.status1.flags = cpu_to_le16(temp);
226262306a36Sopenharmony_ci	temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id);
226362306a36Sopenharmony_ci	ctio->u.status1.ox_id = cpu_to_le16(temp);
226462306a36Sopenharmony_ci	ctio->u.status1.scsi_status =
226562306a36Sopenharmony_ci	    cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID);
226662306a36Sopenharmony_ci	ctio->u.status1.response_len = cpu_to_le16(8);
226762306a36Sopenharmony_ci	ctio->u.status1.sense_data[0] = resp_code;
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	/* Memory Barrier */
227062306a36Sopenharmony_ci	wmb();
227162306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
227262306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
227362306a36Sopenharmony_ci	else
227462306a36Sopenharmony_ci		qla2x00_start_iocbs(ha, qpair->req);
227562306a36Sopenharmony_ci}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_civoid qlt_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
227862306a36Sopenharmony_ci{
227962306a36Sopenharmony_ci	mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
228062306a36Sopenharmony_ci}
228162306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_free_mcmd);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci/*
228462306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then
228562306a36Sopenharmony_ci * reacquire
228662306a36Sopenharmony_ci */
228762306a36Sopenharmony_civoid qlt_send_resp_ctio(struct qla_qpair *qpair, struct qla_tgt_cmd *cmd,
228862306a36Sopenharmony_ci    uint8_t scsi_status, uint8_t sense_key, uint8_t asc, uint8_t ascq)
228962306a36Sopenharmony_ci{
229062306a36Sopenharmony_ci	struct atio_from_isp *atio = &cmd->atio;
229162306a36Sopenharmony_ci	struct ctio7_to_24xx *ctio;
229262306a36Sopenharmony_ci	uint16_t temp;
229362306a36Sopenharmony_ci	struct scsi_qla_host *vha = cmd->vha;
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_dif, vha, 0x3066,
229662306a36Sopenharmony_ci	    "Sending response CTIO7 (vha=%p, atio=%p, scsi_status=%02x, "
229762306a36Sopenharmony_ci	    "sense_key=%02x, asc=%02x, ascq=%02x",
229862306a36Sopenharmony_ci	    vha, atio, scsi_status, sense_key, asc, ascq);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(vha, NULL);
230162306a36Sopenharmony_ci	if (!ctio) {
230262306a36Sopenharmony_ci		ql_dbg(ql_dbg_async, vha, 0x3067,
230362306a36Sopenharmony_ci		    "qla2x00t(%ld): %s failed: unable to allocate request packet",
230462306a36Sopenharmony_ci		    vha->host_no, __func__);
230562306a36Sopenharmony_ci		goto out;
230662306a36Sopenharmony_ci	}
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	ctio->entry_type = CTIO_TYPE7;
230962306a36Sopenharmony_ci	ctio->entry_count = 1;
231062306a36Sopenharmony_ci	ctio->handle = QLA_TGT_SKIP_HANDLE;
231162306a36Sopenharmony_ci	ctio->nport_handle = cpu_to_le16(cmd->sess->loop_id);
231262306a36Sopenharmony_ci	ctio->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
231362306a36Sopenharmony_ci	ctio->vp_index = vha->vp_idx;
231462306a36Sopenharmony_ci	ctio->initiator_id = be_id_to_le(atio->u.isp24.fcp_hdr.s_id);
231562306a36Sopenharmony_ci	ctio->exchange_addr = atio->u.isp24.exchange_addr;
231662306a36Sopenharmony_ci	temp = (atio->u.isp24.attr << 9) |
231762306a36Sopenharmony_ci	    CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS;
231862306a36Sopenharmony_ci	ctio->u.status1.flags = cpu_to_le16(temp);
231962306a36Sopenharmony_ci	temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id);
232062306a36Sopenharmony_ci	ctio->u.status1.ox_id = cpu_to_le16(temp);
232162306a36Sopenharmony_ci	ctio->u.status1.scsi_status =
232262306a36Sopenharmony_ci	    cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID | scsi_status);
232362306a36Sopenharmony_ci	ctio->u.status1.response_len = cpu_to_le16(18);
232462306a36Sopenharmony_ci	ctio->u.status1.residual = cpu_to_le32(get_datalen_for_atio(atio));
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	if (ctio->u.status1.residual != 0)
232762306a36Sopenharmony_ci		ctio->u.status1.scsi_status |=
232862306a36Sopenharmony_ci		    cpu_to_le16(SS_RESIDUAL_UNDER);
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	/* Fixed format sense data. */
233162306a36Sopenharmony_ci	ctio->u.status1.sense_data[0] = 0x70;
233262306a36Sopenharmony_ci	ctio->u.status1.sense_data[2] = sense_key;
233362306a36Sopenharmony_ci	/* Additional sense length */
233462306a36Sopenharmony_ci	ctio->u.status1.sense_data[7] = 0xa;
233562306a36Sopenharmony_ci	/* ASC and ASCQ */
233662306a36Sopenharmony_ci	ctio->u.status1.sense_data[12] = asc;
233762306a36Sopenharmony_ci	ctio->u.status1.sense_data[13] = ascq;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	/* Memory Barrier */
234062306a36Sopenharmony_ci	wmb();
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
234362306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
234462306a36Sopenharmony_ci	else
234562306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ciout:
234862306a36Sopenharmony_ci	return;
234962306a36Sopenharmony_ci}
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci/* callback from target fabric module code */
235262306a36Sopenharmony_civoid qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd)
235362306a36Sopenharmony_ci{
235462306a36Sopenharmony_ci	struct scsi_qla_host *vha = mcmd->sess->vha;
235562306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
235662306a36Sopenharmony_ci	unsigned long flags;
235762306a36Sopenharmony_ci	struct qla_qpair *qpair = mcmd->qpair;
235862306a36Sopenharmony_ci	bool free_mcmd = true;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf013,
236162306a36Sopenharmony_ci	    "TM response mcmd (%p) status %#x state %#x",
236262306a36Sopenharmony_ci	    mcmd, mcmd->fc_tm_rsp, mcmd->flags);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	if (!vha->flags.online || mcmd->reset_count != qpair->chip_reset) {
236762306a36Sopenharmony_ci		/*
236862306a36Sopenharmony_ci		 * Either the port is not online or this request was from
236962306a36Sopenharmony_ci		 * previous life, just abort the processing.
237062306a36Sopenharmony_ci		 */
237162306a36Sopenharmony_ci		ql_dbg(ql_dbg_async, vha, 0xe100,
237262306a36Sopenharmony_ci			"RESET-TMR online/active/old-count/new-count = %d/%d/%d/%d.\n",
237362306a36Sopenharmony_ci			vha->flags.online, qla2x00_reset_active(vha),
237462306a36Sopenharmony_ci			mcmd->reset_count, qpair->chip_reset);
237562306a36Sopenharmony_ci		ha->tgt.tgt_ops->free_mcmd(mcmd);
237662306a36Sopenharmony_ci		spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
237762306a36Sopenharmony_ci		return;
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	if (mcmd->flags == QLA24XX_MGMT_SEND_NACK) {
238162306a36Sopenharmony_ci		switch (mcmd->orig_iocb.imm_ntfy.u.isp24.status_subcode) {
238262306a36Sopenharmony_ci		case ELS_LOGO:
238362306a36Sopenharmony_ci		case ELS_PRLO:
238462306a36Sopenharmony_ci		case ELS_TPRLO:
238562306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0x2106,
238662306a36Sopenharmony_ci			    "TM response logo %8phC status %#x state %#x",
238762306a36Sopenharmony_ci			    mcmd->sess->port_name, mcmd->fc_tm_rsp,
238862306a36Sopenharmony_ci			    mcmd->flags);
238962306a36Sopenharmony_ci			qlt_schedule_sess_for_deletion(mcmd->sess);
239062306a36Sopenharmony_ci			break;
239162306a36Sopenharmony_ci		default:
239262306a36Sopenharmony_ci			qlt_send_notify_ack(vha->hw->base_qpair,
239362306a36Sopenharmony_ci			    &mcmd->orig_iocb.imm_ntfy, 0, 0, 0, 0, 0, 0);
239462306a36Sopenharmony_ci			break;
239562306a36Sopenharmony_ci		}
239662306a36Sopenharmony_ci	} else {
239762306a36Sopenharmony_ci		if (mcmd->orig_iocb.atio.u.raw.entry_type == ABTS_RECV_24XX) {
239862306a36Sopenharmony_ci			qlt_build_abts_resp_iocb(mcmd);
239962306a36Sopenharmony_ci			free_mcmd = false;
240062306a36Sopenharmony_ci		} else
240162306a36Sopenharmony_ci			qlt_24xx_send_task_mgmt_ctio(qpair, mcmd,
240262306a36Sopenharmony_ci			    mcmd->fc_tm_rsp);
240362306a36Sopenharmony_ci	}
240462306a36Sopenharmony_ci	/*
240562306a36Sopenharmony_ci	 * Make the callback for ->free_mcmd() to queue_work() and invoke
240662306a36Sopenharmony_ci	 * target_put_sess_cmd() to drop cmd_kref to 1.  The final
240762306a36Sopenharmony_ci	 * target_put_sess_cmd() call will be made from TFO->check_stop_free()
240862306a36Sopenharmony_ci	 * -> tcm_qla2xxx_check_stop_free() to release the TMR associated se_cmd
240962306a36Sopenharmony_ci	 * descriptor after TFO->queue_tm_rsp() -> tcm_qla2xxx_queue_tm_rsp() ->
241062306a36Sopenharmony_ci	 * qlt_xmit_tm_rsp() returns here..
241162306a36Sopenharmony_ci	 */
241262306a36Sopenharmony_ci	if (free_mcmd)
241362306a36Sopenharmony_ci		ha->tgt.tgt_ops->free_mcmd(mcmd);
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
241662306a36Sopenharmony_ci}
241762306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_xmit_tm_rsp);
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci/* No locks */
242062306a36Sopenharmony_cistatic int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm)
242162306a36Sopenharmony_ci{
242262306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd = prm->cmd;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	BUG_ON(cmd->sg_cnt == 0);
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	prm->sg = (struct scatterlist *)cmd->sg;
242762306a36Sopenharmony_ci	prm->seg_cnt = dma_map_sg(&cmd->qpair->pdev->dev, cmd->sg,
242862306a36Sopenharmony_ci	    cmd->sg_cnt, cmd->dma_data_direction);
242962306a36Sopenharmony_ci	if (unlikely(prm->seg_cnt == 0))
243062306a36Sopenharmony_ci		goto out_err;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	prm->cmd->sg_mapped = 1;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) {
243562306a36Sopenharmony_ci		/*
243662306a36Sopenharmony_ci		 * If greater than four sg entries then we need to allocate
243762306a36Sopenharmony_ci		 * the continuation entries
243862306a36Sopenharmony_ci		 */
243962306a36Sopenharmony_ci		if (prm->seg_cnt > QLA_TGT_DATASEGS_PER_CMD_24XX)
244062306a36Sopenharmony_ci			prm->req_cnt += DIV_ROUND_UP(prm->seg_cnt -
244162306a36Sopenharmony_ci			QLA_TGT_DATASEGS_PER_CMD_24XX,
244262306a36Sopenharmony_ci			QLA_TGT_DATASEGS_PER_CONT_24XX);
244362306a36Sopenharmony_ci	} else {
244462306a36Sopenharmony_ci		/* DIF */
244562306a36Sopenharmony_ci		if ((cmd->se_cmd.prot_op == TARGET_PROT_DIN_INSERT) ||
244662306a36Sopenharmony_ci		    (cmd->se_cmd.prot_op == TARGET_PROT_DOUT_STRIP)) {
244762306a36Sopenharmony_ci			prm->seg_cnt = DIV_ROUND_UP(cmd->bufflen, cmd->blk_sz);
244862306a36Sopenharmony_ci			prm->tot_dsds = prm->seg_cnt;
244962306a36Sopenharmony_ci		} else
245062306a36Sopenharmony_ci			prm->tot_dsds = prm->seg_cnt;
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci		if (cmd->prot_sg_cnt) {
245362306a36Sopenharmony_ci			prm->prot_sg      = cmd->prot_sg;
245462306a36Sopenharmony_ci			prm->prot_seg_cnt = dma_map_sg(&cmd->qpair->pdev->dev,
245562306a36Sopenharmony_ci				cmd->prot_sg, cmd->prot_sg_cnt,
245662306a36Sopenharmony_ci				cmd->dma_data_direction);
245762306a36Sopenharmony_ci			if (unlikely(prm->prot_seg_cnt == 0))
245862306a36Sopenharmony_ci				goto out_err;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci			if ((cmd->se_cmd.prot_op == TARGET_PROT_DIN_INSERT) ||
246162306a36Sopenharmony_ci			    (cmd->se_cmd.prot_op == TARGET_PROT_DOUT_STRIP)) {
246262306a36Sopenharmony_ci				/* Dif Bundling not support here */
246362306a36Sopenharmony_ci				prm->prot_seg_cnt = DIV_ROUND_UP(cmd->bufflen,
246462306a36Sopenharmony_ci								cmd->blk_sz);
246562306a36Sopenharmony_ci				prm->tot_dsds += prm->prot_seg_cnt;
246662306a36Sopenharmony_ci			} else
246762306a36Sopenharmony_ci				prm->tot_dsds += prm->prot_seg_cnt;
246862306a36Sopenharmony_ci		}
246962306a36Sopenharmony_ci	}
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	return 0;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ciout_err:
247462306a36Sopenharmony_ci	ql_dbg_qp(ql_dbg_tgt, prm->cmd->qpair, 0xe04d,
247562306a36Sopenharmony_ci	    "qla_target(%d): PCI mapping failed: sg_cnt=%d",
247662306a36Sopenharmony_ci	    0, prm->cmd->sg_cnt);
247762306a36Sopenharmony_ci	return -1;
247862306a36Sopenharmony_ci}
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_cistatic void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd)
248162306a36Sopenharmony_ci{
248262306a36Sopenharmony_ci	struct qla_hw_data *ha;
248362306a36Sopenharmony_ci	struct qla_qpair *qpair;
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	if (!cmd->sg_mapped)
248662306a36Sopenharmony_ci		return;
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	qpair = cmd->qpair;
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	dma_unmap_sg(&qpair->pdev->dev, cmd->sg, cmd->sg_cnt,
249162306a36Sopenharmony_ci	    cmd->dma_data_direction);
249262306a36Sopenharmony_ci	cmd->sg_mapped = 0;
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	if (cmd->prot_sg_cnt)
249562306a36Sopenharmony_ci		dma_unmap_sg(&qpair->pdev->dev, cmd->prot_sg, cmd->prot_sg_cnt,
249662306a36Sopenharmony_ci			cmd->dma_data_direction);
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	if (!cmd->ctx)
249962306a36Sopenharmony_ci		return;
250062306a36Sopenharmony_ci	ha = vha->hw;
250162306a36Sopenharmony_ci	if (cmd->ctx_dsd_alloced)
250262306a36Sopenharmony_ci		qla2x00_clean_dsd_pool(ha, cmd->ctx);
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	dma_pool_free(ha->dl_dma_pool, cmd->ctx, cmd->ctx->crc_ctx_dma);
250562306a36Sopenharmony_ci}
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_cistatic int qlt_check_reserve_free_req(struct qla_qpair *qpair,
250862306a36Sopenharmony_ci	uint32_t req_cnt)
250962306a36Sopenharmony_ci{
251062306a36Sopenharmony_ci	uint32_t cnt;
251162306a36Sopenharmony_ci	struct req_que *req = qpair->req;
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	if (req->cnt < (req_cnt + 2)) {
251462306a36Sopenharmony_ci		cnt = (uint16_t)(qpair->use_shadow_reg ? *req->out_ptr :
251562306a36Sopenharmony_ci		    rd_reg_dword_relaxed(req->req_q_out));
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci		if  (req->ring_index < cnt)
251862306a36Sopenharmony_ci			req->cnt = cnt - req->ring_index;
251962306a36Sopenharmony_ci		else
252062306a36Sopenharmony_ci			req->cnt = req->length - (req->ring_index - cnt);
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci		if (unlikely(req->cnt < (req_cnt + 2)))
252362306a36Sopenharmony_ci			return -EAGAIN;
252462306a36Sopenharmony_ci	}
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	req->cnt -= req_cnt;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	return 0;
252962306a36Sopenharmony_ci}
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci/*
253262306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
253362306a36Sopenharmony_ci */
253462306a36Sopenharmony_cistatic inline void *qlt_get_req_pkt(struct req_que *req)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	/* Adjust ring index. */
253762306a36Sopenharmony_ci	req->ring_index++;
253862306a36Sopenharmony_ci	if (req->ring_index == req->length) {
253962306a36Sopenharmony_ci		req->ring_index = 0;
254062306a36Sopenharmony_ci		req->ring_ptr = req->ring;
254162306a36Sopenharmony_ci	} else {
254262306a36Sopenharmony_ci		req->ring_ptr++;
254362306a36Sopenharmony_ci	}
254462306a36Sopenharmony_ci	return (cont_entry_t *)req->ring_ptr;
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
254862306a36Sopenharmony_cistatic inline uint32_t qlt_make_handle(struct qla_qpair *qpair)
254962306a36Sopenharmony_ci{
255062306a36Sopenharmony_ci	uint32_t h;
255162306a36Sopenharmony_ci	int index;
255262306a36Sopenharmony_ci	uint8_t found = 0;
255362306a36Sopenharmony_ci	struct req_que *req = qpair->req;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	h = req->current_outstanding_cmd;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	for (index = 1; index < req->num_outstanding_cmds; index++) {
255862306a36Sopenharmony_ci		h++;
255962306a36Sopenharmony_ci		if (h == req->num_outstanding_cmds)
256062306a36Sopenharmony_ci			h = 1;
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci		if (h == QLA_TGT_SKIP_HANDLE)
256362306a36Sopenharmony_ci			continue;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci		if (!req->outstanding_cmds[h]) {
256662306a36Sopenharmony_ci			found = 1;
256762306a36Sopenharmony_ci			break;
256862306a36Sopenharmony_ci		}
256962306a36Sopenharmony_ci	}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	if (found) {
257262306a36Sopenharmony_ci		req->current_outstanding_cmd = h;
257362306a36Sopenharmony_ci	} else {
257462306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, qpair->vha, 0x305b,
257562306a36Sopenharmony_ci		    "qla_target(%d): Ran out of empty cmd slots\n",
257662306a36Sopenharmony_ci		    qpair->vha->vp_idx);
257762306a36Sopenharmony_ci		h = QLA_TGT_NULL_HANDLE;
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	return h;
258162306a36Sopenharmony_ci}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
258462306a36Sopenharmony_cistatic int qlt_24xx_build_ctio_pkt(struct qla_qpair *qpair,
258562306a36Sopenharmony_ci	struct qla_tgt_prm *prm)
258662306a36Sopenharmony_ci{
258762306a36Sopenharmony_ci	uint32_t h;
258862306a36Sopenharmony_ci	struct ctio7_to_24xx *pkt;
258962306a36Sopenharmony_ci	struct atio_from_isp *atio = &prm->cmd->atio;
259062306a36Sopenharmony_ci	uint16_t temp;
259162306a36Sopenharmony_ci	struct qla_tgt_cmd      *cmd = prm->cmd;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	pkt = (struct ctio7_to_24xx *)qpair->req->ring_ptr;
259462306a36Sopenharmony_ci	prm->pkt = pkt;
259562306a36Sopenharmony_ci	memset(pkt, 0, sizeof(*pkt));
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	pkt->entry_type = CTIO_TYPE7;
259862306a36Sopenharmony_ci	pkt->entry_count = (uint8_t)prm->req_cnt;
259962306a36Sopenharmony_ci	pkt->vp_index = prm->cmd->vp_idx;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	h = qlt_make_handle(qpair);
260262306a36Sopenharmony_ci	if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
260362306a36Sopenharmony_ci		/*
260462306a36Sopenharmony_ci		 * CTIO type 7 from the firmware doesn't provide a way to
260562306a36Sopenharmony_ci		 * know the initiator's LOOP ID, hence we can't find
260662306a36Sopenharmony_ci		 * the session and, so, the command.
260762306a36Sopenharmony_ci		 */
260862306a36Sopenharmony_ci		return -EAGAIN;
260962306a36Sopenharmony_ci	} else
261062306a36Sopenharmony_ci		qpair->req->outstanding_cmds[h] = (srb_t *)prm->cmd;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	pkt->handle = make_handle(qpair->req->id, h);
261362306a36Sopenharmony_ci	pkt->handle |= CTIO_COMPLETION_HANDLE_MARK;
261462306a36Sopenharmony_ci	pkt->nport_handle = cpu_to_le16(prm->cmd->loop_id);
261562306a36Sopenharmony_ci	pkt->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
261662306a36Sopenharmony_ci	pkt->initiator_id = be_id_to_le(atio->u.isp24.fcp_hdr.s_id);
261762306a36Sopenharmony_ci	pkt->exchange_addr = atio->u.isp24.exchange_addr;
261862306a36Sopenharmony_ci	temp = atio->u.isp24.attr << 9;
261962306a36Sopenharmony_ci	pkt->u.status0.flags |= cpu_to_le16(temp);
262062306a36Sopenharmony_ci	temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id);
262162306a36Sopenharmony_ci	pkt->u.status0.ox_id = cpu_to_le16(temp);
262262306a36Sopenharmony_ci	pkt->u.status0.relative_offset = cpu_to_le32(prm->cmd->offset);
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	if (cmd->edif) {
262562306a36Sopenharmony_ci		if (cmd->dma_data_direction == DMA_TO_DEVICE)
262662306a36Sopenharmony_ci			prm->cmd->sess->edif.rx_bytes += cmd->bufflen;
262762306a36Sopenharmony_ci		if (cmd->dma_data_direction == DMA_FROM_DEVICE)
262862306a36Sopenharmony_ci			prm->cmd->sess->edif.tx_bytes += cmd->bufflen;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci		pkt->u.status0.edif_flags |= EF_EN_EDIF;
263162306a36Sopenharmony_ci	}
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	return 0;
263462306a36Sopenharmony_ci}
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci/*
263762306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. We have already made sure
263862306a36Sopenharmony_ci * that there is sufficient amount of request entries to not drop it.
263962306a36Sopenharmony_ci */
264062306a36Sopenharmony_cistatic void qlt_load_cont_data_segments(struct qla_tgt_prm *prm)
264162306a36Sopenharmony_ci{
264262306a36Sopenharmony_ci	int cnt;
264362306a36Sopenharmony_ci	struct dsd64 *cur_dsd;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	/* Build continuation packets */
264662306a36Sopenharmony_ci	while (prm->seg_cnt > 0) {
264762306a36Sopenharmony_ci		cont_a64_entry_t *cont_pkt64 =
264862306a36Sopenharmony_ci			(cont_a64_entry_t *)qlt_get_req_pkt(
264962306a36Sopenharmony_ci			   prm->cmd->qpair->req);
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci		/*
265262306a36Sopenharmony_ci		 * Make sure that from cont_pkt64 none of
265362306a36Sopenharmony_ci		 * 64-bit specific fields used for 32-bit
265462306a36Sopenharmony_ci		 * addressing. Cast to (cont_entry_t *) for
265562306a36Sopenharmony_ci		 * that.
265662306a36Sopenharmony_ci		 */
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci		memset(cont_pkt64, 0, sizeof(*cont_pkt64));
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci		cont_pkt64->entry_count = 1;
266162306a36Sopenharmony_ci		cont_pkt64->sys_define = 0;
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci		cont_pkt64->entry_type = CONTINUE_A64_TYPE;
266462306a36Sopenharmony_ci		cur_dsd = cont_pkt64->dsd;
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci		/* Load continuation entry data segments */
266762306a36Sopenharmony_ci		for (cnt = 0;
266862306a36Sopenharmony_ci		    cnt < QLA_TGT_DATASEGS_PER_CONT_24XX && prm->seg_cnt;
266962306a36Sopenharmony_ci		    cnt++, prm->seg_cnt--) {
267062306a36Sopenharmony_ci			append_dsd64(&cur_dsd, prm->sg);
267162306a36Sopenharmony_ci			prm->sg = sg_next(prm->sg);
267262306a36Sopenharmony_ci		}
267362306a36Sopenharmony_ci	}
267462306a36Sopenharmony_ci}
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci/*
267762306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. We have already made sure
267862306a36Sopenharmony_ci * that there is sufficient amount of request entries to not drop it.
267962306a36Sopenharmony_ci */
268062306a36Sopenharmony_cistatic void qlt_load_data_segments(struct qla_tgt_prm *prm)
268162306a36Sopenharmony_ci{
268262306a36Sopenharmony_ci	int cnt;
268362306a36Sopenharmony_ci	struct dsd64 *cur_dsd;
268462306a36Sopenharmony_ci	struct ctio7_to_24xx *pkt24 = (struct ctio7_to_24xx *)prm->pkt;
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci	pkt24->u.status0.transfer_length = cpu_to_le32(prm->cmd->bufflen);
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	/* Setup packet address segment pointer */
268962306a36Sopenharmony_ci	cur_dsd = &pkt24->u.status0.dsd;
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	/* Set total data segment count */
269262306a36Sopenharmony_ci	if (prm->seg_cnt)
269362306a36Sopenharmony_ci		pkt24->dseg_count = cpu_to_le16(prm->seg_cnt);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	if (prm->seg_cnt == 0) {
269662306a36Sopenharmony_ci		/* No data transfer */
269762306a36Sopenharmony_ci		cur_dsd->address = 0;
269862306a36Sopenharmony_ci		cur_dsd->length = 0;
269962306a36Sopenharmony_ci		return;
270062306a36Sopenharmony_ci	}
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	/* If scatter gather */
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	/* Load command entry data segments */
270562306a36Sopenharmony_ci	for (cnt = 0;
270662306a36Sopenharmony_ci	    (cnt < QLA_TGT_DATASEGS_PER_CMD_24XX) && prm->seg_cnt;
270762306a36Sopenharmony_ci	    cnt++, prm->seg_cnt--) {
270862306a36Sopenharmony_ci		append_dsd64(&cur_dsd, prm->sg);
270962306a36Sopenharmony_ci		prm->sg = sg_next(prm->sg);
271062306a36Sopenharmony_ci	}
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	qlt_load_cont_data_segments(prm);
271362306a36Sopenharmony_ci}
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_cistatic inline int qlt_has_data(struct qla_tgt_cmd *cmd)
271662306a36Sopenharmony_ci{
271762306a36Sopenharmony_ci	return cmd->bufflen > 0;
271862306a36Sopenharmony_ci}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_cistatic void qlt_print_dif_err(struct qla_tgt_prm *prm)
272162306a36Sopenharmony_ci{
272262306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
272362306a36Sopenharmony_ci	struct scsi_qla_host *vha;
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	/* asc 0x10=dif error */
272662306a36Sopenharmony_ci	if (prm->sense_buffer && (prm->sense_buffer[12] == 0x10)) {
272762306a36Sopenharmony_ci		cmd = prm->cmd;
272862306a36Sopenharmony_ci		vha = cmd->vha;
272962306a36Sopenharmony_ci		/* ASCQ */
273062306a36Sopenharmony_ci		switch (prm->sense_buffer[13]) {
273162306a36Sopenharmony_ci		case 1:
273262306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_dif, vha, 0xe00b,
273362306a36Sopenharmony_ci			    "BE detected Guard TAG ERR: lba[0x%llx|%lld] len[0x%x] "
273462306a36Sopenharmony_ci			    "se_cmd=%p tag[%x]",
273562306a36Sopenharmony_ci			    cmd->lba, cmd->lba, cmd->num_blks, &cmd->se_cmd,
273662306a36Sopenharmony_ci			    cmd->atio.u.isp24.exchange_addr);
273762306a36Sopenharmony_ci			break;
273862306a36Sopenharmony_ci		case 2:
273962306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_dif, vha, 0xe00c,
274062306a36Sopenharmony_ci			    "BE detected APP TAG ERR: lba[0x%llx|%lld] len[0x%x] "
274162306a36Sopenharmony_ci			    "se_cmd=%p tag[%x]",
274262306a36Sopenharmony_ci			    cmd->lba, cmd->lba, cmd->num_blks, &cmd->se_cmd,
274362306a36Sopenharmony_ci			    cmd->atio.u.isp24.exchange_addr);
274462306a36Sopenharmony_ci			break;
274562306a36Sopenharmony_ci		case 3:
274662306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_dif, vha, 0xe00f,
274762306a36Sopenharmony_ci			    "BE detected REF TAG ERR: lba[0x%llx|%lld] len[0x%x] "
274862306a36Sopenharmony_ci			    "se_cmd=%p tag[%x]",
274962306a36Sopenharmony_ci			    cmd->lba, cmd->lba, cmd->num_blks, &cmd->se_cmd,
275062306a36Sopenharmony_ci			    cmd->atio.u.isp24.exchange_addr);
275162306a36Sopenharmony_ci			break;
275262306a36Sopenharmony_ci		default:
275362306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_dif, vha, 0xe010,
275462306a36Sopenharmony_ci			    "BE detected Dif ERR: lba[%llx|%lld] len[%x] "
275562306a36Sopenharmony_ci			    "se_cmd=%p tag[%x]",
275662306a36Sopenharmony_ci			    cmd->lba, cmd->lba, cmd->num_blks, &cmd->se_cmd,
275762306a36Sopenharmony_ci			    cmd->atio.u.isp24.exchange_addr);
275862306a36Sopenharmony_ci			break;
275962306a36Sopenharmony_ci		}
276062306a36Sopenharmony_ci		ql_dump_buffer(ql_dbg_tgt_dif, vha, 0xe011, cmd->cdb, 16);
276162306a36Sopenharmony_ci	}
276262306a36Sopenharmony_ci}
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci/*
276562306a36Sopenharmony_ci * Called without ha->hardware_lock held
276662306a36Sopenharmony_ci */
276762306a36Sopenharmony_cistatic int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd,
276862306a36Sopenharmony_ci	struct qla_tgt_prm *prm, int xmit_type, uint8_t scsi_status,
276962306a36Sopenharmony_ci	uint32_t *full_req_cnt)
277062306a36Sopenharmony_ci{
277162306a36Sopenharmony_ci	struct se_cmd *se_cmd = &cmd->se_cmd;
277262306a36Sopenharmony_ci	struct qla_qpair *qpair = cmd->qpair;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	prm->cmd = cmd;
277562306a36Sopenharmony_ci	prm->tgt = cmd->tgt;
277662306a36Sopenharmony_ci	prm->pkt = NULL;
277762306a36Sopenharmony_ci	prm->rq_result = scsi_status;
277862306a36Sopenharmony_ci	prm->sense_buffer = &cmd->sense_buffer[0];
277962306a36Sopenharmony_ci	prm->sense_buffer_len = TRANSPORT_SENSE_BUFFER;
278062306a36Sopenharmony_ci	prm->sg = NULL;
278162306a36Sopenharmony_ci	prm->seg_cnt = -1;
278262306a36Sopenharmony_ci	prm->req_cnt = 1;
278362306a36Sopenharmony_ci	prm->residual = 0;
278462306a36Sopenharmony_ci	prm->add_status_pkt = 0;
278562306a36Sopenharmony_ci	prm->prot_sg = NULL;
278662306a36Sopenharmony_ci	prm->prot_seg_cnt = 0;
278762306a36Sopenharmony_ci	prm->tot_dsds = 0;
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	if ((xmit_type & QLA_TGT_XMIT_DATA) && qlt_has_data(cmd)) {
279062306a36Sopenharmony_ci		if  (qlt_pci_map_calc_cnt(prm) != 0)
279162306a36Sopenharmony_ci			return -EAGAIN;
279262306a36Sopenharmony_ci	}
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	*full_req_cnt = prm->req_cnt;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
279762306a36Sopenharmony_ci		prm->residual = se_cmd->residual_count;
279862306a36Sopenharmony_ci		ql_dbg_qp(ql_dbg_io + ql_dbg_verbose, qpair, 0x305c,
279962306a36Sopenharmony_ci		    "Residual underflow: %d (tag %lld, op %x, bufflen %d, rq_result %x)\n",
280062306a36Sopenharmony_ci		       prm->residual, se_cmd->tag,
280162306a36Sopenharmony_ci		       se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0,
280262306a36Sopenharmony_ci		       cmd->bufflen, prm->rq_result);
280362306a36Sopenharmony_ci		prm->rq_result |= SS_RESIDUAL_UNDER;
280462306a36Sopenharmony_ci	} else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
280562306a36Sopenharmony_ci		prm->residual = se_cmd->residual_count;
280662306a36Sopenharmony_ci		ql_dbg_qp(ql_dbg_io, qpair, 0x305d,
280762306a36Sopenharmony_ci		    "Residual overflow: %d (tag %lld, op %x, bufflen %d, rq_result %x)\n",
280862306a36Sopenharmony_ci		       prm->residual, se_cmd->tag, se_cmd->t_task_cdb ?
280962306a36Sopenharmony_ci		       se_cmd->t_task_cdb[0] : 0, cmd->bufflen, prm->rq_result);
281062306a36Sopenharmony_ci		prm->rq_result |= SS_RESIDUAL_OVER;
281162306a36Sopenharmony_ci	}
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	if (xmit_type & QLA_TGT_XMIT_STATUS) {
281462306a36Sopenharmony_ci		/*
281562306a36Sopenharmony_ci		 * If QLA_TGT_XMIT_DATA is not set, add_status_pkt will be
281662306a36Sopenharmony_ci		 * ignored in *xmit_response() below
281762306a36Sopenharmony_ci		 */
281862306a36Sopenharmony_ci		if (qlt_has_data(cmd)) {
281962306a36Sopenharmony_ci			if (QLA_TGT_SENSE_VALID(prm->sense_buffer) ||
282062306a36Sopenharmony_ci			    (IS_FWI2_CAPABLE(cmd->vha->hw) &&
282162306a36Sopenharmony_ci			    (prm->rq_result != 0))) {
282262306a36Sopenharmony_ci				prm->add_status_pkt = 1;
282362306a36Sopenharmony_ci				(*full_req_cnt)++;
282462306a36Sopenharmony_ci			}
282562306a36Sopenharmony_ci		}
282662306a36Sopenharmony_ci	}
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	return 0;
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_cistatic inline int qlt_need_explicit_conf(struct qla_tgt_cmd *cmd,
283262306a36Sopenharmony_ci    int sending_sense)
283362306a36Sopenharmony_ci{
283462306a36Sopenharmony_ci	if (cmd->qpair->enable_class_2)
283562306a36Sopenharmony_ci		return 0;
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci	if (sending_sense)
283862306a36Sopenharmony_ci		return cmd->conf_compl_supported;
283962306a36Sopenharmony_ci	else
284062306a36Sopenharmony_ci		return cmd->qpair->enable_explicit_conf &&
284162306a36Sopenharmony_ci                    cmd->conf_compl_supported;
284262306a36Sopenharmony_ci}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_cistatic void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio,
284562306a36Sopenharmony_ci	struct qla_tgt_prm *prm)
284662306a36Sopenharmony_ci{
284762306a36Sopenharmony_ci	prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len,
284862306a36Sopenharmony_ci	    (uint32_t)sizeof(ctio->u.status1.sense_data));
284962306a36Sopenharmony_ci	ctio->u.status0.flags |= cpu_to_le16(CTIO7_FLAGS_SEND_STATUS);
285062306a36Sopenharmony_ci	if (qlt_need_explicit_conf(prm->cmd, 0)) {
285162306a36Sopenharmony_ci		ctio->u.status0.flags |= cpu_to_le16(
285262306a36Sopenharmony_ci		    CTIO7_FLAGS_EXPLICIT_CONFORM |
285362306a36Sopenharmony_ci		    CTIO7_FLAGS_CONFORM_REQ);
285462306a36Sopenharmony_ci	}
285562306a36Sopenharmony_ci	ctio->u.status0.residual = cpu_to_le32(prm->residual);
285662306a36Sopenharmony_ci	ctio->u.status0.scsi_status = cpu_to_le16(prm->rq_result);
285762306a36Sopenharmony_ci	if (QLA_TGT_SENSE_VALID(prm->sense_buffer)) {
285862306a36Sopenharmony_ci		int i;
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci		if (qlt_need_explicit_conf(prm->cmd, 1)) {
286162306a36Sopenharmony_ci			if ((prm->rq_result & SS_SCSI_STATUS_BYTE) != 0) {
286262306a36Sopenharmony_ci				ql_dbg_qp(ql_dbg_tgt, prm->cmd->qpair, 0xe017,
286362306a36Sopenharmony_ci				    "Skipping EXPLICIT_CONFORM and "
286462306a36Sopenharmony_ci				    "CTIO7_FLAGS_CONFORM_REQ for FCP READ w/ "
286562306a36Sopenharmony_ci				    "non GOOD status\n");
286662306a36Sopenharmony_ci				goto skip_explict_conf;
286762306a36Sopenharmony_ci			}
286862306a36Sopenharmony_ci			ctio->u.status1.flags |= cpu_to_le16(
286962306a36Sopenharmony_ci			    CTIO7_FLAGS_EXPLICIT_CONFORM |
287062306a36Sopenharmony_ci			    CTIO7_FLAGS_CONFORM_REQ);
287162306a36Sopenharmony_ci		}
287262306a36Sopenharmony_ciskip_explict_conf:
287362306a36Sopenharmony_ci		ctio->u.status1.flags &=
287462306a36Sopenharmony_ci		    ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
287562306a36Sopenharmony_ci		ctio->u.status1.flags |=
287662306a36Sopenharmony_ci		    cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
287762306a36Sopenharmony_ci		ctio->u.status1.scsi_status |=
287862306a36Sopenharmony_ci		    cpu_to_le16(SS_SENSE_LEN_VALID);
287962306a36Sopenharmony_ci		ctio->u.status1.sense_length =
288062306a36Sopenharmony_ci		    cpu_to_le16(prm->sense_buffer_len);
288162306a36Sopenharmony_ci		for (i = 0; i < prm->sense_buffer_len/4; i++) {
288262306a36Sopenharmony_ci			uint32_t v;
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci			v = get_unaligned_be32(
288562306a36Sopenharmony_ci					&((uint32_t *)prm->sense_buffer)[i]);
288662306a36Sopenharmony_ci			put_unaligned_le32(v,
288762306a36Sopenharmony_ci				&((uint32_t *)ctio->u.status1.sense_data)[i]);
288862306a36Sopenharmony_ci		}
288962306a36Sopenharmony_ci		qlt_print_dif_err(prm);
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci	} else {
289262306a36Sopenharmony_ci		ctio->u.status1.flags &=
289362306a36Sopenharmony_ci		    ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0);
289462306a36Sopenharmony_ci		ctio->u.status1.flags |=
289562306a36Sopenharmony_ci		    cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1);
289662306a36Sopenharmony_ci		ctio->u.status1.sense_length = 0;
289762306a36Sopenharmony_ci		memset(ctio->u.status1.sense_data, 0,
289862306a36Sopenharmony_ci		    sizeof(ctio->u.status1.sense_data));
289962306a36Sopenharmony_ci	}
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	/* Sense with len > 24, is it possible ??? */
290262306a36Sopenharmony_ci}
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_cistatic inline int
290562306a36Sopenharmony_ciqlt_hba_err_chk_enabled(struct se_cmd *se_cmd)
290662306a36Sopenharmony_ci{
290762306a36Sopenharmony_ci	switch (se_cmd->prot_op) {
290862306a36Sopenharmony_ci	case TARGET_PROT_DOUT_INSERT:
290962306a36Sopenharmony_ci	case TARGET_PROT_DIN_STRIP:
291062306a36Sopenharmony_ci		if (ql2xenablehba_err_chk >= 1)
291162306a36Sopenharmony_ci			return 1;
291262306a36Sopenharmony_ci		break;
291362306a36Sopenharmony_ci	case TARGET_PROT_DOUT_PASS:
291462306a36Sopenharmony_ci	case TARGET_PROT_DIN_PASS:
291562306a36Sopenharmony_ci		if (ql2xenablehba_err_chk >= 2)
291662306a36Sopenharmony_ci			return 1;
291762306a36Sopenharmony_ci		break;
291862306a36Sopenharmony_ci	case TARGET_PROT_DIN_INSERT:
291962306a36Sopenharmony_ci	case TARGET_PROT_DOUT_STRIP:
292062306a36Sopenharmony_ci		return 1;
292162306a36Sopenharmony_ci	default:
292262306a36Sopenharmony_ci		break;
292362306a36Sopenharmony_ci	}
292462306a36Sopenharmony_ci	return 0;
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_cistatic inline int
292862306a36Sopenharmony_ciqla_tgt_ref_mask_check(struct se_cmd *se_cmd)
292962306a36Sopenharmony_ci{
293062306a36Sopenharmony_ci	switch (se_cmd->prot_op) {
293162306a36Sopenharmony_ci	case TARGET_PROT_DIN_INSERT:
293262306a36Sopenharmony_ci	case TARGET_PROT_DOUT_INSERT:
293362306a36Sopenharmony_ci	case TARGET_PROT_DIN_STRIP:
293462306a36Sopenharmony_ci	case TARGET_PROT_DOUT_STRIP:
293562306a36Sopenharmony_ci	case TARGET_PROT_DIN_PASS:
293662306a36Sopenharmony_ci	case TARGET_PROT_DOUT_PASS:
293762306a36Sopenharmony_ci	    return 1;
293862306a36Sopenharmony_ci	default:
293962306a36Sopenharmony_ci	    return 0;
294062306a36Sopenharmony_ci	}
294162306a36Sopenharmony_ci	return 0;
294262306a36Sopenharmony_ci}
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci/*
294562306a36Sopenharmony_ci * qla_tgt_set_dif_tags - Extract Ref and App tags from SCSI command
294662306a36Sopenharmony_ci */
294762306a36Sopenharmony_cistatic void
294862306a36Sopenharmony_ciqla_tgt_set_dif_tags(struct qla_tgt_cmd *cmd, struct crc_context *ctx,
294962306a36Sopenharmony_ci    uint16_t *pfw_prot_opts)
295062306a36Sopenharmony_ci{
295162306a36Sopenharmony_ci	struct se_cmd *se_cmd = &cmd->se_cmd;
295262306a36Sopenharmony_ci	uint32_t lba = 0xffffffff & se_cmd->t_task_lba;
295362306a36Sopenharmony_ci	scsi_qla_host_t *vha = cmd->tgt->vha;
295462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
295562306a36Sopenharmony_ci	uint32_t t32 = 0;
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	/*
295862306a36Sopenharmony_ci	 * wait till Mode Sense/Select cmd, modepage Ah, subpage 2
295962306a36Sopenharmony_ci	 * have been immplemented by TCM, before AppTag is avail.
296062306a36Sopenharmony_ci	 * Look for modesense_handlers[]
296162306a36Sopenharmony_ci	 */
296262306a36Sopenharmony_ci	ctx->app_tag = 0;
296362306a36Sopenharmony_ci	ctx->app_tag_mask[0] = 0x0;
296462306a36Sopenharmony_ci	ctx->app_tag_mask[1] = 0x0;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	if (IS_PI_UNINIT_CAPABLE(ha)) {
296762306a36Sopenharmony_ci		if ((se_cmd->prot_type == TARGET_DIF_TYPE1_PROT) ||
296862306a36Sopenharmony_ci		    (se_cmd->prot_type == TARGET_DIF_TYPE2_PROT))
296962306a36Sopenharmony_ci			*pfw_prot_opts |= PO_DIS_VALD_APP_ESC;
297062306a36Sopenharmony_ci		else if (se_cmd->prot_type == TARGET_DIF_TYPE3_PROT)
297162306a36Sopenharmony_ci			*pfw_prot_opts |= PO_DIS_VALD_APP_REF_ESC;
297262306a36Sopenharmony_ci	}
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	t32 = ha->tgt.tgt_ops->get_dif_tags(cmd, pfw_prot_opts);
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	switch (se_cmd->prot_type) {
297762306a36Sopenharmony_ci	case TARGET_DIF_TYPE0_PROT:
297862306a36Sopenharmony_ci		/*
297962306a36Sopenharmony_ci		 * No check for ql2xenablehba_err_chk, as it
298062306a36Sopenharmony_ci		 * would be an I/O error if hba tag generation
298162306a36Sopenharmony_ci		 * is not done.
298262306a36Sopenharmony_ci		 */
298362306a36Sopenharmony_ci		ctx->ref_tag = cpu_to_le32(lba);
298462306a36Sopenharmony_ci		/* enable ALL bytes of the ref tag */
298562306a36Sopenharmony_ci		ctx->ref_tag_mask[0] = 0xff;
298662306a36Sopenharmony_ci		ctx->ref_tag_mask[1] = 0xff;
298762306a36Sopenharmony_ci		ctx->ref_tag_mask[2] = 0xff;
298862306a36Sopenharmony_ci		ctx->ref_tag_mask[3] = 0xff;
298962306a36Sopenharmony_ci		break;
299062306a36Sopenharmony_ci	case TARGET_DIF_TYPE1_PROT:
299162306a36Sopenharmony_ci	    /*
299262306a36Sopenharmony_ci	     * For TYPE 1 protection: 16 bit GUARD tag, 32 bit
299362306a36Sopenharmony_ci	     * REF tag, and 16 bit app tag.
299462306a36Sopenharmony_ci	     */
299562306a36Sopenharmony_ci	    ctx->ref_tag = cpu_to_le32(lba);
299662306a36Sopenharmony_ci	    if (!qla_tgt_ref_mask_check(se_cmd) ||
299762306a36Sopenharmony_ci		!(ha->tgt.tgt_ops->chk_dif_tags(t32))) {
299862306a36Sopenharmony_ci		    *pfw_prot_opts |= PO_DIS_REF_TAG_VALD;
299962306a36Sopenharmony_ci		    break;
300062306a36Sopenharmony_ci	    }
300162306a36Sopenharmony_ci	    /* enable ALL bytes of the ref tag */
300262306a36Sopenharmony_ci	    ctx->ref_tag_mask[0] = 0xff;
300362306a36Sopenharmony_ci	    ctx->ref_tag_mask[1] = 0xff;
300462306a36Sopenharmony_ci	    ctx->ref_tag_mask[2] = 0xff;
300562306a36Sopenharmony_ci	    ctx->ref_tag_mask[3] = 0xff;
300662306a36Sopenharmony_ci	    break;
300762306a36Sopenharmony_ci	case TARGET_DIF_TYPE2_PROT:
300862306a36Sopenharmony_ci	    /*
300962306a36Sopenharmony_ci	     * For TYPE 2 protection: 16 bit GUARD + 32 bit REF
301062306a36Sopenharmony_ci	     * tag has to match LBA in CDB + N
301162306a36Sopenharmony_ci	     */
301262306a36Sopenharmony_ci	    ctx->ref_tag = cpu_to_le32(lba);
301362306a36Sopenharmony_ci	    if (!qla_tgt_ref_mask_check(se_cmd) ||
301462306a36Sopenharmony_ci		!(ha->tgt.tgt_ops->chk_dif_tags(t32))) {
301562306a36Sopenharmony_ci		    *pfw_prot_opts |= PO_DIS_REF_TAG_VALD;
301662306a36Sopenharmony_ci		    break;
301762306a36Sopenharmony_ci	    }
301862306a36Sopenharmony_ci	    /* enable ALL bytes of the ref tag */
301962306a36Sopenharmony_ci	    ctx->ref_tag_mask[0] = 0xff;
302062306a36Sopenharmony_ci	    ctx->ref_tag_mask[1] = 0xff;
302162306a36Sopenharmony_ci	    ctx->ref_tag_mask[2] = 0xff;
302262306a36Sopenharmony_ci	    ctx->ref_tag_mask[3] = 0xff;
302362306a36Sopenharmony_ci	    break;
302462306a36Sopenharmony_ci	case TARGET_DIF_TYPE3_PROT:
302562306a36Sopenharmony_ci	    /* For TYPE 3 protection: 16 bit GUARD only */
302662306a36Sopenharmony_ci	    *pfw_prot_opts |= PO_DIS_REF_TAG_VALD;
302762306a36Sopenharmony_ci	    ctx->ref_tag_mask[0] = ctx->ref_tag_mask[1] =
302862306a36Sopenharmony_ci		ctx->ref_tag_mask[2] = ctx->ref_tag_mask[3] = 0x00;
302962306a36Sopenharmony_ci	    break;
303062306a36Sopenharmony_ci	}
303162306a36Sopenharmony_ci}
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_cistatic inline int
303462306a36Sopenharmony_ciqlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm)
303562306a36Sopenharmony_ci{
303662306a36Sopenharmony_ci	struct dsd64		*cur_dsd;
303762306a36Sopenharmony_ci	uint32_t		transfer_length = 0;
303862306a36Sopenharmony_ci	uint32_t		data_bytes;
303962306a36Sopenharmony_ci	uint32_t		dif_bytes;
304062306a36Sopenharmony_ci	uint8_t			bundling = 1;
304162306a36Sopenharmony_ci	struct crc_context	*crc_ctx_pkt = NULL;
304262306a36Sopenharmony_ci	struct qla_hw_data	*ha;
304362306a36Sopenharmony_ci	struct ctio_crc2_to_fw	*pkt;
304462306a36Sopenharmony_ci	dma_addr_t		crc_ctx_dma;
304562306a36Sopenharmony_ci	uint16_t		fw_prot_opts = 0;
304662306a36Sopenharmony_ci	struct qla_tgt_cmd	*cmd = prm->cmd;
304762306a36Sopenharmony_ci	struct se_cmd		*se_cmd = &cmd->se_cmd;
304862306a36Sopenharmony_ci	uint32_t h;
304962306a36Sopenharmony_ci	struct atio_from_isp *atio = &prm->cmd->atio;
305062306a36Sopenharmony_ci	struct qla_tc_param	tc;
305162306a36Sopenharmony_ci	uint16_t t16;
305262306a36Sopenharmony_ci	scsi_qla_host_t *vha = cmd->vha;
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	ha = vha->hw;
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	pkt = (struct ctio_crc2_to_fw *)qpair->req->ring_ptr;
305762306a36Sopenharmony_ci	prm->pkt = pkt;
305862306a36Sopenharmony_ci	memset(pkt, 0, sizeof(*pkt));
305962306a36Sopenharmony_ci
306062306a36Sopenharmony_ci	ql_dbg_qp(ql_dbg_tgt, cmd->qpair, 0xe071,
306162306a36Sopenharmony_ci		"qla_target(%d):%s: se_cmd[%p] CRC2 prot_op[0x%x] cmd prot sg:cnt[%p:%x] lba[%llu]\n",
306262306a36Sopenharmony_ci		cmd->vp_idx, __func__, se_cmd, se_cmd->prot_op,
306362306a36Sopenharmony_ci		prm->prot_sg, prm->prot_seg_cnt, se_cmd->t_task_lba);
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci	if ((se_cmd->prot_op == TARGET_PROT_DIN_INSERT) ||
306662306a36Sopenharmony_ci	    (se_cmd->prot_op == TARGET_PROT_DOUT_STRIP))
306762306a36Sopenharmony_ci		bundling = 0;
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	/* Compute dif len and adjust data len to incude protection */
307062306a36Sopenharmony_ci	data_bytes = cmd->bufflen;
307162306a36Sopenharmony_ci	dif_bytes  = (data_bytes / cmd->blk_sz) * 8;
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci	switch (se_cmd->prot_op) {
307462306a36Sopenharmony_ci	case TARGET_PROT_DIN_INSERT:
307562306a36Sopenharmony_ci	case TARGET_PROT_DOUT_STRIP:
307662306a36Sopenharmony_ci		transfer_length = data_bytes;
307762306a36Sopenharmony_ci		if (cmd->prot_sg_cnt)
307862306a36Sopenharmony_ci			data_bytes += dif_bytes;
307962306a36Sopenharmony_ci		break;
308062306a36Sopenharmony_ci	case TARGET_PROT_DIN_STRIP:
308162306a36Sopenharmony_ci	case TARGET_PROT_DOUT_INSERT:
308262306a36Sopenharmony_ci	case TARGET_PROT_DIN_PASS:
308362306a36Sopenharmony_ci	case TARGET_PROT_DOUT_PASS:
308462306a36Sopenharmony_ci		transfer_length = data_bytes + dif_bytes;
308562306a36Sopenharmony_ci		break;
308662306a36Sopenharmony_ci	default:
308762306a36Sopenharmony_ci		BUG();
308862306a36Sopenharmony_ci		break;
308962306a36Sopenharmony_ci	}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	if (!qlt_hba_err_chk_enabled(se_cmd))
309262306a36Sopenharmony_ci		fw_prot_opts |= 0x10; /* Disable Guard tag checking */
309362306a36Sopenharmony_ci	/* HBA error checking enabled */
309462306a36Sopenharmony_ci	else if (IS_PI_UNINIT_CAPABLE(ha)) {
309562306a36Sopenharmony_ci		if ((se_cmd->prot_type == TARGET_DIF_TYPE1_PROT) ||
309662306a36Sopenharmony_ci		    (se_cmd->prot_type == TARGET_DIF_TYPE2_PROT))
309762306a36Sopenharmony_ci			fw_prot_opts |= PO_DIS_VALD_APP_ESC;
309862306a36Sopenharmony_ci		else if (se_cmd->prot_type == TARGET_DIF_TYPE3_PROT)
309962306a36Sopenharmony_ci			fw_prot_opts |= PO_DIS_VALD_APP_REF_ESC;
310062306a36Sopenharmony_ci	}
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	switch (se_cmd->prot_op) {
310362306a36Sopenharmony_ci	case TARGET_PROT_DIN_INSERT:
310462306a36Sopenharmony_ci	case TARGET_PROT_DOUT_INSERT:
310562306a36Sopenharmony_ci		fw_prot_opts |= PO_MODE_DIF_INSERT;
310662306a36Sopenharmony_ci		break;
310762306a36Sopenharmony_ci	case TARGET_PROT_DIN_STRIP:
310862306a36Sopenharmony_ci	case TARGET_PROT_DOUT_STRIP:
310962306a36Sopenharmony_ci		fw_prot_opts |= PO_MODE_DIF_REMOVE;
311062306a36Sopenharmony_ci		break;
311162306a36Sopenharmony_ci	case TARGET_PROT_DIN_PASS:
311262306a36Sopenharmony_ci	case TARGET_PROT_DOUT_PASS:
311362306a36Sopenharmony_ci		fw_prot_opts |= PO_MODE_DIF_PASS;
311462306a36Sopenharmony_ci		/* FUTURE: does tcm require T10CRC<->IPCKSUM conversion? */
311562306a36Sopenharmony_ci		break;
311662306a36Sopenharmony_ci	default:/* Normal Request */
311762306a36Sopenharmony_ci		fw_prot_opts |= PO_MODE_DIF_PASS;
311862306a36Sopenharmony_ci		break;
311962306a36Sopenharmony_ci	}
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	/* ---- PKT ---- */
312262306a36Sopenharmony_ci	/* Update entry type to indicate Command Type CRC_2 IOCB */
312362306a36Sopenharmony_ci	pkt->entry_type  = CTIO_CRC2;
312462306a36Sopenharmony_ci	pkt->entry_count = 1;
312562306a36Sopenharmony_ci	pkt->vp_index = cmd->vp_idx;
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	h = qlt_make_handle(qpair);
312862306a36Sopenharmony_ci	if (unlikely(h == QLA_TGT_NULL_HANDLE)) {
312962306a36Sopenharmony_ci		/*
313062306a36Sopenharmony_ci		 * CTIO type 7 from the firmware doesn't provide a way to
313162306a36Sopenharmony_ci		 * know the initiator's LOOP ID, hence we can't find
313262306a36Sopenharmony_ci		 * the session and, so, the command.
313362306a36Sopenharmony_ci		 */
313462306a36Sopenharmony_ci		return -EAGAIN;
313562306a36Sopenharmony_ci	} else
313662306a36Sopenharmony_ci		qpair->req->outstanding_cmds[h] = (srb_t *)prm->cmd;
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	pkt->handle  = make_handle(qpair->req->id, h);
313962306a36Sopenharmony_ci	pkt->handle |= CTIO_COMPLETION_HANDLE_MARK;
314062306a36Sopenharmony_ci	pkt->nport_handle = cpu_to_le16(prm->cmd->loop_id);
314162306a36Sopenharmony_ci	pkt->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
314262306a36Sopenharmony_ci	pkt->initiator_id = be_id_to_le(atio->u.isp24.fcp_hdr.s_id);
314362306a36Sopenharmony_ci	pkt->exchange_addr   = atio->u.isp24.exchange_addr;
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	/* silence compile warning */
314662306a36Sopenharmony_ci	t16 = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id);
314762306a36Sopenharmony_ci	pkt->ox_id  = cpu_to_le16(t16);
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	t16 = (atio->u.isp24.attr << 9);
315062306a36Sopenharmony_ci	pkt->flags |= cpu_to_le16(t16);
315162306a36Sopenharmony_ci	pkt->relative_offset = cpu_to_le32(prm->cmd->offset);
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci	/* Set transfer direction */
315462306a36Sopenharmony_ci	if (cmd->dma_data_direction == DMA_TO_DEVICE)
315562306a36Sopenharmony_ci		pkt->flags = cpu_to_le16(CTIO7_FLAGS_DATA_IN);
315662306a36Sopenharmony_ci	else if (cmd->dma_data_direction == DMA_FROM_DEVICE)
315762306a36Sopenharmony_ci		pkt->flags = cpu_to_le16(CTIO7_FLAGS_DATA_OUT);
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	pkt->dseg_count = cpu_to_le16(prm->tot_dsds);
316062306a36Sopenharmony_ci	/* Fibre channel byte count */
316162306a36Sopenharmony_ci	pkt->transfer_length = cpu_to_le32(transfer_length);
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	/* ----- CRC context -------- */
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci	/* Allocate CRC context from global pool */
316662306a36Sopenharmony_ci	crc_ctx_pkt = cmd->ctx =
316762306a36Sopenharmony_ci	    dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	if (!crc_ctx_pkt)
317062306a36Sopenharmony_ci		goto crc_queuing_error;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
317362306a36Sopenharmony_ci	INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list);
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	/* Set handle */
317662306a36Sopenharmony_ci	crc_ctx_pkt->handle = pkt->handle;
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	qla_tgt_set_dif_tags(cmd, crc_ctx_pkt, &fw_prot_opts);
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	put_unaligned_le64(crc_ctx_dma, &pkt->crc_context_address);
318162306a36Sopenharmony_ci	pkt->crc_context_len = cpu_to_le16(CRC_CONTEXT_LEN_FW);
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_ci	if (!bundling) {
318462306a36Sopenharmony_ci		cur_dsd = &crc_ctx_pkt->u.nobundling.data_dsd[0];
318562306a36Sopenharmony_ci	} else {
318662306a36Sopenharmony_ci		/*
318762306a36Sopenharmony_ci		 * Configure Bundling if we need to fetch interlaving
318862306a36Sopenharmony_ci		 * protection PCI accesses
318962306a36Sopenharmony_ci		 */
319062306a36Sopenharmony_ci		fw_prot_opts |= PO_ENABLE_DIF_BUNDLING;
319162306a36Sopenharmony_ci		crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes);
319262306a36Sopenharmony_ci		crc_ctx_pkt->u.bundling.dseg_count =
319362306a36Sopenharmony_ci			cpu_to_le16(prm->tot_dsds - prm->prot_seg_cnt);
319462306a36Sopenharmony_ci		cur_dsd = &crc_ctx_pkt->u.bundling.data_dsd[0];
319562306a36Sopenharmony_ci	}
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci	/* Finish the common fields of CRC pkt */
319862306a36Sopenharmony_ci	crc_ctx_pkt->blk_size   = cpu_to_le16(cmd->blk_sz);
319962306a36Sopenharmony_ci	crc_ctx_pkt->prot_opts  = cpu_to_le16(fw_prot_opts);
320062306a36Sopenharmony_ci	crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes);
320162306a36Sopenharmony_ci	crc_ctx_pkt->guard_seed = cpu_to_le16(0);
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	memset((uint8_t *)&tc, 0 , sizeof(tc));
320462306a36Sopenharmony_ci	tc.vha = vha;
320562306a36Sopenharmony_ci	tc.blk_sz = cmd->blk_sz;
320662306a36Sopenharmony_ci	tc.bufflen = cmd->bufflen;
320762306a36Sopenharmony_ci	tc.sg = cmd->sg;
320862306a36Sopenharmony_ci	tc.prot_sg = cmd->prot_sg;
320962306a36Sopenharmony_ci	tc.ctx = crc_ctx_pkt;
321062306a36Sopenharmony_ci	tc.ctx_dsd_alloced = &cmd->ctx_dsd_alloced;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	/* Walks data segments */
321362306a36Sopenharmony_ci	pkt->flags |= cpu_to_le16(CTIO7_FLAGS_DSD_PTR);
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci	if (!bundling && prm->prot_seg_cnt) {
321662306a36Sopenharmony_ci		if (qla24xx_walk_and_build_sglist_no_difb(ha, NULL, cur_dsd,
321762306a36Sopenharmony_ci			prm->tot_dsds, &tc))
321862306a36Sopenharmony_ci			goto crc_queuing_error;
321962306a36Sopenharmony_ci	} else if (qla24xx_walk_and_build_sglist(ha, NULL, cur_dsd,
322062306a36Sopenharmony_ci		(prm->tot_dsds - prm->prot_seg_cnt), &tc))
322162306a36Sopenharmony_ci		goto crc_queuing_error;
322262306a36Sopenharmony_ci
322362306a36Sopenharmony_ci	if (bundling && prm->prot_seg_cnt) {
322462306a36Sopenharmony_ci		/* Walks dif segments */
322562306a36Sopenharmony_ci		pkt->add_flags |= CTIO_CRC2_AF_DIF_DSD_ENA;
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci		cur_dsd = &crc_ctx_pkt->u.bundling.dif_dsd;
322862306a36Sopenharmony_ci		if (qla24xx_walk_and_build_prot_sglist(ha, NULL, cur_dsd,
322962306a36Sopenharmony_ci			prm->prot_seg_cnt, cmd))
323062306a36Sopenharmony_ci			goto crc_queuing_error;
323162306a36Sopenharmony_ci	}
323262306a36Sopenharmony_ci	return QLA_SUCCESS;
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_cicrc_queuing_error:
323562306a36Sopenharmony_ci	/* Cleanup will be performed by the caller */
323662306a36Sopenharmony_ci	qpair->req->outstanding_cmds[h] = NULL;
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_ci	return QLA_FUNCTION_FAILED;
323962306a36Sopenharmony_ci}
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci/*
324262306a36Sopenharmony_ci * Callback to setup response of xmit_type of QLA_TGT_XMIT_DATA and *
324362306a36Sopenharmony_ci * QLA_TGT_XMIT_STATUS for >= 24xx silicon
324462306a36Sopenharmony_ci */
324562306a36Sopenharmony_ciint qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
324662306a36Sopenharmony_ci	uint8_t scsi_status)
324762306a36Sopenharmony_ci{
324862306a36Sopenharmony_ci	struct scsi_qla_host *vha = cmd->vha;
324962306a36Sopenharmony_ci	struct qla_qpair *qpair = cmd->qpair;
325062306a36Sopenharmony_ci	struct ctio7_to_24xx *pkt;
325162306a36Sopenharmony_ci	struct qla_tgt_prm prm;
325262306a36Sopenharmony_ci	uint32_t full_req_cnt = 0;
325362306a36Sopenharmony_ci	unsigned long flags = 0;
325462306a36Sopenharmony_ci	int res;
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	if (!qpair->fw_started || (cmd->reset_count != qpair->chip_reset) ||
325762306a36Sopenharmony_ci	    (cmd->sess && cmd->sess->deleted)) {
325862306a36Sopenharmony_ci		cmd->state = QLA_TGT_STATE_PROCESSED;
325962306a36Sopenharmony_ci		return 0;
326062306a36Sopenharmony_ci	}
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci	ql_dbg_qp(ql_dbg_tgt, qpair, 0xe018,
326362306a36Sopenharmony_ci	    "is_send_status=%d, cmd->bufflen=%d, cmd->sg_cnt=%d, cmd->dma_data_direction=%d se_cmd[%p] qp %d\n",
326462306a36Sopenharmony_ci	    (xmit_type & QLA_TGT_XMIT_STATUS) ?
326562306a36Sopenharmony_ci	    1 : 0, cmd->bufflen, cmd->sg_cnt, cmd->dma_data_direction,
326662306a36Sopenharmony_ci	    &cmd->se_cmd, qpair->id);
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci	res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status,
326962306a36Sopenharmony_ci	    &full_req_cnt);
327062306a36Sopenharmony_ci	if (unlikely(res != 0)) {
327162306a36Sopenharmony_ci		return res;
327262306a36Sopenharmony_ci	}
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	if (xmit_type == QLA_TGT_XMIT_STATUS)
327762306a36Sopenharmony_ci		qpair->tgt_counters.core_qla_snd_status++;
327862306a36Sopenharmony_ci	else
327962306a36Sopenharmony_ci		qpair->tgt_counters.core_qla_que_buf++;
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	if (!qpair->fw_started || cmd->reset_count != qpair->chip_reset) {
328262306a36Sopenharmony_ci		/*
328362306a36Sopenharmony_ci		 * Either the port is not online or this request was from
328462306a36Sopenharmony_ci		 * previous life, just abort the processing.
328562306a36Sopenharmony_ci		 */
328662306a36Sopenharmony_ci		cmd->state = QLA_TGT_STATE_PROCESSED;
328762306a36Sopenharmony_ci		ql_dbg_qp(ql_dbg_async, qpair, 0xe101,
328862306a36Sopenharmony_ci			"RESET-RSP online/active/old-count/new-count = %d/%d/%d/%d.\n",
328962306a36Sopenharmony_ci			vha->flags.online, qla2x00_reset_active(vha),
329062306a36Sopenharmony_ci			cmd->reset_count, qpair->chip_reset);
329162306a36Sopenharmony_ci		res = 0;
329262306a36Sopenharmony_ci		goto out_unmap_unlock;
329362306a36Sopenharmony_ci	}
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_ci	/* Does F/W have an IOCBs for this request */
329662306a36Sopenharmony_ci	res = qlt_check_reserve_free_req(qpair, full_req_cnt);
329762306a36Sopenharmony_ci	if (unlikely(res))
329862306a36Sopenharmony_ci		goto out_unmap_unlock;
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	if (cmd->se_cmd.prot_op && (xmit_type & QLA_TGT_XMIT_DATA))
330162306a36Sopenharmony_ci		res = qlt_build_ctio_crc2_pkt(qpair, &prm);
330262306a36Sopenharmony_ci	else
330362306a36Sopenharmony_ci		res = qlt_24xx_build_ctio_pkt(qpair, &prm);
330462306a36Sopenharmony_ci	if (unlikely(res != 0)) {
330562306a36Sopenharmony_ci		qpair->req->cnt += full_req_cnt;
330662306a36Sopenharmony_ci		goto out_unmap_unlock;
330762306a36Sopenharmony_ci	}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci	pkt = (struct ctio7_to_24xx *)prm.pkt;
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	if (qlt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) {
331262306a36Sopenharmony_ci		pkt->u.status0.flags |=
331362306a36Sopenharmony_ci		    cpu_to_le16(CTIO7_FLAGS_DATA_IN |
331462306a36Sopenharmony_ci			CTIO7_FLAGS_STATUS_MODE_0);
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci		if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL)
331762306a36Sopenharmony_ci			qlt_load_data_segments(&prm);
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci		if (prm.add_status_pkt == 0) {
332062306a36Sopenharmony_ci			if (xmit_type & QLA_TGT_XMIT_STATUS) {
332162306a36Sopenharmony_ci				pkt->u.status0.scsi_status =
332262306a36Sopenharmony_ci				    cpu_to_le16(prm.rq_result);
332362306a36Sopenharmony_ci				if (!cmd->edif)
332462306a36Sopenharmony_ci					pkt->u.status0.residual =
332562306a36Sopenharmony_ci						cpu_to_le32(prm.residual);
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci				pkt->u.status0.flags |= cpu_to_le16(
332862306a36Sopenharmony_ci				    CTIO7_FLAGS_SEND_STATUS);
332962306a36Sopenharmony_ci				if (qlt_need_explicit_conf(cmd, 0)) {
333062306a36Sopenharmony_ci					pkt->u.status0.flags |=
333162306a36Sopenharmony_ci					    cpu_to_le16(
333262306a36Sopenharmony_ci						CTIO7_FLAGS_EXPLICIT_CONFORM |
333362306a36Sopenharmony_ci						CTIO7_FLAGS_CONFORM_REQ);
333462306a36Sopenharmony_ci				}
333562306a36Sopenharmony_ci			}
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci		} else {
333862306a36Sopenharmony_ci			/*
333962306a36Sopenharmony_ci			 * We have already made sure that there is sufficient
334062306a36Sopenharmony_ci			 * amount of request entries to not drop HW lock in
334162306a36Sopenharmony_ci			 * req_pkt().
334262306a36Sopenharmony_ci			 */
334362306a36Sopenharmony_ci			struct ctio7_to_24xx *ctio =
334462306a36Sopenharmony_ci				(struct ctio7_to_24xx *)qlt_get_req_pkt(
334562306a36Sopenharmony_ci				    qpair->req);
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci			ql_dbg_qp(ql_dbg_tgt, qpair, 0x305e,
334862306a36Sopenharmony_ci			    "Building additional status packet 0x%p.\n",
334962306a36Sopenharmony_ci			    ctio);
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci			/*
335262306a36Sopenharmony_ci			 * T10Dif: ctio_crc2_to_fw overlay ontop of
335362306a36Sopenharmony_ci			 * ctio7_to_24xx
335462306a36Sopenharmony_ci			 */
335562306a36Sopenharmony_ci			memcpy(ctio, pkt, sizeof(*ctio));
335662306a36Sopenharmony_ci			/* reset back to CTIO7 */
335762306a36Sopenharmony_ci			ctio->entry_count = 1;
335862306a36Sopenharmony_ci			ctio->entry_type = CTIO_TYPE7;
335962306a36Sopenharmony_ci			ctio->dseg_count = 0;
336062306a36Sopenharmony_ci			ctio->u.status1.flags &= ~cpu_to_le16(
336162306a36Sopenharmony_ci			    CTIO7_FLAGS_DATA_IN);
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci			/* Real finish is ctio_m1's finish */
336462306a36Sopenharmony_ci			pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK;
336562306a36Sopenharmony_ci			pkt->u.status0.flags |= cpu_to_le16(
336662306a36Sopenharmony_ci			    CTIO7_FLAGS_DONT_RET_CTIO);
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci			/* qlt_24xx_init_ctio_to_isp will correct
336962306a36Sopenharmony_ci			 * all neccessary fields that's part of CTIO7.
337062306a36Sopenharmony_ci			 * There should be no residual of CTIO-CRC2 data.
337162306a36Sopenharmony_ci			 */
337262306a36Sopenharmony_ci			qlt_24xx_init_ctio_to_isp((struct ctio7_to_24xx *)ctio,
337362306a36Sopenharmony_ci			    &prm);
337462306a36Sopenharmony_ci		}
337562306a36Sopenharmony_ci	} else
337662306a36Sopenharmony_ci		qlt_24xx_init_ctio_to_isp(pkt, &prm);
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci	cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */
338062306a36Sopenharmony_ci	cmd->cmd_sent_to_fw = 1;
338162306a36Sopenharmony_ci	cmd->ctio_flags = le16_to_cpu(pkt->u.status0.flags);
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_ci	/* Memory Barrier */
338462306a36Sopenharmony_ci	wmb();
338562306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
338662306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
338762306a36Sopenharmony_ci	else
338862306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
338962306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	return 0;
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ciout_unmap_unlock:
339462306a36Sopenharmony_ci	qlt_unmap_sg(vha, cmd);
339562306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	return res;
339862306a36Sopenharmony_ci}
339962306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_xmit_response);
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ciint qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
340262306a36Sopenharmony_ci{
340362306a36Sopenharmony_ci	struct ctio7_to_24xx *pkt;
340462306a36Sopenharmony_ci	struct scsi_qla_host *vha = cmd->vha;
340562306a36Sopenharmony_ci	struct qla_tgt *tgt = cmd->tgt;
340662306a36Sopenharmony_ci	struct qla_tgt_prm prm;
340762306a36Sopenharmony_ci	unsigned long flags = 0;
340862306a36Sopenharmony_ci	int res = 0;
340962306a36Sopenharmony_ci	struct qla_qpair *qpair = cmd->qpair;
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	memset(&prm, 0, sizeof(prm));
341262306a36Sopenharmony_ci	prm.cmd = cmd;
341362306a36Sopenharmony_ci	prm.tgt = tgt;
341462306a36Sopenharmony_ci	prm.sg = NULL;
341562306a36Sopenharmony_ci	prm.req_cnt = 1;
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	if (!qpair->fw_started || (cmd->reset_count != qpair->chip_reset) ||
341862306a36Sopenharmony_ci	    (cmd->sess && cmd->sess->deleted)) {
341962306a36Sopenharmony_ci		/*
342062306a36Sopenharmony_ci		 * Either the port is not online or this request was from
342162306a36Sopenharmony_ci		 * previous life, just abort the processing.
342262306a36Sopenharmony_ci		 */
342362306a36Sopenharmony_ci		cmd->aborted = 1;
342462306a36Sopenharmony_ci		cmd->write_data_transferred = 0;
342562306a36Sopenharmony_ci		cmd->state = QLA_TGT_STATE_DATA_IN;
342662306a36Sopenharmony_ci		vha->hw->tgt.tgt_ops->handle_data(cmd);
342762306a36Sopenharmony_ci		ql_dbg_qp(ql_dbg_async, qpair, 0xe102,
342862306a36Sopenharmony_ci			"RESET-XFR online/active/old-count/new-count = %d/%d/%d/%d.\n",
342962306a36Sopenharmony_ci			vha->flags.online, qla2x00_reset_active(vha),
343062306a36Sopenharmony_ci			cmd->reset_count, qpair->chip_reset);
343162306a36Sopenharmony_ci		return 0;
343262306a36Sopenharmony_ci	}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci	/* Calculate number of entries and segments required */
343562306a36Sopenharmony_ci	if (qlt_pci_map_calc_cnt(&prm) != 0)
343662306a36Sopenharmony_ci		return -EAGAIN;
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
343962306a36Sopenharmony_ci	/* Does F/W have an IOCBs for this request */
344062306a36Sopenharmony_ci	res = qlt_check_reserve_free_req(qpair, prm.req_cnt);
344162306a36Sopenharmony_ci	if (res != 0)
344262306a36Sopenharmony_ci		goto out_unlock_free_unmap;
344362306a36Sopenharmony_ci	if (cmd->se_cmd.prot_op)
344462306a36Sopenharmony_ci		res = qlt_build_ctio_crc2_pkt(qpair, &prm);
344562306a36Sopenharmony_ci	else
344662306a36Sopenharmony_ci		res = qlt_24xx_build_ctio_pkt(qpair, &prm);
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci	if (unlikely(res != 0)) {
344962306a36Sopenharmony_ci		qpair->req->cnt += prm.req_cnt;
345062306a36Sopenharmony_ci		goto out_unlock_free_unmap;
345162306a36Sopenharmony_ci	}
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_ci	pkt = (struct ctio7_to_24xx *)prm.pkt;
345462306a36Sopenharmony_ci	pkt->u.status0.flags |= cpu_to_le16(CTIO7_FLAGS_DATA_OUT |
345562306a36Sopenharmony_ci	    CTIO7_FLAGS_STATUS_MODE_0);
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL)
345862306a36Sopenharmony_ci		qlt_load_data_segments(&prm);
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci	cmd->state = QLA_TGT_STATE_NEED_DATA;
346162306a36Sopenharmony_ci	cmd->cmd_sent_to_fw = 1;
346262306a36Sopenharmony_ci	cmd->ctio_flags = le16_to_cpu(pkt->u.status0.flags);
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci	/* Memory Barrier */
346562306a36Sopenharmony_ci	wmb();
346662306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
346762306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
346862306a36Sopenharmony_ci	else
346962306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
347062306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	return res;
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_ciout_unlock_free_unmap:
347562306a36Sopenharmony_ci	qlt_unmap_sg(vha, cmd);
347662306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	return res;
347962306a36Sopenharmony_ci}
348062306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_rdy_to_xfer);
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci/*
348462306a36Sopenharmony_ci * it is assumed either hardware_lock or qpair lock is held.
348562306a36Sopenharmony_ci */
348662306a36Sopenharmony_cistatic void
348762306a36Sopenharmony_ciqlt_handle_dif_error(struct qla_qpair *qpair, struct qla_tgt_cmd *cmd,
348862306a36Sopenharmony_ci	struct ctio_crc_from_fw *sts)
348962306a36Sopenharmony_ci{
349062306a36Sopenharmony_ci	uint8_t		*ap = &sts->actual_dif[0];
349162306a36Sopenharmony_ci	uint8_t		*ep = &sts->expected_dif[0];
349262306a36Sopenharmony_ci	uint64_t	lba = cmd->se_cmd.t_task_lba;
349362306a36Sopenharmony_ci	uint8_t scsi_status, sense_key, asc, ascq;
349462306a36Sopenharmony_ci	unsigned long flags;
349562306a36Sopenharmony_ci	struct scsi_qla_host *vha = cmd->vha;
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	cmd->trc_flags |= TRC_DIF_ERR;
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci	cmd->a_guard   = get_unaligned_be16(ap + 0);
350062306a36Sopenharmony_ci	cmd->a_app_tag = get_unaligned_be16(ap + 2);
350162306a36Sopenharmony_ci	cmd->a_ref_tag = get_unaligned_be32(ap + 4);
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	cmd->e_guard   = get_unaligned_be16(ep + 0);
350462306a36Sopenharmony_ci	cmd->e_app_tag = get_unaligned_be16(ep + 2);
350562306a36Sopenharmony_ci	cmd->e_ref_tag = get_unaligned_be32(ep + 4);
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_dif, vha, 0xf075,
350862306a36Sopenharmony_ci	    "%s: aborted %d state %d\n", __func__, cmd->aborted, cmd->state);
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	scsi_status = sense_key = asc = ascq = 0;
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_ci	/* check appl tag */
351362306a36Sopenharmony_ci	if (cmd->e_app_tag != cmd->a_app_tag) {
351462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_dif, vha, 0xe00d,
351562306a36Sopenharmony_ci		    "App Tag ERR: cdb[%x] lba[%llx %llx] blks[%x] [Actual|Expected] Ref[%x|%x], App[%x|%x], Guard [%x|%x] cmd=%p ox_id[%04x]",
351662306a36Sopenharmony_ci		    cmd->cdb[0], lba, (lba+cmd->num_blks), cmd->num_blks,
351762306a36Sopenharmony_ci		    cmd->a_ref_tag, cmd->e_ref_tag, cmd->a_app_tag,
351862306a36Sopenharmony_ci		    cmd->e_app_tag, cmd->a_guard, cmd->e_guard, cmd,
351962306a36Sopenharmony_ci		    cmd->atio.u.isp24.fcp_hdr.ox_id);
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_ci		cmd->dif_err_code = DIF_ERR_APP;
352262306a36Sopenharmony_ci		scsi_status = SAM_STAT_CHECK_CONDITION;
352362306a36Sopenharmony_ci		sense_key = ABORTED_COMMAND;
352462306a36Sopenharmony_ci		asc = 0x10;
352562306a36Sopenharmony_ci		ascq = 0x2;
352662306a36Sopenharmony_ci	}
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	/* check ref tag */
352962306a36Sopenharmony_ci	if (cmd->e_ref_tag != cmd->a_ref_tag) {
353062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_dif, vha, 0xe00e,
353162306a36Sopenharmony_ci		    "Ref Tag ERR: cdb[%x] lba[%llx %llx] blks[%x] [Actual|Expected] Ref[%x|%x], App[%x|%x], Guard[%x|%x] cmd=%p ox_id[%04x] ",
353262306a36Sopenharmony_ci		    cmd->cdb[0], lba, (lba+cmd->num_blks), cmd->num_blks,
353362306a36Sopenharmony_ci		    cmd->a_ref_tag, cmd->e_ref_tag, cmd->a_app_tag,
353462306a36Sopenharmony_ci		    cmd->e_app_tag, cmd->a_guard, cmd->e_guard, cmd,
353562306a36Sopenharmony_ci		    cmd->atio.u.isp24.fcp_hdr.ox_id);
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci		cmd->dif_err_code = DIF_ERR_REF;
353862306a36Sopenharmony_ci		scsi_status = SAM_STAT_CHECK_CONDITION;
353962306a36Sopenharmony_ci		sense_key = ABORTED_COMMAND;
354062306a36Sopenharmony_ci		asc = 0x10;
354162306a36Sopenharmony_ci		ascq = 0x3;
354262306a36Sopenharmony_ci		goto out;
354362306a36Sopenharmony_ci	}
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	/* check guard */
354662306a36Sopenharmony_ci	if (cmd->e_guard != cmd->a_guard) {
354762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_dif, vha, 0xe012,
354862306a36Sopenharmony_ci		    "Guard ERR: cdb[%x] lba[%llx %llx] blks[%x] [Actual|Expected] Ref[%x|%x], App[%x|%x], Guard [%x|%x] cmd=%p ox_id[%04x]",
354962306a36Sopenharmony_ci		    cmd->cdb[0], lba, (lba+cmd->num_blks), cmd->num_blks,
355062306a36Sopenharmony_ci		    cmd->a_ref_tag, cmd->e_ref_tag, cmd->a_app_tag,
355162306a36Sopenharmony_ci		    cmd->e_app_tag, cmd->a_guard, cmd->e_guard, cmd,
355262306a36Sopenharmony_ci		    cmd->atio.u.isp24.fcp_hdr.ox_id);
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci		cmd->dif_err_code = DIF_ERR_GRD;
355562306a36Sopenharmony_ci		scsi_status = SAM_STAT_CHECK_CONDITION;
355662306a36Sopenharmony_ci		sense_key = ABORTED_COMMAND;
355762306a36Sopenharmony_ci		asc = 0x10;
355862306a36Sopenharmony_ci		ascq = 0x1;
355962306a36Sopenharmony_ci	}
356062306a36Sopenharmony_ciout:
356162306a36Sopenharmony_ci	switch (cmd->state) {
356262306a36Sopenharmony_ci	case QLA_TGT_STATE_NEED_DATA:
356362306a36Sopenharmony_ci		/* handle_data will load DIF error code  */
356462306a36Sopenharmony_ci		cmd->state = QLA_TGT_STATE_DATA_IN;
356562306a36Sopenharmony_ci		vha->hw->tgt.tgt_ops->handle_data(cmd);
356662306a36Sopenharmony_ci		break;
356762306a36Sopenharmony_ci	default:
356862306a36Sopenharmony_ci		spin_lock_irqsave(&cmd->cmd_lock, flags);
356962306a36Sopenharmony_ci		if (cmd->aborted) {
357062306a36Sopenharmony_ci			spin_unlock_irqrestore(&cmd->cmd_lock, flags);
357162306a36Sopenharmony_ci			vha->hw->tgt.tgt_ops->free_cmd(cmd);
357262306a36Sopenharmony_ci			break;
357362306a36Sopenharmony_ci		}
357462306a36Sopenharmony_ci		spin_unlock_irqrestore(&cmd->cmd_lock, flags);
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci		qlt_send_resp_ctio(qpair, cmd, scsi_status, sense_key, asc,
357762306a36Sopenharmony_ci		    ascq);
357862306a36Sopenharmony_ci		/* assume scsi status gets out on the wire.
357962306a36Sopenharmony_ci		 * Will not wait for completion.
358062306a36Sopenharmony_ci		 */
358162306a36Sopenharmony_ci		vha->hw->tgt.tgt_ops->free_cmd(cmd);
358262306a36Sopenharmony_ci		break;
358362306a36Sopenharmony_ci	}
358462306a36Sopenharmony_ci}
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci/* If hardware_lock held on entry, might drop it, then reaquire */
358762306a36Sopenharmony_ci/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
358862306a36Sopenharmony_cistatic int __qlt_send_term_imm_notif(struct scsi_qla_host *vha,
358962306a36Sopenharmony_ci	struct imm_ntfy_from_isp *ntfy)
359062306a36Sopenharmony_ci{
359162306a36Sopenharmony_ci	struct nack_to_isp *nack;
359262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
359362306a36Sopenharmony_ci	request_t *pkt;
359462306a36Sopenharmony_ci	int ret = 0;
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_tmr, vha, 0xe01c,
359762306a36Sopenharmony_ci	    "Sending TERM ELS CTIO (ha=%p)\n", ha);
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL);
360062306a36Sopenharmony_ci	if (pkt == NULL) {
360162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe080,
360262306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
360362306a36Sopenharmony_ci		    "request packet\n", vha->vp_idx, __func__);
360462306a36Sopenharmony_ci		return -ENOMEM;
360562306a36Sopenharmony_ci	}
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	pkt->entry_type = NOTIFY_ACK_TYPE;
360862306a36Sopenharmony_ci	pkt->entry_count = 1;
360962306a36Sopenharmony_ci	pkt->handle = QLA_TGT_SKIP_HANDLE;
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	nack = (struct nack_to_isp *)pkt;
361262306a36Sopenharmony_ci	nack->ox_id = ntfy->ox_id;
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
361562306a36Sopenharmony_ci	if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
361662306a36Sopenharmony_ci		nack->u.isp24.flags = ntfy->u.isp24.flags &
361762306a36Sopenharmony_ci			cpu_to_le16(NOTIFY24XX_FLAGS_PUREX_IOCB);
361862306a36Sopenharmony_ci	}
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	/* terminate */
362162306a36Sopenharmony_ci	nack->u.isp24.flags |=
362262306a36Sopenharmony_ci		__constant_cpu_to_le16(NOTIFY_ACK_FLAGS_TERMINATE);
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci	nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
362562306a36Sopenharmony_ci	nack->u.isp24.status = ntfy->u.isp24.status;
362662306a36Sopenharmony_ci	nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
362762306a36Sopenharmony_ci	nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle;
362862306a36Sopenharmony_ci	nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
362962306a36Sopenharmony_ci	nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
363062306a36Sopenharmony_ci	nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
363162306a36Sopenharmony_ci	nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci	qla2x00_start_iocbs(vha, vha->req);
363462306a36Sopenharmony_ci	return ret;
363562306a36Sopenharmony_ci}
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_cistatic void qlt_send_term_imm_notif(struct scsi_qla_host *vha,
363862306a36Sopenharmony_ci	struct imm_ntfy_from_isp *imm, int ha_locked)
363962306a36Sopenharmony_ci{
364062306a36Sopenharmony_ci	int rc;
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	WARN_ON_ONCE(!ha_locked);
364362306a36Sopenharmony_ci	rc = __qlt_send_term_imm_notif(vha, imm);
364462306a36Sopenharmony_ci	pr_debug("rc = %d\n", rc);
364562306a36Sopenharmony_ci}
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci/*
364862306a36Sopenharmony_ci * If hardware_lock held on entry, might drop it, then reaquire
364962306a36Sopenharmony_ci * This function sends the appropriate CTIO to ISP 2xxx or 24xx
365062306a36Sopenharmony_ci */
365162306a36Sopenharmony_cistatic int __qlt_send_term_exchange(struct qla_qpair *qpair,
365262306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd,
365362306a36Sopenharmony_ci	struct atio_from_isp *atio)
365462306a36Sopenharmony_ci{
365562306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
365662306a36Sopenharmony_ci	struct ctio7_to_24xx *ctio24;
365762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
365862306a36Sopenharmony_ci	request_t *pkt;
365962306a36Sopenharmony_ci	int ret = 0;
366062306a36Sopenharmony_ci	uint16_t temp;
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe009, "Sending TERM EXCH CTIO (ha=%p)\n", ha);
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_ci	if (cmd)
366562306a36Sopenharmony_ci		vha = cmd->vha;
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci	pkt = (request_t *)qla2x00_alloc_iocbs_ready(qpair, NULL);
366862306a36Sopenharmony_ci	if (pkt == NULL) {
366962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe050,
367062306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
367162306a36Sopenharmony_ci		    "request packet\n", vha->vp_idx, __func__);
367262306a36Sopenharmony_ci		return -ENOMEM;
367362306a36Sopenharmony_ci	}
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_ci	if (cmd != NULL) {
367662306a36Sopenharmony_ci		if (cmd->state < QLA_TGT_STATE_PROCESSED) {
367762306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe051,
367862306a36Sopenharmony_ci			    "qla_target(%d): Terminating cmd %p with "
367962306a36Sopenharmony_ci			    "incorrect state %d\n", vha->vp_idx, cmd,
368062306a36Sopenharmony_ci			    cmd->state);
368162306a36Sopenharmony_ci		} else
368262306a36Sopenharmony_ci			ret = 1;
368362306a36Sopenharmony_ci	}
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	qpair->tgt_counters.num_term_xchg_sent++;
368662306a36Sopenharmony_ci	pkt->entry_count = 1;
368762306a36Sopenharmony_ci	pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	ctio24 = (struct ctio7_to_24xx *)pkt;
369062306a36Sopenharmony_ci	ctio24->entry_type = CTIO_TYPE7;
369162306a36Sopenharmony_ci	ctio24->nport_handle = cpu_to_le16(CTIO7_NHANDLE_UNRECOGNIZED);
369262306a36Sopenharmony_ci	ctio24->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
369362306a36Sopenharmony_ci	ctio24->vp_index = vha->vp_idx;
369462306a36Sopenharmony_ci	ctio24->initiator_id = be_id_to_le(atio->u.isp24.fcp_hdr.s_id);
369562306a36Sopenharmony_ci	ctio24->exchange_addr = atio->u.isp24.exchange_addr;
369662306a36Sopenharmony_ci	temp = (atio->u.isp24.attr << 9) | CTIO7_FLAGS_STATUS_MODE_1 |
369762306a36Sopenharmony_ci		CTIO7_FLAGS_TERMINATE;
369862306a36Sopenharmony_ci	ctio24->u.status1.flags = cpu_to_le16(temp);
369962306a36Sopenharmony_ci	temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id);
370062306a36Sopenharmony_ci	ctio24->u.status1.ox_id = cpu_to_le16(temp);
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci	/* Memory Barrier */
370362306a36Sopenharmony_ci	wmb();
370462306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
370562306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
370662306a36Sopenharmony_ci	else
370762306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
370862306a36Sopenharmony_ci	return ret;
370962306a36Sopenharmony_ci}
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_cistatic void qlt_send_term_exchange(struct qla_qpair *qpair,
371262306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked,
371362306a36Sopenharmony_ci	int ul_abort)
371462306a36Sopenharmony_ci{
371562306a36Sopenharmony_ci	struct scsi_qla_host *vha;
371662306a36Sopenharmony_ci	unsigned long flags = 0;
371762306a36Sopenharmony_ci	int rc;
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci	/* why use different vha? NPIV */
372062306a36Sopenharmony_ci	if (cmd)
372162306a36Sopenharmony_ci		vha = cmd->vha;
372262306a36Sopenharmony_ci	else
372362306a36Sopenharmony_ci		vha = qpair->vha;
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci	if (ha_locked) {
372662306a36Sopenharmony_ci		rc = __qlt_send_term_exchange(qpair, cmd, atio);
372762306a36Sopenharmony_ci		if (rc == -ENOMEM)
372862306a36Sopenharmony_ci			qlt_alloc_qfull_cmd(vha, atio, 0, 0);
372962306a36Sopenharmony_ci		goto done;
373062306a36Sopenharmony_ci	}
373162306a36Sopenharmony_ci	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
373262306a36Sopenharmony_ci	rc = __qlt_send_term_exchange(qpair, cmd, atio);
373362306a36Sopenharmony_ci	if (rc == -ENOMEM)
373462306a36Sopenharmony_ci		qlt_alloc_qfull_cmd(vha, atio, 0, 0);
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_cidone:
373762306a36Sopenharmony_ci	if (cmd && !ul_abort && !cmd->aborted) {
373862306a36Sopenharmony_ci		if (cmd->sg_mapped)
373962306a36Sopenharmony_ci			qlt_unmap_sg(vha, cmd);
374062306a36Sopenharmony_ci		vha->hw->tgt.tgt_ops->free_cmd(cmd);
374162306a36Sopenharmony_ci	}
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ci	if (!ha_locked)
374462306a36Sopenharmony_ci		spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	return;
374762306a36Sopenharmony_ci}
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_cistatic void qlt_init_term_exchange(struct scsi_qla_host *vha)
375062306a36Sopenharmony_ci{
375162306a36Sopenharmony_ci	struct list_head free_list;
375262306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd, *tcmd;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	vha->hw->tgt.leak_exchg_thresh_hold =
375562306a36Sopenharmony_ci	    (vha->hw->cur_fw_xcb_count/100) * LEAK_EXCHG_THRESH_HOLD_PERCENT;
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci	cmd = tcmd = NULL;
375862306a36Sopenharmony_ci	if (!list_empty(&vha->hw->tgt.q_full_list)) {
375962306a36Sopenharmony_ci		INIT_LIST_HEAD(&free_list);
376062306a36Sopenharmony_ci		list_splice_init(&vha->hw->tgt.q_full_list, &free_list);
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci		list_for_each_entry_safe(cmd, tcmd, &free_list, cmd_list) {
376362306a36Sopenharmony_ci			list_del(&cmd->cmd_list);
376462306a36Sopenharmony_ci			/* This cmd was never sent to TCM.  There is no need
376562306a36Sopenharmony_ci			 * to schedule free or call free_cmd
376662306a36Sopenharmony_ci			 */
376762306a36Sopenharmony_ci			qlt_free_cmd(cmd);
376862306a36Sopenharmony_ci			vha->hw->tgt.num_qfull_cmds_alloc--;
376962306a36Sopenharmony_ci		}
377062306a36Sopenharmony_ci	}
377162306a36Sopenharmony_ci	vha->hw->tgt.num_qfull_cmds_dropped = 0;
377262306a36Sopenharmony_ci}
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_cistatic void qlt_chk_exch_leak_thresh_hold(struct scsi_qla_host *vha)
377562306a36Sopenharmony_ci{
377662306a36Sopenharmony_ci	uint32_t total_leaked;
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	total_leaked = vha->hw->tgt.num_qfull_cmds_dropped;
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	if (vha->hw->tgt.leak_exchg_thresh_hold &&
378162306a36Sopenharmony_ci	    (total_leaked > vha->hw->tgt.leak_exchg_thresh_hold)) {
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe079,
378462306a36Sopenharmony_ci		    "Chip reset due to exchange starvation: %d/%d.\n",
378562306a36Sopenharmony_ci		    total_leaked, vha->hw->cur_fw_xcb_count);
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci		if (IS_P3P_TYPE(vha->hw))
378862306a36Sopenharmony_ci			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
378962306a36Sopenharmony_ci		else
379062306a36Sopenharmony_ci			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
379162306a36Sopenharmony_ci		qla2xxx_wake_dpc(vha);
379262306a36Sopenharmony_ci	}
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci}
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_ciint qlt_abort_cmd(struct qla_tgt_cmd *cmd)
379762306a36Sopenharmony_ci{
379862306a36Sopenharmony_ci	struct qla_tgt *tgt = cmd->tgt;
379962306a36Sopenharmony_ci	struct scsi_qla_host *vha = tgt->vha;
380062306a36Sopenharmony_ci	struct se_cmd *se_cmd = &cmd->se_cmd;
380162306a36Sopenharmony_ci	unsigned long flags;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014,
380462306a36Sopenharmony_ci	    "qla_target(%d): terminating exchange for aborted cmd=%p "
380562306a36Sopenharmony_ci	    "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, &cmd->se_cmd,
380662306a36Sopenharmony_ci	    se_cmd->tag);
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	spin_lock_irqsave(&cmd->cmd_lock, flags);
380962306a36Sopenharmony_ci	if (cmd->aborted) {
381062306a36Sopenharmony_ci		if (cmd->sg_mapped)
381162306a36Sopenharmony_ci			qlt_unmap_sg(vha, cmd);
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci		spin_unlock_irqrestore(&cmd->cmd_lock, flags);
381462306a36Sopenharmony_ci		/*
381562306a36Sopenharmony_ci		 * It's normal to see 2 calls in this path:
381662306a36Sopenharmony_ci		 *  1) XFER Rdy completion + CMD_T_ABORT
381762306a36Sopenharmony_ci		 *  2) TCM TMR - drain_state_list
381862306a36Sopenharmony_ci		 */
381962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf016,
382062306a36Sopenharmony_ci		    "multiple abort. %p transport_state %x, t_state %x, "
382162306a36Sopenharmony_ci		    "se_cmd_flags %x\n", cmd, cmd->se_cmd.transport_state,
382262306a36Sopenharmony_ci		    cmd->se_cmd.t_state, cmd->se_cmd.se_cmd_flags);
382362306a36Sopenharmony_ci		return -EIO;
382462306a36Sopenharmony_ci	}
382562306a36Sopenharmony_ci	cmd->aborted = 1;
382662306a36Sopenharmony_ci	cmd->trc_flags |= TRC_ABORT;
382762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cmd->cmd_lock, flags);
382862306a36Sopenharmony_ci
382962306a36Sopenharmony_ci	qlt_send_term_exchange(cmd->qpair, cmd, &cmd->atio, 0, 1);
383062306a36Sopenharmony_ci	return 0;
383162306a36Sopenharmony_ci}
383262306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_abort_cmd);
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_civoid qlt_free_cmd(struct qla_tgt_cmd *cmd)
383562306a36Sopenharmony_ci{
383662306a36Sopenharmony_ci	struct fc_port *sess = cmd->sess;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, cmd->vha, 0xe074,
383962306a36Sopenharmony_ci	    "%s: se_cmd[%p] ox_id %04x\n",
384062306a36Sopenharmony_ci	    __func__, &cmd->se_cmd,
384162306a36Sopenharmony_ci	    be16_to_cpu(cmd->atio.u.isp24.fcp_hdr.ox_id));
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	BUG_ON(cmd->cmd_in_wq);
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci	if (!cmd->q_full)
384662306a36Sopenharmony_ci		qlt_decr_num_pend_cmds(cmd->vha);
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	BUG_ON(cmd->sg_mapped);
384962306a36Sopenharmony_ci	cmd->jiffies_at_free = get_jiffies_64();
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci	if (!sess || !sess->se_sess) {
385262306a36Sopenharmony_ci		WARN_ON(1);
385362306a36Sopenharmony_ci		return;
385462306a36Sopenharmony_ci	}
385562306a36Sopenharmony_ci	cmd->jiffies_at_free = get_jiffies_64();
385662306a36Sopenharmony_ci	cmd->vha->hw->tgt.tgt_ops->rel_cmd(cmd);
385762306a36Sopenharmony_ci}
385862306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_free_cmd);
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci/*
386162306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
386262306a36Sopenharmony_ci */
386362306a36Sopenharmony_cistatic int qlt_term_ctio_exchange(struct qla_qpair *qpair, void *ctio,
386462306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd, uint32_t status)
386562306a36Sopenharmony_ci{
386662306a36Sopenharmony_ci	int term = 0;
386762306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci	if (cmd->se_cmd.prot_op)
387062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_dif, vha, 0xe013,
387162306a36Sopenharmony_ci		    "Term DIF cmd: lba[0x%llx|%lld] len[0x%x] "
387262306a36Sopenharmony_ci		    "se_cmd=%p tag[%x] op %#x/%s",
387362306a36Sopenharmony_ci		     cmd->lba, cmd->lba,
387462306a36Sopenharmony_ci		     cmd->num_blks, &cmd->se_cmd,
387562306a36Sopenharmony_ci		     cmd->atio.u.isp24.exchange_addr,
387662306a36Sopenharmony_ci		     cmd->se_cmd.prot_op,
387762306a36Sopenharmony_ci		     prot_op_str(cmd->se_cmd.prot_op));
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci	if (ctio != NULL) {
388062306a36Sopenharmony_ci		struct ctio7_from_24xx *c = (struct ctio7_from_24xx *)ctio;
388162306a36Sopenharmony_ci
388262306a36Sopenharmony_ci		term = !(c->flags &
388362306a36Sopenharmony_ci		    cpu_to_le16(OF_TERM_EXCH));
388462306a36Sopenharmony_ci	} else
388562306a36Sopenharmony_ci		term = 1;
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	if (term)
388862306a36Sopenharmony_ci		qlt_send_term_exchange(qpair, cmd, &cmd->atio, 1, 0);
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	return term;
389162306a36Sopenharmony_ci}
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
389562306a36Sopenharmony_cistatic void *qlt_ctio_to_cmd(struct scsi_qla_host *vha,
389662306a36Sopenharmony_ci	struct rsp_que *rsp, uint32_t handle, void *ctio)
389762306a36Sopenharmony_ci{
389862306a36Sopenharmony_ci	void *cmd = NULL;
389962306a36Sopenharmony_ci	struct req_que *req;
390062306a36Sopenharmony_ci	int qid = GET_QID(handle);
390162306a36Sopenharmony_ci	uint32_t h = handle & ~QLA_TGT_HANDLE_MASK;
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_ci	if (unlikely(h == QLA_TGT_SKIP_HANDLE))
390462306a36Sopenharmony_ci		return NULL;
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	if (qid == rsp->req->id) {
390762306a36Sopenharmony_ci		req = rsp->req;
390862306a36Sopenharmony_ci	} else if (vha->hw->req_q_map[qid]) {
390962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0x1000a,
391062306a36Sopenharmony_ci		    "qla_target(%d): CTIO completion with different QID %d handle %x\n",
391162306a36Sopenharmony_ci		    vha->vp_idx, rsp->id, handle);
391262306a36Sopenharmony_ci		req = vha->hw->req_q_map[qid];
391362306a36Sopenharmony_ci	} else {
391462306a36Sopenharmony_ci		return NULL;
391562306a36Sopenharmony_ci	}
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci	h &= QLA_CMD_HANDLE_MASK;
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	if (h != QLA_TGT_NULL_HANDLE) {
392062306a36Sopenharmony_ci		if (unlikely(h >= req->num_outstanding_cmds)) {
392162306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe052,
392262306a36Sopenharmony_ci			    "qla_target(%d): Wrong handle %x received\n",
392362306a36Sopenharmony_ci			    vha->vp_idx, handle);
392462306a36Sopenharmony_ci			return NULL;
392562306a36Sopenharmony_ci		}
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci		cmd = req->outstanding_cmds[h];
392862306a36Sopenharmony_ci		if (unlikely(cmd == NULL)) {
392962306a36Sopenharmony_ci			ql_dbg(ql_dbg_async, vha, 0xe053,
393062306a36Sopenharmony_ci			    "qla_target(%d): Suspicious: unable to find the command with handle %x req->id %d rsp->id %d\n",
393162306a36Sopenharmony_ci				vha->vp_idx, handle, req->id, rsp->id);
393262306a36Sopenharmony_ci			return NULL;
393362306a36Sopenharmony_ci		}
393462306a36Sopenharmony_ci		req->outstanding_cmds[h] = NULL;
393562306a36Sopenharmony_ci	} else if (ctio != NULL) {
393662306a36Sopenharmony_ci		/* We can't get loop ID from CTIO7 */
393762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe054,
393862306a36Sopenharmony_ci		    "qla_target(%d): Wrong CTIO received: QLA24xx doesn't "
393962306a36Sopenharmony_ci		    "support NULL handles\n", vha->vp_idx);
394062306a36Sopenharmony_ci		return NULL;
394162306a36Sopenharmony_ci	}
394262306a36Sopenharmony_ci
394362306a36Sopenharmony_ci	return cmd;
394462306a36Sopenharmony_ci}
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci/*
394762306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
394862306a36Sopenharmony_ci */
394962306a36Sopenharmony_cistatic void qlt_do_ctio_completion(struct scsi_qla_host *vha,
395062306a36Sopenharmony_ci    struct rsp_que *rsp, uint32_t handle, uint32_t status, void *ctio)
395162306a36Sopenharmony_ci{
395262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
395362306a36Sopenharmony_ci	struct se_cmd *se_cmd;
395462306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
395562306a36Sopenharmony_ci	struct qla_qpair *qpair = rsp->qpair;
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) {
395862306a36Sopenharmony_ci		/* That could happen only in case of an error/reset/abort */
395962306a36Sopenharmony_ci		if (status != CTIO_SUCCESS) {
396062306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01d,
396162306a36Sopenharmony_ci			    "Intermediate CTIO received"
396262306a36Sopenharmony_ci			    " (status %x)\n", status);
396362306a36Sopenharmony_ci		}
396462306a36Sopenharmony_ci		return;
396562306a36Sopenharmony_ci	}
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	cmd = qlt_ctio_to_cmd(vha, rsp, handle, ctio);
396862306a36Sopenharmony_ci	if (cmd == NULL)
396962306a36Sopenharmony_ci		return;
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_ci	if ((le16_to_cpu(((struct ctio7_from_24xx *)ctio)->flags) & CTIO7_FLAGS_DATA_OUT) &&
397262306a36Sopenharmony_ci	    cmd->sess) {
397362306a36Sopenharmony_ci		qlt_chk_edif_rx_sa_delete_pending(vha, cmd->sess,
397462306a36Sopenharmony_ci		    (struct ctio7_from_24xx *)ctio);
397562306a36Sopenharmony_ci	}
397662306a36Sopenharmony_ci
397762306a36Sopenharmony_ci	se_cmd = &cmd->se_cmd;
397862306a36Sopenharmony_ci	cmd->cmd_sent_to_fw = 0;
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	qlt_unmap_sg(vha, cmd);
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	if (unlikely(status != CTIO_SUCCESS)) {
398362306a36Sopenharmony_ci		switch (status & 0xFFFF) {
398462306a36Sopenharmony_ci		case CTIO_INVALID_RX_ID:
398562306a36Sopenharmony_ci			if (printk_ratelimit())
398662306a36Sopenharmony_ci				dev_info(&vha->hw->pdev->dev,
398762306a36Sopenharmony_ci				    "qla_target(%d): CTIO with INVALID_RX_ID ATIO attr %x CTIO Flags %x|%x\n",
398862306a36Sopenharmony_ci				    vha->vp_idx, cmd->atio.u.isp24.attr,
398962306a36Sopenharmony_ci				    ((cmd->ctio_flags >> 9) & 0xf),
399062306a36Sopenharmony_ci				    cmd->ctio_flags);
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci			break;
399362306a36Sopenharmony_ci		case CTIO_LIP_RESET:
399462306a36Sopenharmony_ci		case CTIO_TARGET_RESET:
399562306a36Sopenharmony_ci		case CTIO_ABORTED:
399662306a36Sopenharmony_ci			/* driver request abort via Terminate exchange */
399762306a36Sopenharmony_ci		case CTIO_TIMEOUT:
399862306a36Sopenharmony_ci			/* They are OK */
399962306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf058,
400062306a36Sopenharmony_ci			    "qla_target(%d): CTIO with "
400162306a36Sopenharmony_ci			    "status %#x received, state %x, se_cmd %p, "
400262306a36Sopenharmony_ci			    "(LIP_RESET=e, ABORTED=2, TARGET_RESET=17, "
400362306a36Sopenharmony_ci			    "TIMEOUT=b, INVALID_RX_ID=8)\n", vha->vp_idx,
400462306a36Sopenharmony_ci			    status, cmd->state, se_cmd);
400562306a36Sopenharmony_ci			break;
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_ci		case CTIO_PORT_LOGGED_OUT:
400862306a36Sopenharmony_ci		case CTIO_PORT_UNAVAILABLE:
400962306a36Sopenharmony_ci		{
401062306a36Sopenharmony_ci			int logged_out =
401162306a36Sopenharmony_ci				(status & 0xFFFF) == CTIO_PORT_LOGGED_OUT;
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059,
401462306a36Sopenharmony_ci			    "qla_target(%d): CTIO with %s status %x "
401562306a36Sopenharmony_ci			    "received (state %x, se_cmd %p)\n", vha->vp_idx,
401662306a36Sopenharmony_ci			    logged_out ? "PORT LOGGED OUT" : "PORT UNAVAILABLE",
401762306a36Sopenharmony_ci			    status, cmd->state, se_cmd);
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_ci			if (logged_out && cmd->sess) {
402062306a36Sopenharmony_ci				/*
402162306a36Sopenharmony_ci				 * Session is already logged out, but we need
402262306a36Sopenharmony_ci				 * to notify initiator, who's not aware of this
402362306a36Sopenharmony_ci				 */
402462306a36Sopenharmony_ci				cmd->sess->send_els_logo = 1;
402562306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0x20f8,
402662306a36Sopenharmony_ci				    "%s %d %8phC post del sess\n",
402762306a36Sopenharmony_ci				    __func__, __LINE__, cmd->sess->port_name);
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci				qlt_schedule_sess_for_deletion(cmd->sess);
403062306a36Sopenharmony_ci			}
403162306a36Sopenharmony_ci			break;
403262306a36Sopenharmony_ci		}
403362306a36Sopenharmony_ci		case CTIO_DIF_ERROR: {
403462306a36Sopenharmony_ci			struct ctio_crc_from_fw *crc =
403562306a36Sopenharmony_ci				(struct ctio_crc_from_fw *)ctio;
403662306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf073,
403762306a36Sopenharmony_ci			    "qla_target(%d): CTIO with DIF_ERROR status %x "
403862306a36Sopenharmony_ci			    "received (state %x, ulp_cmd %p) actual_dif[0x%llx] "
403962306a36Sopenharmony_ci			    "expect_dif[0x%llx]\n",
404062306a36Sopenharmony_ci			    vha->vp_idx, status, cmd->state, se_cmd,
404162306a36Sopenharmony_ci			    *((u64 *)&crc->actual_dif[0]),
404262306a36Sopenharmony_ci			    *((u64 *)&crc->expected_dif[0]));
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_ci			qlt_handle_dif_error(qpair, cmd, ctio);
404562306a36Sopenharmony_ci			return;
404662306a36Sopenharmony_ci		}
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci		case CTIO_FAST_AUTH_ERR:
404962306a36Sopenharmony_ci		case CTIO_FAST_INCOMP_PAD_LEN:
405062306a36Sopenharmony_ci		case CTIO_FAST_INVALID_REQ:
405162306a36Sopenharmony_ci		case CTIO_FAST_SPI_ERR:
405262306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b,
405362306a36Sopenharmony_ci			    "qla_target(%d): CTIO with EDIF error status 0x%x received (state %x, se_cmd %p\n",
405462306a36Sopenharmony_ci			    vha->vp_idx, status, cmd->state, se_cmd);
405562306a36Sopenharmony_ci			break;
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci		default:
405862306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b,
405962306a36Sopenharmony_ci			    "qla_target(%d): CTIO with error status 0x%x received (state %x, se_cmd %p\n",
406062306a36Sopenharmony_ci			    vha->vp_idx, status, cmd->state, se_cmd);
406162306a36Sopenharmony_ci			break;
406262306a36Sopenharmony_ci		}
406362306a36Sopenharmony_ci
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_ci		/* "cmd->aborted" means
406662306a36Sopenharmony_ci		 * cmd is already aborted/terminated, we don't
406762306a36Sopenharmony_ci		 * need to terminate again.  The exchange is already
406862306a36Sopenharmony_ci		 * cleaned up/freed at FW level.  Just cleanup at driver
406962306a36Sopenharmony_ci		 * level.
407062306a36Sopenharmony_ci		 */
407162306a36Sopenharmony_ci		if ((cmd->state != QLA_TGT_STATE_NEED_DATA) &&
407262306a36Sopenharmony_ci		    (!cmd->aborted)) {
407362306a36Sopenharmony_ci			cmd->trc_flags |= TRC_CTIO_ERR;
407462306a36Sopenharmony_ci			if (qlt_term_ctio_exchange(qpair, ctio, cmd, status))
407562306a36Sopenharmony_ci				return;
407662306a36Sopenharmony_ci		}
407762306a36Sopenharmony_ci	}
407862306a36Sopenharmony_ci
407962306a36Sopenharmony_ci	if (cmd->state == QLA_TGT_STATE_PROCESSED) {
408062306a36Sopenharmony_ci		cmd->trc_flags |= TRC_CTIO_DONE;
408162306a36Sopenharmony_ci	} else if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
408262306a36Sopenharmony_ci		cmd->state = QLA_TGT_STATE_DATA_IN;
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci		if (status == CTIO_SUCCESS)
408562306a36Sopenharmony_ci			cmd->write_data_transferred = 1;
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci		ha->tgt.tgt_ops->handle_data(cmd);
408862306a36Sopenharmony_ci		return;
408962306a36Sopenharmony_ci	} else if (cmd->aborted) {
409062306a36Sopenharmony_ci		cmd->trc_flags |= TRC_CTIO_ABORTED;
409162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e,
409262306a36Sopenharmony_ci		  "Aborted command %p (tag %lld) finished\n", cmd, se_cmd->tag);
409362306a36Sopenharmony_ci	} else {
409462306a36Sopenharmony_ci		cmd->trc_flags |= TRC_CTIO_STRANGE;
409562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c,
409662306a36Sopenharmony_ci		    "qla_target(%d): A command in state (%d) should "
409762306a36Sopenharmony_ci		    "not return a CTIO complete\n", vha->vp_idx, cmd->state);
409862306a36Sopenharmony_ci	}
409962306a36Sopenharmony_ci
410062306a36Sopenharmony_ci	if (unlikely(status != CTIO_SUCCESS) &&
410162306a36Sopenharmony_ci		!cmd->aborted) {
410262306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01f, "Finishing failed CTIO\n");
410362306a36Sopenharmony_ci		dump_stack();
410462306a36Sopenharmony_ci	}
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci	ha->tgt.tgt_ops->free_cmd(cmd);
410762306a36Sopenharmony_ci}
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_cistatic inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha,
411062306a36Sopenharmony_ci	uint8_t task_codes)
411162306a36Sopenharmony_ci{
411262306a36Sopenharmony_ci	int fcp_task_attr;
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ci	switch (task_codes) {
411562306a36Sopenharmony_ci	case ATIO_SIMPLE_QUEUE:
411662306a36Sopenharmony_ci		fcp_task_attr = TCM_SIMPLE_TAG;
411762306a36Sopenharmony_ci		break;
411862306a36Sopenharmony_ci	case ATIO_HEAD_OF_QUEUE:
411962306a36Sopenharmony_ci		fcp_task_attr = TCM_HEAD_TAG;
412062306a36Sopenharmony_ci		break;
412162306a36Sopenharmony_ci	case ATIO_ORDERED_QUEUE:
412262306a36Sopenharmony_ci		fcp_task_attr = TCM_ORDERED_TAG;
412362306a36Sopenharmony_ci		break;
412462306a36Sopenharmony_ci	case ATIO_ACA_QUEUE:
412562306a36Sopenharmony_ci		fcp_task_attr = TCM_ACA_TAG;
412662306a36Sopenharmony_ci		break;
412762306a36Sopenharmony_ci	case ATIO_UNTAGGED:
412862306a36Sopenharmony_ci		fcp_task_attr = TCM_SIMPLE_TAG;
412962306a36Sopenharmony_ci		break;
413062306a36Sopenharmony_ci	default:
413162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05d,
413262306a36Sopenharmony_ci		    "qla_target: unknown task code %x, use ORDERED instead\n",
413362306a36Sopenharmony_ci		    task_codes);
413462306a36Sopenharmony_ci		fcp_task_attr = TCM_ORDERED_TAG;
413562306a36Sopenharmony_ci		break;
413662306a36Sopenharmony_ci	}
413762306a36Sopenharmony_ci
413862306a36Sopenharmony_ci	return fcp_task_attr;
413962306a36Sopenharmony_ci}
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci/*
414262306a36Sopenharmony_ci * Process context for I/O path into tcm_qla2xxx code
414362306a36Sopenharmony_ci */
414462306a36Sopenharmony_cistatic void __qlt_do_work(struct qla_tgt_cmd *cmd)
414562306a36Sopenharmony_ci{
414662306a36Sopenharmony_ci	scsi_qla_host_t *vha = cmd->vha;
414762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
414862306a36Sopenharmony_ci	struct fc_port *sess = cmd->sess;
414962306a36Sopenharmony_ci	struct atio_from_isp *atio = &cmd->atio;
415062306a36Sopenharmony_ci	unsigned char *cdb;
415162306a36Sopenharmony_ci	unsigned long flags;
415262306a36Sopenharmony_ci	uint32_t data_length;
415362306a36Sopenharmony_ci	int ret, fcp_task_attr, data_dir, bidi = 0;
415462306a36Sopenharmony_ci	struct qla_qpair *qpair = cmd->qpair;
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci	cmd->cmd_in_wq = 0;
415762306a36Sopenharmony_ci	cmd->trc_flags |= TRC_DO_WORK;
415862306a36Sopenharmony_ci
415962306a36Sopenharmony_ci	if (cmd->aborted) {
416062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf082,
416162306a36Sopenharmony_ci		    "cmd with tag %u is aborted\n",
416262306a36Sopenharmony_ci		    cmd->atio.u.isp24.exchange_addr);
416362306a36Sopenharmony_ci		goto out_term;
416462306a36Sopenharmony_ci	}
416562306a36Sopenharmony_ci
416662306a36Sopenharmony_ci	spin_lock_init(&cmd->cmd_lock);
416762306a36Sopenharmony_ci	cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
416862306a36Sopenharmony_ci	cmd->se_cmd.tag = le32_to_cpu(atio->u.isp24.exchange_addr);
416962306a36Sopenharmony_ci
417062306a36Sopenharmony_ci	if (atio->u.isp24.fcp_cmnd.rddata &&
417162306a36Sopenharmony_ci	    atio->u.isp24.fcp_cmnd.wrdata) {
417262306a36Sopenharmony_ci		bidi = 1;
417362306a36Sopenharmony_ci		data_dir = DMA_TO_DEVICE;
417462306a36Sopenharmony_ci	} else if (atio->u.isp24.fcp_cmnd.rddata)
417562306a36Sopenharmony_ci		data_dir = DMA_FROM_DEVICE;
417662306a36Sopenharmony_ci	else if (atio->u.isp24.fcp_cmnd.wrdata)
417762306a36Sopenharmony_ci		data_dir = DMA_TO_DEVICE;
417862306a36Sopenharmony_ci	else
417962306a36Sopenharmony_ci		data_dir = DMA_NONE;
418062306a36Sopenharmony_ci
418162306a36Sopenharmony_ci	fcp_task_attr = qlt_get_fcp_task_attr(vha,
418262306a36Sopenharmony_ci	    atio->u.isp24.fcp_cmnd.task_attr);
418362306a36Sopenharmony_ci	data_length = get_datalen_for_atio(atio);
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_ci	ret = ha->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length,
418662306a36Sopenharmony_ci				          fcp_task_attr, data_dir, bidi);
418762306a36Sopenharmony_ci	if (ret != 0)
418862306a36Sopenharmony_ci		goto out_term;
418962306a36Sopenharmony_ci	/*
419062306a36Sopenharmony_ci	 * Drop extra session reference from qlt_handle_cmd_for_atio().
419162306a36Sopenharmony_ci	 */
419262306a36Sopenharmony_ci	ha->tgt.tgt_ops->put_sess(sess);
419362306a36Sopenharmony_ci	return;
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ciout_term:
419662306a36Sopenharmony_ci	ql_dbg(ql_dbg_io, vha, 0x3060, "Terminating work cmd %p", cmd);
419762306a36Sopenharmony_ci	/*
419862306a36Sopenharmony_ci	 * cmd has not sent to target yet, so pass NULL as the second
419962306a36Sopenharmony_ci	 * argument to qlt_send_term_exchange() and free the memory here.
420062306a36Sopenharmony_ci	 */
420162306a36Sopenharmony_ci	cmd->trc_flags |= TRC_DO_WORK_ERR;
420262306a36Sopenharmony_ci	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
420362306a36Sopenharmony_ci	qlt_send_term_exchange(qpair, NULL, &cmd->atio, 1, 0);
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	qlt_decr_num_pend_cmds(vha);
420662306a36Sopenharmony_ci	cmd->vha->hw->tgt.tgt_ops->rel_cmd(cmd);
420762306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci	ha->tgt.tgt_ops->put_sess(sess);
421062306a36Sopenharmony_ci}
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_cistatic void qlt_do_work(struct work_struct *work)
421362306a36Sopenharmony_ci{
421462306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
421562306a36Sopenharmony_ci	scsi_qla_host_t *vha = cmd->vha;
421662306a36Sopenharmony_ci	unsigned long flags;
421762306a36Sopenharmony_ci
421862306a36Sopenharmony_ci	spin_lock_irqsave(&vha->cmd_list_lock, flags);
421962306a36Sopenharmony_ci	list_del(&cmd->cmd_list);
422062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
422162306a36Sopenharmony_ci
422262306a36Sopenharmony_ci	__qlt_do_work(cmd);
422362306a36Sopenharmony_ci}
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_civoid qlt_clr_qp_table(struct scsi_qla_host *vha)
422662306a36Sopenharmony_ci{
422762306a36Sopenharmony_ci	unsigned long flags;
422862306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
422962306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
423062306a36Sopenharmony_ci	void *node;
423162306a36Sopenharmony_ci	u64 key = 0;
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci	ql_log(ql_log_info, vha, 0x706c,
423462306a36Sopenharmony_ci	    "User update Number of Active Qpairs %d\n",
423562306a36Sopenharmony_ci	    ha->tgt.num_act_qpairs);
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.atio_lock, flags);
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci	btree_for_each_safe64(&tgt->lun_qpair_map, key, node)
424062306a36Sopenharmony_ci		btree_remove64(&tgt->lun_qpair_map, key);
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci	ha->base_qpair->lun_cnt = 0;
424362306a36Sopenharmony_ci	for (key = 0; key < ha->max_qpairs; key++)
424462306a36Sopenharmony_ci		if (ha->queue_pair_map[key])
424562306a36Sopenharmony_ci			ha->queue_pair_map[key]->lun_cnt = 0;
424662306a36Sopenharmony_ci
424762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.atio_lock, flags);
424862306a36Sopenharmony_ci}
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_cistatic void qlt_assign_qpair(struct scsi_qla_host *vha,
425162306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd)
425262306a36Sopenharmony_ci{
425362306a36Sopenharmony_ci	struct qla_qpair *qpair, *qp;
425462306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
425562306a36Sopenharmony_ci	struct qla_qpair_hint *h;
425662306a36Sopenharmony_ci
425762306a36Sopenharmony_ci	if (vha->flags.qpairs_available) {
425862306a36Sopenharmony_ci		h = btree_lookup64(&tgt->lun_qpair_map, cmd->unpacked_lun);
425962306a36Sopenharmony_ci		if (unlikely(!h)) {
426062306a36Sopenharmony_ci			/* spread lun to qpair ratio evently */
426162306a36Sopenharmony_ci			int lcnt = 0, rc;
426262306a36Sopenharmony_ci			struct scsi_qla_host *base_vha =
426362306a36Sopenharmony_ci				pci_get_drvdata(vha->hw->pdev);
426462306a36Sopenharmony_ci
426562306a36Sopenharmony_ci			qpair = vha->hw->base_qpair;
426662306a36Sopenharmony_ci			if (qpair->lun_cnt == 0) {
426762306a36Sopenharmony_ci				qpair->lun_cnt++;
426862306a36Sopenharmony_ci				h = qla_qpair_to_hint(tgt, qpair);
426962306a36Sopenharmony_ci				BUG_ON(!h);
427062306a36Sopenharmony_ci				rc = btree_insert64(&tgt->lun_qpair_map,
427162306a36Sopenharmony_ci					cmd->unpacked_lun, h, GFP_ATOMIC);
427262306a36Sopenharmony_ci				if (rc) {
427362306a36Sopenharmony_ci					qpair->lun_cnt--;
427462306a36Sopenharmony_ci					ql_log(ql_log_info, vha, 0xd037,
427562306a36Sopenharmony_ci					    "Unable to insert lun %llx into lun_qpair_map\n",
427662306a36Sopenharmony_ci					    cmd->unpacked_lun);
427762306a36Sopenharmony_ci				}
427862306a36Sopenharmony_ci				goto out;
427962306a36Sopenharmony_ci			} else {
428062306a36Sopenharmony_ci				lcnt = qpair->lun_cnt;
428162306a36Sopenharmony_ci			}
428262306a36Sopenharmony_ci
428362306a36Sopenharmony_ci			h = NULL;
428462306a36Sopenharmony_ci			list_for_each_entry(qp, &base_vha->qp_list,
428562306a36Sopenharmony_ci			    qp_list_elem) {
428662306a36Sopenharmony_ci				if (qp->lun_cnt == 0) {
428762306a36Sopenharmony_ci					qp->lun_cnt++;
428862306a36Sopenharmony_ci					h = qla_qpair_to_hint(tgt, qp);
428962306a36Sopenharmony_ci					BUG_ON(!h);
429062306a36Sopenharmony_ci					rc = btree_insert64(&tgt->lun_qpair_map,
429162306a36Sopenharmony_ci					    cmd->unpacked_lun, h, GFP_ATOMIC);
429262306a36Sopenharmony_ci					if (rc) {
429362306a36Sopenharmony_ci						qp->lun_cnt--;
429462306a36Sopenharmony_ci						ql_log(ql_log_info, vha, 0xd038,
429562306a36Sopenharmony_ci							"Unable to insert lun %llx into lun_qpair_map\n",
429662306a36Sopenharmony_ci							cmd->unpacked_lun);
429762306a36Sopenharmony_ci					}
429862306a36Sopenharmony_ci					qpair = qp;
429962306a36Sopenharmony_ci					goto out;
430062306a36Sopenharmony_ci				} else {
430162306a36Sopenharmony_ci					if (qp->lun_cnt < lcnt) {
430262306a36Sopenharmony_ci						lcnt = qp->lun_cnt;
430362306a36Sopenharmony_ci						qpair = qp;
430462306a36Sopenharmony_ci						continue;
430562306a36Sopenharmony_ci					}
430662306a36Sopenharmony_ci				}
430762306a36Sopenharmony_ci			}
430862306a36Sopenharmony_ci			BUG_ON(!qpair);
430962306a36Sopenharmony_ci			qpair->lun_cnt++;
431062306a36Sopenharmony_ci			h = qla_qpair_to_hint(tgt, qpair);
431162306a36Sopenharmony_ci			BUG_ON(!h);
431262306a36Sopenharmony_ci			rc = btree_insert64(&tgt->lun_qpair_map,
431362306a36Sopenharmony_ci				cmd->unpacked_lun, h, GFP_ATOMIC);
431462306a36Sopenharmony_ci			if (rc) {
431562306a36Sopenharmony_ci				qpair->lun_cnt--;
431662306a36Sopenharmony_ci				ql_log(ql_log_info, vha, 0xd039,
431762306a36Sopenharmony_ci				   "Unable to insert lun %llx into lun_qpair_map\n",
431862306a36Sopenharmony_ci				   cmd->unpacked_lun);
431962306a36Sopenharmony_ci			}
432062306a36Sopenharmony_ci		}
432162306a36Sopenharmony_ci	} else {
432262306a36Sopenharmony_ci		h = &tgt->qphints[0];
432362306a36Sopenharmony_ci	}
432462306a36Sopenharmony_ciout:
432562306a36Sopenharmony_ci	cmd->qpair = h->qpair;
432662306a36Sopenharmony_ci	cmd->se_cmd.cpuid = h->cpuid;
432762306a36Sopenharmony_ci}
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_cistatic struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha,
433062306a36Sopenharmony_ci				       struct fc_port *sess,
433162306a36Sopenharmony_ci				       struct atio_from_isp *atio)
433262306a36Sopenharmony_ci{
433362306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
433462306a36Sopenharmony_ci
433562306a36Sopenharmony_ci	cmd = vha->hw->tgt.tgt_ops->get_cmd(sess);
433662306a36Sopenharmony_ci	if (!cmd)
433762306a36Sopenharmony_ci		return NULL;
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci	cmd->cmd_type = TYPE_TGT_CMD;
434062306a36Sopenharmony_ci	memcpy(&cmd->atio, atio, sizeof(*atio));
434162306a36Sopenharmony_ci	INIT_LIST_HEAD(&cmd->sess_cmd_list);
434262306a36Sopenharmony_ci	cmd->state = QLA_TGT_STATE_NEW;
434362306a36Sopenharmony_ci	cmd->tgt = vha->vha_tgt.qla_tgt;
434462306a36Sopenharmony_ci	qlt_incr_num_pend_cmds(vha);
434562306a36Sopenharmony_ci	cmd->vha = vha;
434662306a36Sopenharmony_ci	cmd->sess = sess;
434762306a36Sopenharmony_ci	cmd->loop_id = sess->loop_id;
434862306a36Sopenharmony_ci	cmd->conf_compl_supported = sess->conf_compl_supported;
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	cmd->trc_flags = 0;
435162306a36Sopenharmony_ci	cmd->jiffies_at_alloc = get_jiffies_64();
435262306a36Sopenharmony_ci
435362306a36Sopenharmony_ci	cmd->unpacked_lun = scsilun_to_int(
435462306a36Sopenharmony_ci	    (struct scsi_lun *)&atio->u.isp24.fcp_cmnd.lun);
435562306a36Sopenharmony_ci	qlt_assign_qpair(vha, cmd);
435662306a36Sopenharmony_ci	cmd->reset_count = vha->hw->base_qpair->chip_reset;
435762306a36Sopenharmony_ci	cmd->vp_idx = vha->vp_idx;
435862306a36Sopenharmony_ci	cmd->edif = sess->edif.enable;
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	return cmd;
436162306a36Sopenharmony_ci}
436262306a36Sopenharmony_ci
436362306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
436462306a36Sopenharmony_cistatic int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
436562306a36Sopenharmony_ci	struct atio_from_isp *atio)
436662306a36Sopenharmony_ci{
436762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
436862306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
436962306a36Sopenharmony_ci	struct fc_port *sess;
437062306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
437162306a36Sopenharmony_ci	unsigned long flags;
437262306a36Sopenharmony_ci	port_id_t id;
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci	if (unlikely(tgt->tgt_stop)) {
437562306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, vha, 0x3061,
437662306a36Sopenharmony_ci		    "New command while device %p is shutting down\n", tgt);
437762306a36Sopenharmony_ci		return -ENODEV;
437862306a36Sopenharmony_ci	}
437962306a36Sopenharmony_ci
438062306a36Sopenharmony_ci	id = be_to_port_id(atio->u.isp24.fcp_hdr.s_id);
438162306a36Sopenharmony_ci	if (IS_SW_RESV_ADDR(id))
438262306a36Sopenharmony_ci		return -EBUSY;
438362306a36Sopenharmony_ci
438462306a36Sopenharmony_ci	sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, atio->u.isp24.fcp_hdr.s_id);
438562306a36Sopenharmony_ci	if (unlikely(!sess))
438662306a36Sopenharmony_ci		return -EFAULT;
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	/* Another WWN used to have our s_id. Our PLOGI scheduled its
438962306a36Sopenharmony_ci	 * session deletion, but it's still in sess_del_work wq */
439062306a36Sopenharmony_ci	if (sess->deleted) {
439162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf002,
439262306a36Sopenharmony_ci		    "New command while old session %p is being deleted\n",
439362306a36Sopenharmony_ci		    sess);
439462306a36Sopenharmony_ci		return -EFAULT;
439562306a36Sopenharmony_ci	}
439662306a36Sopenharmony_ci
439762306a36Sopenharmony_ci	/*
439862306a36Sopenharmony_ci	 * Do kref_get() before returning + dropping qla_hw_data->hardware_lock.
439962306a36Sopenharmony_ci	 */
440062306a36Sopenharmony_ci	if (!kref_get_unless_zero(&sess->sess_kref)) {
440162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
440262306a36Sopenharmony_ci		    "%s: kref_get fail, %8phC oxid %x \n",
440362306a36Sopenharmony_ci		    __func__, sess->port_name,
440462306a36Sopenharmony_ci		     be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id));
440562306a36Sopenharmony_ci		return -EFAULT;
440662306a36Sopenharmony_ci	}
440762306a36Sopenharmony_ci
440862306a36Sopenharmony_ci	cmd = qlt_get_tag(vha, sess, atio);
440962306a36Sopenharmony_ci	if (!cmd) {
441062306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, vha, 0x3062,
441162306a36Sopenharmony_ci		    "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx);
441262306a36Sopenharmony_ci		ha->tgt.tgt_ops->put_sess(sess);
441362306a36Sopenharmony_ci		return -EBUSY;
441462306a36Sopenharmony_ci	}
441562306a36Sopenharmony_ci
441662306a36Sopenharmony_ci	cmd->cmd_in_wq = 1;
441762306a36Sopenharmony_ci	cmd->trc_flags |= TRC_NEW_CMD;
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	spin_lock_irqsave(&vha->cmd_list_lock, flags);
442062306a36Sopenharmony_ci	list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list);
442162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci	INIT_WORK(&cmd->work, qlt_do_work);
442462306a36Sopenharmony_ci	if (vha->flags.qpairs_available) {
442562306a36Sopenharmony_ci		queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq, &cmd->work);
442662306a36Sopenharmony_ci	} else if (ha->msix_count) {
442762306a36Sopenharmony_ci		if (cmd->atio.u.isp24.fcp_cmnd.rddata)
442862306a36Sopenharmony_ci			queue_work(qla_tgt_wq, &cmd->work);
442962306a36Sopenharmony_ci		else
443062306a36Sopenharmony_ci			queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq,
443162306a36Sopenharmony_ci			    &cmd->work);
443262306a36Sopenharmony_ci	} else {
443362306a36Sopenharmony_ci		queue_work(qla_tgt_wq, &cmd->work);
443462306a36Sopenharmony_ci	}
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci	return 0;
443762306a36Sopenharmony_ci}
443862306a36Sopenharmony_ci
443962306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
444062306a36Sopenharmony_cistatic int qlt_issue_task_mgmt(struct fc_port *sess, u64 lun,
444162306a36Sopenharmony_ci	int fn, void *iocb, int flags)
444262306a36Sopenharmony_ci{
444362306a36Sopenharmony_ci	struct scsi_qla_host *vha = sess->vha;
444462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
444562306a36Sopenharmony_ci	struct qla_tgt_mgmt_cmd *mcmd;
444662306a36Sopenharmony_ci	struct atio_from_isp *a = (struct atio_from_isp *)iocb;
444762306a36Sopenharmony_ci	struct qla_qpair_hint *h = &vha->vha_tgt.qla_tgt->qphints[0];
444862306a36Sopenharmony_ci
444962306a36Sopenharmony_ci	mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
445062306a36Sopenharmony_ci	if (!mcmd) {
445162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_tmr, vha, 0x10009,
445262306a36Sopenharmony_ci		    "qla_target(%d): Allocation of management "
445362306a36Sopenharmony_ci		    "command failed, some commands and their data could "
445462306a36Sopenharmony_ci		    "leak\n", vha->vp_idx);
445562306a36Sopenharmony_ci		return -ENOMEM;
445662306a36Sopenharmony_ci	}
445762306a36Sopenharmony_ci	memset(mcmd, 0, sizeof(*mcmd));
445862306a36Sopenharmony_ci	mcmd->sess = sess;
445962306a36Sopenharmony_ci
446062306a36Sopenharmony_ci	if (iocb) {
446162306a36Sopenharmony_ci		memcpy(&mcmd->orig_iocb.imm_ntfy, iocb,
446262306a36Sopenharmony_ci		    sizeof(mcmd->orig_iocb.imm_ntfy));
446362306a36Sopenharmony_ci	}
446462306a36Sopenharmony_ci	mcmd->tmr_func = fn;
446562306a36Sopenharmony_ci	mcmd->flags = flags;
446662306a36Sopenharmony_ci	mcmd->reset_count = ha->base_qpair->chip_reset;
446762306a36Sopenharmony_ci	mcmd->qpair = h->qpair;
446862306a36Sopenharmony_ci	mcmd->vha = vha;
446962306a36Sopenharmony_ci	mcmd->se_cmd.cpuid = h->cpuid;
447062306a36Sopenharmony_ci	mcmd->unpacked_lun = lun;
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_ci	switch (fn) {
447362306a36Sopenharmony_ci	case QLA_TGT_LUN_RESET:
447462306a36Sopenharmony_ci	case QLA_TGT_CLEAR_TS:
447562306a36Sopenharmony_ci	case QLA_TGT_ABORT_TS:
447662306a36Sopenharmony_ci		abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id);
447762306a36Sopenharmony_ci		fallthrough;
447862306a36Sopenharmony_ci	case QLA_TGT_CLEAR_ACA:
447962306a36Sopenharmony_ci		h = qlt_find_qphint(vha, mcmd->unpacked_lun);
448062306a36Sopenharmony_ci		mcmd->qpair = h->qpair;
448162306a36Sopenharmony_ci		mcmd->se_cmd.cpuid = h->cpuid;
448262306a36Sopenharmony_ci		break;
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_ci	case QLA_TGT_TARGET_RESET:
448562306a36Sopenharmony_ci	case QLA_TGT_NEXUS_LOSS_SESS:
448662306a36Sopenharmony_ci	case QLA_TGT_NEXUS_LOSS:
448762306a36Sopenharmony_ci	case QLA_TGT_ABORT_ALL:
448862306a36Sopenharmony_ci	default:
448962306a36Sopenharmony_ci		/* no-op */
449062306a36Sopenharmony_ci		break;
449162306a36Sopenharmony_ci	}
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci	INIT_WORK(&mcmd->work, qlt_do_tmr_work);
449462306a36Sopenharmony_ci	queue_work_on(mcmd->se_cmd.cpuid, qla_tgt_wq,
449562306a36Sopenharmony_ci	    &mcmd->work);
449662306a36Sopenharmony_ci
449762306a36Sopenharmony_ci	return 0;
449862306a36Sopenharmony_ci}
449962306a36Sopenharmony_ci
450062306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
450162306a36Sopenharmony_cistatic int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb)
450262306a36Sopenharmony_ci{
450362306a36Sopenharmony_ci	struct atio_from_isp *a = (struct atio_from_isp *)iocb;
450462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
450562306a36Sopenharmony_ci	struct fc_port *sess;
450662306a36Sopenharmony_ci	u64 unpacked_lun;
450762306a36Sopenharmony_ci	int fn;
450862306a36Sopenharmony_ci	unsigned long flags;
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_ci	fn = a->u.isp24.fcp_cmnd.task_mgmt_flags;
451162306a36Sopenharmony_ci
451262306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
451362306a36Sopenharmony_ci	sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha,
451462306a36Sopenharmony_ci	    a->u.isp24.fcp_hdr.s_id);
451562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
451662306a36Sopenharmony_ci
451762306a36Sopenharmony_ci	unpacked_lun =
451862306a36Sopenharmony_ci	    scsilun_to_int((struct scsi_lun *)&a->u.isp24.fcp_cmnd.lun);
451962306a36Sopenharmony_ci
452062306a36Sopenharmony_ci	if (sess == NULL || sess->deleted)
452162306a36Sopenharmony_ci		return -EFAULT;
452262306a36Sopenharmony_ci
452362306a36Sopenharmony_ci	return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
452462306a36Sopenharmony_ci}
452562306a36Sopenharmony_ci
452662306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
452762306a36Sopenharmony_cistatic int __qlt_abort_task(struct scsi_qla_host *vha,
452862306a36Sopenharmony_ci	struct imm_ntfy_from_isp *iocb, struct fc_port *sess)
452962306a36Sopenharmony_ci{
453062306a36Sopenharmony_ci	struct atio_from_isp *a = (struct atio_from_isp *)iocb;
453162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
453262306a36Sopenharmony_ci	struct qla_tgt_mgmt_cmd *mcmd;
453362306a36Sopenharmony_ci	u64 unpacked_lun;
453462306a36Sopenharmony_ci	int rc;
453562306a36Sopenharmony_ci
453662306a36Sopenharmony_ci	mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC);
453762306a36Sopenharmony_ci	if (mcmd == NULL) {
453862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05f,
453962306a36Sopenharmony_ci		    "qla_target(%d): %s: Allocation of ABORT cmd failed\n",
454062306a36Sopenharmony_ci		    vha->vp_idx, __func__);
454162306a36Sopenharmony_ci		return -ENOMEM;
454262306a36Sopenharmony_ci	}
454362306a36Sopenharmony_ci	memset(mcmd, 0, sizeof(*mcmd));
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci	mcmd->sess = sess;
454662306a36Sopenharmony_ci	memcpy(&mcmd->orig_iocb.imm_ntfy, iocb,
454762306a36Sopenharmony_ci	    sizeof(mcmd->orig_iocb.imm_ntfy));
454862306a36Sopenharmony_ci
454962306a36Sopenharmony_ci	unpacked_lun =
455062306a36Sopenharmony_ci	    scsilun_to_int((struct scsi_lun *)&a->u.isp24.fcp_cmnd.lun);
455162306a36Sopenharmony_ci	mcmd->reset_count = ha->base_qpair->chip_reset;
455262306a36Sopenharmony_ci	mcmd->tmr_func = QLA_TGT_2G_ABORT_TASK;
455362306a36Sopenharmony_ci	mcmd->qpair = ha->base_qpair;
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci	rc = ha->tgt.tgt_ops->handle_tmr(mcmd, unpacked_lun, mcmd->tmr_func,
455662306a36Sopenharmony_ci	    le16_to_cpu(iocb->u.isp2x.seq_id));
455762306a36Sopenharmony_ci	if (rc != 0) {
455862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf060,
455962306a36Sopenharmony_ci		    "qla_target(%d): tgt_ops->handle_tmr() failed: %d\n",
456062306a36Sopenharmony_ci		    vha->vp_idx, rc);
456162306a36Sopenharmony_ci		mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
456262306a36Sopenharmony_ci		return -EFAULT;
456362306a36Sopenharmony_ci	}
456462306a36Sopenharmony_ci
456562306a36Sopenharmony_ci	return 0;
456662306a36Sopenharmony_ci}
456762306a36Sopenharmony_ci
456862306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
456962306a36Sopenharmony_cistatic int qlt_abort_task(struct scsi_qla_host *vha,
457062306a36Sopenharmony_ci	struct imm_ntfy_from_isp *iocb)
457162306a36Sopenharmony_ci{
457262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
457362306a36Sopenharmony_ci	struct fc_port *sess;
457462306a36Sopenharmony_ci	int loop_id;
457562306a36Sopenharmony_ci	unsigned long flags;
457662306a36Sopenharmony_ci
457762306a36Sopenharmony_ci	loop_id = GET_TARGET_ID(ha, (struct atio_from_isp *)iocb);
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
458062306a36Sopenharmony_ci	sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id);
458162306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
458262306a36Sopenharmony_ci
458362306a36Sopenharmony_ci	if (sess == NULL) {
458462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf025,
458562306a36Sopenharmony_ci		    "qla_target(%d): task abort for unexisting "
458662306a36Sopenharmony_ci		    "session\n", vha->vp_idx);
458762306a36Sopenharmony_ci		return qlt_sched_sess_work(vha->vha_tgt.qla_tgt,
458862306a36Sopenharmony_ci		    QLA_TGT_SESS_WORK_ABORT, iocb, sizeof(*iocb));
458962306a36Sopenharmony_ci	}
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci	return __qlt_abort_task(vha, iocb, sess);
459262306a36Sopenharmony_ci}
459362306a36Sopenharmony_ci
459462306a36Sopenharmony_civoid qlt_logo_completion_handler(fc_port_t *fcport, int rc)
459562306a36Sopenharmony_ci{
459662306a36Sopenharmony_ci	if (rc != MBS_COMMAND_COMPLETE) {
459762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093,
459862306a36Sopenharmony_ci			"%s: se_sess %p / sess %p from"
459962306a36Sopenharmony_ci			" port %8phC loop_id %#04x s_id %02x:%02x:%02x"
460062306a36Sopenharmony_ci			" LOGO failed: %#x\n",
460162306a36Sopenharmony_ci			__func__,
460262306a36Sopenharmony_ci			fcport->se_sess,
460362306a36Sopenharmony_ci			fcport,
460462306a36Sopenharmony_ci			fcport->port_name, fcport->loop_id,
460562306a36Sopenharmony_ci			fcport->d_id.b.domain, fcport->d_id.b.area,
460662306a36Sopenharmony_ci			fcport->d_id.b.al_pa, rc);
460762306a36Sopenharmony_ci	}
460862306a36Sopenharmony_ci
460962306a36Sopenharmony_ci	fcport->logout_completed = 1;
461062306a36Sopenharmony_ci}
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci/*
461362306a36Sopenharmony_ci* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list)
461462306a36Sopenharmony_ci*
461562306a36Sopenharmony_ci* Schedules sessions with matching port_id/loop_id but different wwn for
461662306a36Sopenharmony_ci* deletion. Returns existing session with matching wwn if present.
461762306a36Sopenharmony_ci* Null otherwise.
461862306a36Sopenharmony_ci*/
461962306a36Sopenharmony_cistruct fc_port *
462062306a36Sopenharmony_ciqlt_find_sess_invalidate_other(scsi_qla_host_t *vha, uint64_t wwn,
462162306a36Sopenharmony_ci    port_id_t port_id, uint16_t loop_id, struct fc_port **conflict_sess)
462262306a36Sopenharmony_ci{
462362306a36Sopenharmony_ci	struct fc_port *sess = NULL, *other_sess;
462462306a36Sopenharmony_ci	uint64_t other_wwn;
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci	*conflict_sess = NULL;
462762306a36Sopenharmony_ci
462862306a36Sopenharmony_ci	list_for_each_entry(other_sess, &vha->vp_fcports, list) {
462962306a36Sopenharmony_ci
463062306a36Sopenharmony_ci		other_wwn = wwn_to_u64(other_sess->port_name);
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci		if (wwn == other_wwn) {
463362306a36Sopenharmony_ci			WARN_ON(sess);
463462306a36Sopenharmony_ci			sess = other_sess;
463562306a36Sopenharmony_ci			continue;
463662306a36Sopenharmony_ci		}
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci		/* find other sess with nport_id collision */
463962306a36Sopenharmony_ci		if (port_id.b24 == other_sess->d_id.b24) {
464062306a36Sopenharmony_ci			if (loop_id != other_sess->loop_id) {
464162306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0x1000c,
464262306a36Sopenharmony_ci				    "Invalidating sess %p loop_id %d wwn %llx.\n",
464362306a36Sopenharmony_ci				    other_sess, other_sess->loop_id, other_wwn);
464462306a36Sopenharmony_ci
464562306a36Sopenharmony_ci				/*
464662306a36Sopenharmony_ci				 * logout_on_delete is set by default, but another
464762306a36Sopenharmony_ci				 * session that has the same s_id/loop_id combo
464862306a36Sopenharmony_ci				 * might have cleared it when requested this session
464962306a36Sopenharmony_ci				 * deletion, so don't touch it
465062306a36Sopenharmony_ci				 */
465162306a36Sopenharmony_ci				qlt_schedule_sess_for_deletion(other_sess);
465262306a36Sopenharmony_ci			} else {
465362306a36Sopenharmony_ci				/*
465462306a36Sopenharmony_ci				 * Another wwn used to have our s_id/loop_id
465562306a36Sopenharmony_ci				 * kill the session, but don't free the loop_id
465662306a36Sopenharmony_ci				 */
465762306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0xf01b,
465862306a36Sopenharmony_ci				    "Invalidating sess %p loop_id %d wwn %llx.\n",
465962306a36Sopenharmony_ci				    other_sess, other_sess->loop_id, other_wwn);
466062306a36Sopenharmony_ci
466162306a36Sopenharmony_ci				other_sess->keep_nport_handle = 1;
466262306a36Sopenharmony_ci				if (other_sess->disc_state != DSC_DELETED)
466362306a36Sopenharmony_ci					*conflict_sess = other_sess;
466462306a36Sopenharmony_ci				qlt_schedule_sess_for_deletion(other_sess);
466562306a36Sopenharmony_ci			}
466662306a36Sopenharmony_ci			continue;
466762306a36Sopenharmony_ci		}
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci		/* find other sess with nport handle collision */
467062306a36Sopenharmony_ci		if ((loop_id == other_sess->loop_id) &&
467162306a36Sopenharmony_ci			(loop_id != FC_NO_LOOP_ID)) {
467262306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0x1000d,
467362306a36Sopenharmony_ci			       "Invalidating sess %p loop_id %d wwn %llx.\n",
467462306a36Sopenharmony_ci			       other_sess, other_sess->loop_id, other_wwn);
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci			/* Same loop_id but different s_id
467762306a36Sopenharmony_ci			 * Ok to kill and logout */
467862306a36Sopenharmony_ci			qlt_schedule_sess_for_deletion(other_sess);
467962306a36Sopenharmony_ci		}
468062306a36Sopenharmony_ci	}
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	return sess;
468362306a36Sopenharmony_ci}
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_ci/* Abort any commands for this s_id waiting on qla_tgt_wq workqueue */
468662306a36Sopenharmony_cistatic int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id)
468762306a36Sopenharmony_ci{
468862306a36Sopenharmony_ci	struct qla_tgt_sess_op *op;
468962306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
469062306a36Sopenharmony_ci	uint32_t key;
469162306a36Sopenharmony_ci	int count = 0;
469262306a36Sopenharmony_ci	unsigned long flags;
469362306a36Sopenharmony_ci
469462306a36Sopenharmony_ci	key = (((u32)s_id->b.domain << 16) |
469562306a36Sopenharmony_ci	       ((u32)s_id->b.area   <<  8) |
469662306a36Sopenharmony_ci	       ((u32)s_id->b.al_pa));
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci	spin_lock_irqsave(&vha->cmd_list_lock, flags);
469962306a36Sopenharmony_ci	list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) {
470062306a36Sopenharmony_ci		uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_ci		if (op_key == key) {
470362306a36Sopenharmony_ci			op->aborted = true;
470462306a36Sopenharmony_ci			count++;
470562306a36Sopenharmony_ci		}
470662306a36Sopenharmony_ci	}
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_ci	list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
470962306a36Sopenharmony_ci		uint32_t cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id);
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_ci		if (cmd_key == key) {
471262306a36Sopenharmony_ci			cmd->aborted = 1;
471362306a36Sopenharmony_ci			count++;
471462306a36Sopenharmony_ci		}
471562306a36Sopenharmony_ci	}
471662306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
471762306a36Sopenharmony_ci
471862306a36Sopenharmony_ci	return count;
471962306a36Sopenharmony_ci}
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_cistatic int qlt_handle_login(struct scsi_qla_host *vha,
472262306a36Sopenharmony_ci    struct imm_ntfy_from_isp *iocb)
472362306a36Sopenharmony_ci{
472462306a36Sopenharmony_ci	struct fc_port *sess = NULL, *conflict_sess = NULL;
472562306a36Sopenharmony_ci	uint64_t wwn;
472662306a36Sopenharmony_ci	port_id_t port_id;
472762306a36Sopenharmony_ci	uint16_t loop_id, wd3_lo;
472862306a36Sopenharmony_ci	int res = 0;
472962306a36Sopenharmony_ci	struct qlt_plogi_ack_t *pla;
473062306a36Sopenharmony_ci	unsigned long flags;
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci	lockdep_assert_held(&vha->hw->hardware_lock);
473362306a36Sopenharmony_ci
473462306a36Sopenharmony_ci	wwn = wwn_to_u64(iocb->u.isp24.port_name);
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_ci	port_id.b.domain = iocb->u.isp24.port_id[2];
473762306a36Sopenharmony_ci	port_id.b.area   = iocb->u.isp24.port_id[1];
473862306a36Sopenharmony_ci	port_id.b.al_pa  = iocb->u.isp24.port_id[0];
473962306a36Sopenharmony_ci	port_id.b.rsvd_1 = 0;
474062306a36Sopenharmony_ci
474162306a36Sopenharmony_ci	loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
474262306a36Sopenharmony_ci
474362306a36Sopenharmony_ci	/* Mark all stale commands sitting in qla_tgt_wq for deletion */
474462306a36Sopenharmony_ci	abort_cmds_for_s_id(vha, &port_id);
474562306a36Sopenharmony_ci
474662306a36Sopenharmony_ci	if (wwn) {
474762306a36Sopenharmony_ci		spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
474862306a36Sopenharmony_ci		sess = qlt_find_sess_invalidate_other(vha, wwn,
474962306a36Sopenharmony_ci		    port_id, loop_id, &conflict_sess);
475062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
475162306a36Sopenharmony_ci	} else {
475262306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0xffff,
475362306a36Sopenharmony_ci		    "%s %d Term INOT due to WWN=0 lid=%d, NportID %06X ",
475462306a36Sopenharmony_ci		    __func__, __LINE__, loop_id, port_id.b24);
475562306a36Sopenharmony_ci		qlt_send_term_imm_notif(vha, iocb, 1);
475662306a36Sopenharmony_ci		goto out;
475762306a36Sopenharmony_ci	}
475862306a36Sopenharmony_ci
475962306a36Sopenharmony_ci	if (IS_SW_RESV_ADDR(port_id)) {
476062306a36Sopenharmony_ci		res = 1;
476162306a36Sopenharmony_ci		goto out;
476262306a36Sopenharmony_ci	}
476362306a36Sopenharmony_ci
476462306a36Sopenharmony_ci	if (vha->hw->flags.edif_enabled &&
476562306a36Sopenharmony_ci	    !(vha->e_dbell.db_flags & EDB_ACTIVE) &&
476662306a36Sopenharmony_ci	    iocb->u.isp24.status_subcode == ELS_PLOGI &&
476762306a36Sopenharmony_ci	    !(le16_to_cpu(iocb->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP)) {
476862306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0xffff,
476962306a36Sopenharmony_ci			"%s %d Term INOT due to app not available lid=%d, NportID %06X ",
477062306a36Sopenharmony_ci			__func__, __LINE__, loop_id, port_id.b24);
477162306a36Sopenharmony_ci		qlt_send_term_imm_notif(vha, iocb, 1);
477262306a36Sopenharmony_ci		goto out;
477362306a36Sopenharmony_ci	}
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_ci	if (vha->hw->flags.edif_enabled) {
477662306a36Sopenharmony_ci		if (DBELL_INACTIVE(vha)) {
477762306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0xffff,
477862306a36Sopenharmony_ci			       "%s %d Term INOT due to app not started lid=%d, NportID %06X ",
477962306a36Sopenharmony_ci			       __func__, __LINE__, loop_id, port_id.b24);
478062306a36Sopenharmony_ci			qlt_send_term_imm_notif(vha, iocb, 1);
478162306a36Sopenharmony_ci			goto out;
478262306a36Sopenharmony_ci		} else if (iocb->u.isp24.status_subcode == ELS_PLOGI &&
478362306a36Sopenharmony_ci			   !(le16_to_cpu(iocb->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP)) {
478462306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0xffff,
478562306a36Sopenharmony_ci			       "%s %d Term INOT due to unsecure lid=%d, NportID %06X ",
478662306a36Sopenharmony_ci			       __func__, __LINE__, loop_id, port_id.b24);
478762306a36Sopenharmony_ci			qlt_send_term_imm_notif(vha, iocb, 1);
478862306a36Sopenharmony_ci			goto out;
478962306a36Sopenharmony_ci		}
479062306a36Sopenharmony_ci	}
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	pla = qlt_plogi_ack_find_add(vha, &port_id, iocb);
479362306a36Sopenharmony_ci	if (!pla) {
479462306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xffff,
479562306a36Sopenharmony_ci		    "%s %d %8phC Term INOT due to mem alloc fail",
479662306a36Sopenharmony_ci		    __func__, __LINE__,
479762306a36Sopenharmony_ci		    iocb->u.isp24.port_name);
479862306a36Sopenharmony_ci		qlt_send_term_imm_notif(vha, iocb, 1);
479962306a36Sopenharmony_ci		goto out;
480062306a36Sopenharmony_ci	}
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci	if (conflict_sess) {
480362306a36Sopenharmony_ci		conflict_sess->login_gen++;
480462306a36Sopenharmony_ci		qlt_plogi_ack_link(vha, pla, conflict_sess,
480562306a36Sopenharmony_ci		    QLT_PLOGI_LINK_CONFLICT);
480662306a36Sopenharmony_ci	}
480762306a36Sopenharmony_ci
480862306a36Sopenharmony_ci	if (!sess) {
480962306a36Sopenharmony_ci		pla->ref_count++;
481062306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0xffff,
481162306a36Sopenharmony_ci		    "%s %d %8phC post new sess\n",
481262306a36Sopenharmony_ci		    __func__, __LINE__, iocb->u.isp24.port_name);
481362306a36Sopenharmony_ci		if (iocb->u.isp24.status_subcode == ELS_PLOGI)
481462306a36Sopenharmony_ci			qla24xx_post_newsess_work(vha, &port_id,
481562306a36Sopenharmony_ci			    iocb->u.isp24.port_name,
481662306a36Sopenharmony_ci			    iocb->u.isp24.u.plogi.node_name,
481762306a36Sopenharmony_ci			    pla, 0);
481862306a36Sopenharmony_ci		else
481962306a36Sopenharmony_ci			qla24xx_post_newsess_work(vha, &port_id,
482062306a36Sopenharmony_ci			    iocb->u.isp24.port_name, NULL,
482162306a36Sopenharmony_ci			    pla, 0);
482262306a36Sopenharmony_ci
482362306a36Sopenharmony_ci		goto out;
482462306a36Sopenharmony_ci	}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	if (sess->disc_state == DSC_UPD_FCPORT) {
482762306a36Sopenharmony_ci		u16 sec;
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci		/*
483062306a36Sopenharmony_ci		 * Remote port registration is still going on from
483162306a36Sopenharmony_ci		 * previous login. Allow it to finish before we
483262306a36Sopenharmony_ci		 * accept the new login.
483362306a36Sopenharmony_ci		 */
483462306a36Sopenharmony_ci		sess->next_disc_state = DSC_DELETE_PEND;
483562306a36Sopenharmony_ci		sec = jiffies_to_msecs(jiffies -
483662306a36Sopenharmony_ci		    sess->jiffies_at_registration) / 1000;
483762306a36Sopenharmony_ci		if (sess->sec_since_registration < sec && sec &&
483862306a36Sopenharmony_ci		    !(sec % 5)) {
483962306a36Sopenharmony_ci			sess->sec_since_registration = sec;
484062306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0xffff,
484162306a36Sopenharmony_ci			    "%s %8phC - Slow Rport registration (%d Sec)\n",
484262306a36Sopenharmony_ci			    __func__, sess->port_name, sec);
484362306a36Sopenharmony_ci		}
484462306a36Sopenharmony_ci
484562306a36Sopenharmony_ci		if (!conflict_sess) {
484662306a36Sopenharmony_ci			list_del(&pla->list);
484762306a36Sopenharmony_ci			kmem_cache_free(qla_tgt_plogi_cachep, pla);
484862306a36Sopenharmony_ci		}
484962306a36Sopenharmony_ci
485062306a36Sopenharmony_ci		qlt_send_term_imm_notif(vha, iocb, 1);
485162306a36Sopenharmony_ci		goto out;
485262306a36Sopenharmony_ci	}
485362306a36Sopenharmony_ci
485462306a36Sopenharmony_ci	qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN);
485562306a36Sopenharmony_ci	sess->d_id = port_id;
485662306a36Sopenharmony_ci	sess->login_gen++;
485762306a36Sopenharmony_ci	sess->loop_id = loop_id;
485862306a36Sopenharmony_ci
485962306a36Sopenharmony_ci	if (iocb->u.isp24.status_subcode == ELS_PLOGI) {
486062306a36Sopenharmony_ci		/* remote port has assigned Port ID */
486162306a36Sopenharmony_ci		if (N2N_TOPO(vha->hw) && fcport_is_bigger(sess))
486262306a36Sopenharmony_ci			vha->d_id = sess->d_id;
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0xffff,
486562306a36Sopenharmony_ci		    "%s %8phC - send port online\n",
486662306a36Sopenharmony_ci		    __func__, sess->port_name);
486762306a36Sopenharmony_ci
486862306a36Sopenharmony_ci		qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE,
486962306a36Sopenharmony_ci		    sess->d_id.b24);
487062306a36Sopenharmony_ci	}
487162306a36Sopenharmony_ci
487262306a36Sopenharmony_ci	if (iocb->u.isp24.status_subcode == ELS_PRLI) {
487362306a36Sopenharmony_ci		sess->fw_login_state = DSC_LS_PRLI_PEND;
487462306a36Sopenharmony_ci		sess->local = 0;
487562306a36Sopenharmony_ci		sess->loop_id = loop_id;
487662306a36Sopenharmony_ci		sess->d_id = port_id;
487762306a36Sopenharmony_ci		sess->fw_login_state = DSC_LS_PRLI_PEND;
487862306a36Sopenharmony_ci		wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo);
487962306a36Sopenharmony_ci
488062306a36Sopenharmony_ci		if (wd3_lo & BIT_7)
488162306a36Sopenharmony_ci			sess->conf_compl_supported = 1;
488262306a36Sopenharmony_ci
488362306a36Sopenharmony_ci		if ((wd3_lo & BIT_4) == 0)
488462306a36Sopenharmony_ci			sess->port_type = FCT_INITIATOR;
488562306a36Sopenharmony_ci		else
488662306a36Sopenharmony_ci			sess->port_type = FCT_TARGET;
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_ci	} else
488962306a36Sopenharmony_ci		sess->fw_login_state = DSC_LS_PLOGI_PEND;
489062306a36Sopenharmony_ci
489162306a36Sopenharmony_ci
489262306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0x20f9,
489362306a36Sopenharmony_ci	    "%s %d %8phC  DS %d\n",
489462306a36Sopenharmony_ci	    __func__, __LINE__, sess->port_name, sess->disc_state);
489562306a36Sopenharmony_ci
489662306a36Sopenharmony_ci	switch (sess->disc_state) {
489762306a36Sopenharmony_ci	case DSC_DELETED:
489862306a36Sopenharmony_ci	case DSC_LOGIN_PEND:
489962306a36Sopenharmony_ci		qlt_plogi_ack_unref(vha, pla);
490062306a36Sopenharmony_ci		break;
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci	default:
490362306a36Sopenharmony_ci		/*
490462306a36Sopenharmony_ci		 * Under normal circumstances we want to release nport handle
490562306a36Sopenharmony_ci		 * during LOGO process to avoid nport handle leaks inside FW.
490662306a36Sopenharmony_ci		 * The exception is when LOGO is done while another PLOGI with
490762306a36Sopenharmony_ci		 * the same nport handle is waiting as might be the case here.
490862306a36Sopenharmony_ci		 * Note: there is always a possibily of a race where session
490962306a36Sopenharmony_ci		 * deletion has already started for other reasons (e.g. ACL
491062306a36Sopenharmony_ci		 * removal) and now PLOGI arrives:
491162306a36Sopenharmony_ci		 * 1. if PLOGI arrived in FW after nport handle has been freed,
491262306a36Sopenharmony_ci		 *    FW must have assigned this PLOGI a new/same handle and we
491362306a36Sopenharmony_ci		 *    can proceed ACK'ing it as usual when session deletion
491462306a36Sopenharmony_ci		 *    completes.
491562306a36Sopenharmony_ci		 * 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT
491662306a36Sopenharmony_ci		 *    bit reached it, the handle has now been released. We'll
491762306a36Sopenharmony_ci		 *    get an error when we ACK this PLOGI. Nothing will be sent
491862306a36Sopenharmony_ci		 *    back to initiator. Initiator should eventually retry
491962306a36Sopenharmony_ci		 *    PLOGI and situation will correct itself.
492062306a36Sopenharmony_ci		 */
492162306a36Sopenharmony_ci		sess->keep_nport_handle = ((sess->loop_id == loop_id) &&
492262306a36Sopenharmony_ci		    (sess->d_id.b24 == port_id.b24));
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0x20f9,
492562306a36Sopenharmony_ci		    "%s %d %8phC post del sess\n",
492662306a36Sopenharmony_ci		    __func__, __LINE__, sess->port_name);
492762306a36Sopenharmony_ci
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_ci		qlt_schedule_sess_for_deletion(sess);
493062306a36Sopenharmony_ci		break;
493162306a36Sopenharmony_ci	}
493262306a36Sopenharmony_ciout:
493362306a36Sopenharmony_ci	return res;
493462306a36Sopenharmony_ci}
493562306a36Sopenharmony_ci
493662306a36Sopenharmony_ci/*
493762306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
493862306a36Sopenharmony_ci */
493962306a36Sopenharmony_cistatic int qlt_24xx_handle_els(struct scsi_qla_host *vha,
494062306a36Sopenharmony_ci	struct imm_ntfy_from_isp *iocb)
494162306a36Sopenharmony_ci{
494262306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
494362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
494462306a36Sopenharmony_ci	struct fc_port *sess = NULL, *conflict_sess = NULL;
494562306a36Sopenharmony_ci	uint64_t wwn;
494662306a36Sopenharmony_ci	port_id_t port_id;
494762306a36Sopenharmony_ci	uint16_t loop_id;
494862306a36Sopenharmony_ci	uint16_t wd3_lo;
494962306a36Sopenharmony_ci	int res = 0;
495062306a36Sopenharmony_ci	unsigned long flags;
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_ci	lockdep_assert_held(&ha->hardware_lock);
495362306a36Sopenharmony_ci
495462306a36Sopenharmony_ci	wwn = wwn_to_u64(iocb->u.isp24.port_name);
495562306a36Sopenharmony_ci
495662306a36Sopenharmony_ci	port_id.b.domain = iocb->u.isp24.port_id[2];
495762306a36Sopenharmony_ci	port_id.b.area   = iocb->u.isp24.port_id[1];
495862306a36Sopenharmony_ci	port_id.b.al_pa  = iocb->u.isp24.port_id[0];
495962306a36Sopenharmony_ci	port_id.b.rsvd_1 = 0;
496062306a36Sopenharmony_ci
496162306a36Sopenharmony_ci	loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
496262306a36Sopenharmony_ci
496362306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0xf026,
496462306a36Sopenharmony_ci	    "qla_target(%d): Port ID: %02x:%02x:%02x ELS opcode: 0x%02x lid %d %8phC\n",
496562306a36Sopenharmony_ci	    vha->vp_idx, iocb->u.isp24.port_id[2],
496662306a36Sopenharmony_ci		iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[0],
496762306a36Sopenharmony_ci		   iocb->u.isp24.status_subcode, loop_id,
496862306a36Sopenharmony_ci		iocb->u.isp24.port_name);
496962306a36Sopenharmony_ci
497062306a36Sopenharmony_ci	/* res = 1 means ack at the end of thread
497162306a36Sopenharmony_ci	 * res = 0 means ack async/later.
497262306a36Sopenharmony_ci	 */
497362306a36Sopenharmony_ci	switch (iocb->u.isp24.status_subcode) {
497462306a36Sopenharmony_ci	case ELS_PLOGI:
497562306a36Sopenharmony_ci		res = qlt_handle_login(vha, iocb);
497662306a36Sopenharmony_ci		break;
497762306a36Sopenharmony_ci
497862306a36Sopenharmony_ci	case ELS_PRLI:
497962306a36Sopenharmony_ci		if (N2N_TOPO(ha)) {
498062306a36Sopenharmony_ci			sess = qla2x00_find_fcport_by_wwpn(vha,
498162306a36Sopenharmony_ci			    iocb->u.isp24.port_name, 1);
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci			if (vha->hw->flags.edif_enabled && sess &&
498462306a36Sopenharmony_ci			    (!(sess->flags & FCF_FCSP_DEVICE) ||
498562306a36Sopenharmony_ci			     !sess->edif.authok)) {
498662306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0xffff,
498762306a36Sopenharmony_ci				       "%s %d %8phC Term PRLI due to unauthorize PRLI\n",
498862306a36Sopenharmony_ci				       __func__, __LINE__, iocb->u.isp24.port_name);
498962306a36Sopenharmony_ci				qlt_send_term_imm_notif(vha, iocb, 1);
499062306a36Sopenharmony_ci				break;
499162306a36Sopenharmony_ci			}
499262306a36Sopenharmony_ci
499362306a36Sopenharmony_ci			if (sess && sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]) {
499462306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0xffff,
499562306a36Sopenharmony_ci				    "%s %d %8phC Term PRLI due to PLOGI ACK not completed\n",
499662306a36Sopenharmony_ci				    __func__, __LINE__,
499762306a36Sopenharmony_ci				    iocb->u.isp24.port_name);
499862306a36Sopenharmony_ci				qlt_send_term_imm_notif(vha, iocb, 1);
499962306a36Sopenharmony_ci				break;
500062306a36Sopenharmony_ci			}
500162306a36Sopenharmony_ci
500262306a36Sopenharmony_ci			res = qlt_handle_login(vha, iocb);
500362306a36Sopenharmony_ci			break;
500462306a36Sopenharmony_ci		}
500562306a36Sopenharmony_ci
500662306a36Sopenharmony_ci		if (IS_SW_RESV_ADDR(port_id)) {
500762306a36Sopenharmony_ci			res = 1;
500862306a36Sopenharmony_ci			break;
500962306a36Sopenharmony_ci		}
501062306a36Sopenharmony_ci
501162306a36Sopenharmony_ci		wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo);
501262306a36Sopenharmony_ci
501362306a36Sopenharmony_ci		if (wwn) {
501462306a36Sopenharmony_ci			spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
501562306a36Sopenharmony_ci			sess = qlt_find_sess_invalidate_other(vha, wwn, port_id,
501662306a36Sopenharmony_ci				loop_id, &conflict_sess);
501762306a36Sopenharmony_ci			spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
501862306a36Sopenharmony_ci		}
501962306a36Sopenharmony_ci
502062306a36Sopenharmony_ci		if (conflict_sess) {
502162306a36Sopenharmony_ci			switch (conflict_sess->disc_state) {
502262306a36Sopenharmony_ci			case DSC_DELETED:
502362306a36Sopenharmony_ci			case DSC_DELETE_PEND:
502462306a36Sopenharmony_ci				break;
502562306a36Sopenharmony_ci			default:
502662306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt_mgt, vha, 0xf09b,
502762306a36Sopenharmony_ci				    "PRLI with conflicting sess %p port %8phC\n",
502862306a36Sopenharmony_ci				    conflict_sess, conflict_sess->port_name);
502962306a36Sopenharmony_ci				conflict_sess->fw_login_state =
503062306a36Sopenharmony_ci				    DSC_LS_PORT_UNAVAIL;
503162306a36Sopenharmony_ci				qlt_send_term_imm_notif(vha, iocb, 1);
503262306a36Sopenharmony_ci				res = 0;
503362306a36Sopenharmony_ci				break;
503462306a36Sopenharmony_ci			}
503562306a36Sopenharmony_ci		}
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci		if (sess != NULL) {
503862306a36Sopenharmony_ci			bool delete = false;
503962306a36Sopenharmony_ci			int sec;
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ci			if (vha->hw->flags.edif_enabled && sess &&
504262306a36Sopenharmony_ci			    (!(sess->flags & FCF_FCSP_DEVICE) ||
504362306a36Sopenharmony_ci			     !sess->edif.authok)) {
504462306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0xffff,
504562306a36Sopenharmony_ci				       "%s %d %8phC Term PRLI due to unauthorize prli\n",
504662306a36Sopenharmony_ci				       __func__, __LINE__, iocb->u.isp24.port_name);
504762306a36Sopenharmony_ci				qlt_send_term_imm_notif(vha, iocb, 1);
504862306a36Sopenharmony_ci				break;
504962306a36Sopenharmony_ci			}
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_ci			spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
505262306a36Sopenharmony_ci			switch (sess->fw_login_state) {
505362306a36Sopenharmony_ci			case DSC_LS_PLOGI_PEND:
505462306a36Sopenharmony_ci			case DSC_LS_PLOGI_COMP:
505562306a36Sopenharmony_ci			case DSC_LS_PRLI_COMP:
505662306a36Sopenharmony_ci				break;
505762306a36Sopenharmony_ci			default:
505862306a36Sopenharmony_ci				delete = true;
505962306a36Sopenharmony_ci				break;
506062306a36Sopenharmony_ci			}
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_ci			switch (sess->disc_state) {
506362306a36Sopenharmony_ci			case DSC_UPD_FCPORT:
506462306a36Sopenharmony_ci				spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock,
506562306a36Sopenharmony_ci				    flags);
506662306a36Sopenharmony_ci
506762306a36Sopenharmony_ci				sec = jiffies_to_msecs(jiffies -
506862306a36Sopenharmony_ci				    sess->jiffies_at_registration)/1000;
506962306a36Sopenharmony_ci				if (sess->sec_since_registration < sec && sec &&
507062306a36Sopenharmony_ci				    !(sec % 5)) {
507162306a36Sopenharmony_ci					sess->sec_since_registration = sec;
507262306a36Sopenharmony_ci					ql_dbg(ql_dbg_disc, sess->vha, 0xffff,
507362306a36Sopenharmony_ci					    "%s %8phC : Slow Rport registration(%d Sec)\n",
507462306a36Sopenharmony_ci					    __func__, sess->port_name, sec);
507562306a36Sopenharmony_ci				}
507662306a36Sopenharmony_ci				qlt_send_term_imm_notif(vha, iocb, 1);
507762306a36Sopenharmony_ci				return 0;
507862306a36Sopenharmony_ci
507962306a36Sopenharmony_ci			case DSC_LOGIN_PEND:
508062306a36Sopenharmony_ci			case DSC_GPDB:
508162306a36Sopenharmony_ci			case DSC_LOGIN_COMPLETE:
508262306a36Sopenharmony_ci			case DSC_ADISC:
508362306a36Sopenharmony_ci				delete = false;
508462306a36Sopenharmony_ci				break;
508562306a36Sopenharmony_ci			default:
508662306a36Sopenharmony_ci				break;
508762306a36Sopenharmony_ci			}
508862306a36Sopenharmony_ci
508962306a36Sopenharmony_ci			if (delete) {
509062306a36Sopenharmony_ci				spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock,
509162306a36Sopenharmony_ci				    flags);
509262306a36Sopenharmony_ci				/*
509362306a36Sopenharmony_ci				 * Impatient initiator sent PRLI before last
509462306a36Sopenharmony_ci				 * PLOGI could finish. Will force him to re-try,
509562306a36Sopenharmony_ci				 * while last one finishes.
509662306a36Sopenharmony_ci				 */
509762306a36Sopenharmony_ci				ql_log(ql_log_warn, sess->vha, 0xf095,
509862306a36Sopenharmony_ci				    "sess %p PRLI received, before plogi ack.\n",
509962306a36Sopenharmony_ci				    sess);
510062306a36Sopenharmony_ci				qlt_send_term_imm_notif(vha, iocb, 1);
510162306a36Sopenharmony_ci				res = 0;
510262306a36Sopenharmony_ci				break;
510362306a36Sopenharmony_ci			}
510462306a36Sopenharmony_ci
510562306a36Sopenharmony_ci			/*
510662306a36Sopenharmony_ci			 * This shouldn't happen under normal circumstances,
510762306a36Sopenharmony_ci			 * since we have deleted the old session during PLOGI
510862306a36Sopenharmony_ci			 */
510962306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf096,
511062306a36Sopenharmony_ci			    "PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n",
511162306a36Sopenharmony_ci			    sess->loop_id, sess, iocb->u.isp24.nport_handle);
511262306a36Sopenharmony_ci
511362306a36Sopenharmony_ci			sess->local = 0;
511462306a36Sopenharmony_ci			sess->loop_id = loop_id;
511562306a36Sopenharmony_ci			sess->d_id = port_id;
511662306a36Sopenharmony_ci			sess->fw_login_state = DSC_LS_PRLI_PEND;
511762306a36Sopenharmony_ci
511862306a36Sopenharmony_ci			if (wd3_lo & BIT_7)
511962306a36Sopenharmony_ci				sess->conf_compl_supported = 1;
512062306a36Sopenharmony_ci
512162306a36Sopenharmony_ci			if ((wd3_lo & BIT_4) == 0)
512262306a36Sopenharmony_ci				sess->port_type = FCT_INITIATOR;
512362306a36Sopenharmony_ci			else
512462306a36Sopenharmony_ci				sess->port_type = FCT_TARGET;
512562306a36Sopenharmony_ci
512662306a36Sopenharmony_ci			spin_unlock_irqrestore(&tgt->ha->tgt.sess_lock, flags);
512762306a36Sopenharmony_ci		}
512862306a36Sopenharmony_ci		res = 1; /* send notify ack */
512962306a36Sopenharmony_ci
513062306a36Sopenharmony_ci		/* Make session global (not used in fabric mode) */
513162306a36Sopenharmony_ci		if (ha->current_topology != ISP_CFG_F) {
513262306a36Sopenharmony_ci			if (sess) {
513362306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0x20fa,
513462306a36Sopenharmony_ci				    "%s %d %8phC post nack\n",
513562306a36Sopenharmony_ci				    __func__, __LINE__, sess->port_name);
513662306a36Sopenharmony_ci				qla24xx_post_nack_work(vha, sess, iocb,
513762306a36Sopenharmony_ci					SRB_NACK_PRLI);
513862306a36Sopenharmony_ci				res = 0;
513962306a36Sopenharmony_ci			} else {
514062306a36Sopenharmony_ci				set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
514162306a36Sopenharmony_ci				set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
514262306a36Sopenharmony_ci				qla2xxx_wake_dpc(vha);
514362306a36Sopenharmony_ci			}
514462306a36Sopenharmony_ci		} else {
514562306a36Sopenharmony_ci			if (sess) {
514662306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0x20fb,
514762306a36Sopenharmony_ci				    "%s %d %8phC post nack\n",
514862306a36Sopenharmony_ci				    __func__, __LINE__, sess->port_name);
514962306a36Sopenharmony_ci				qla24xx_post_nack_work(vha, sess, iocb,
515062306a36Sopenharmony_ci					SRB_NACK_PRLI);
515162306a36Sopenharmony_ci				res = 0;
515262306a36Sopenharmony_ci			}
515362306a36Sopenharmony_ci		}
515462306a36Sopenharmony_ci		break;
515562306a36Sopenharmony_ci
515662306a36Sopenharmony_ci	case ELS_TPRLO:
515762306a36Sopenharmony_ci		if (le16_to_cpu(iocb->u.isp24.flags) &
515862306a36Sopenharmony_ci			NOTIFY24XX_FLAGS_GLOBAL_TPRLO) {
515962306a36Sopenharmony_ci			loop_id = 0xFFFF;
516062306a36Sopenharmony_ci			qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS);
516162306a36Sopenharmony_ci			res = 1;
516262306a36Sopenharmony_ci			break;
516362306a36Sopenharmony_ci		}
516462306a36Sopenharmony_ci		fallthrough;
516562306a36Sopenharmony_ci	case ELS_LOGO:
516662306a36Sopenharmony_ci	case ELS_PRLO:
516762306a36Sopenharmony_ci		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
516862306a36Sopenharmony_ci		sess = qla2x00_find_fcport_by_loopid(vha, loop_id);
516962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
517062306a36Sopenharmony_ci
517162306a36Sopenharmony_ci		if (sess) {
517262306a36Sopenharmony_ci			sess->login_gen++;
517362306a36Sopenharmony_ci			sess->fw_login_state = DSC_LS_LOGO_PEND;
517462306a36Sopenharmony_ci			sess->logo_ack_needed = 1;
517562306a36Sopenharmony_ci			memcpy(sess->iocb, iocb, IOCB_SIZE);
517662306a36Sopenharmony_ci		}
517762306a36Sopenharmony_ci
517862306a36Sopenharmony_ci		res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
517962306a36Sopenharmony_ci
518062306a36Sopenharmony_ci		ql_dbg(ql_dbg_disc, vha, 0x20fc,
518162306a36Sopenharmony_ci		    "%s: logo %llx res %d sess %p ",
518262306a36Sopenharmony_ci		    __func__, wwn, res, sess);
518362306a36Sopenharmony_ci		if (res == 0) {
518462306a36Sopenharmony_ci			/*
518562306a36Sopenharmony_ci			 * cmd went upper layer, look for qlt_xmit_tm_rsp()
518662306a36Sopenharmony_ci			 * for LOGO_ACK & sess delete
518762306a36Sopenharmony_ci			 */
518862306a36Sopenharmony_ci			BUG_ON(!sess);
518962306a36Sopenharmony_ci			res = 0;
519062306a36Sopenharmony_ci		} else {
519162306a36Sopenharmony_ci			/* cmd did not go to upper layer. */
519262306a36Sopenharmony_ci			if (sess) {
519362306a36Sopenharmony_ci				qlt_schedule_sess_for_deletion(sess);
519462306a36Sopenharmony_ci				res = 0;
519562306a36Sopenharmony_ci			}
519662306a36Sopenharmony_ci			/* else logo will be ack */
519762306a36Sopenharmony_ci		}
519862306a36Sopenharmony_ci		break;
519962306a36Sopenharmony_ci	case ELS_PDISC:
520062306a36Sopenharmony_ci	case ELS_ADISC:
520162306a36Sopenharmony_ci	{
520262306a36Sopenharmony_ci		struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
520362306a36Sopenharmony_ci
520462306a36Sopenharmony_ci		if (tgt->link_reinit_iocb_pending) {
520562306a36Sopenharmony_ci			qlt_send_notify_ack(ha->base_qpair,
520662306a36Sopenharmony_ci			    &tgt->link_reinit_iocb, 0, 0, 0, 0, 0, 0);
520762306a36Sopenharmony_ci			tgt->link_reinit_iocb_pending = 0;
520862306a36Sopenharmony_ci		}
520962306a36Sopenharmony_ci
521062306a36Sopenharmony_ci		sess = qla2x00_find_fcport_by_wwpn(vha,
521162306a36Sopenharmony_ci		    iocb->u.isp24.port_name, 1);
521262306a36Sopenharmony_ci		if (sess) {
521362306a36Sopenharmony_ci			ql_dbg(ql_dbg_disc, vha, 0x20fd,
521462306a36Sopenharmony_ci				"sess %p lid %d|%d DS %d LS %d\n",
521562306a36Sopenharmony_ci				sess, sess->loop_id, loop_id,
521662306a36Sopenharmony_ci				sess->disc_state, sess->fw_login_state);
521762306a36Sopenharmony_ci		}
521862306a36Sopenharmony_ci
521962306a36Sopenharmony_ci		res = 1; /* send notify ack */
522062306a36Sopenharmony_ci		break;
522162306a36Sopenharmony_ci	}
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci	case ELS_FLOGI:	/* should never happen */
522462306a36Sopenharmony_ci	default:
522562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
522662306a36Sopenharmony_ci		    "qla_target(%d): Unsupported ELS command %x "
522762306a36Sopenharmony_ci		    "received\n", vha->vp_idx, iocb->u.isp24.status_subcode);
522862306a36Sopenharmony_ci		res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
522962306a36Sopenharmony_ci		break;
523062306a36Sopenharmony_ci	}
523162306a36Sopenharmony_ci
523262306a36Sopenharmony_ci	ql_dbg(ql_dbg_disc, vha, 0xf026,
523362306a36Sopenharmony_ci	    "qla_target(%d): Exit ELS opcode: 0x%02x res %d\n",
523462306a36Sopenharmony_ci	    vha->vp_idx, iocb->u.isp24.status_subcode, res);
523562306a36Sopenharmony_ci
523662306a36Sopenharmony_ci	return res;
523762306a36Sopenharmony_ci}
523862306a36Sopenharmony_ci
523962306a36Sopenharmony_ci/*
524062306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry.
524162306a36Sopenharmony_ci * Might drop it, then reacquire.
524262306a36Sopenharmony_ci */
524362306a36Sopenharmony_cistatic void qlt_handle_imm_notify(struct scsi_qla_host *vha,
524462306a36Sopenharmony_ci	struct imm_ntfy_from_isp *iocb)
524562306a36Sopenharmony_ci{
524662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
524762306a36Sopenharmony_ci	uint32_t add_flags = 0;
524862306a36Sopenharmony_ci	int send_notify_ack = 1;
524962306a36Sopenharmony_ci	uint16_t status;
525062306a36Sopenharmony_ci
525162306a36Sopenharmony_ci	lockdep_assert_held(&ha->hardware_lock);
525262306a36Sopenharmony_ci
525362306a36Sopenharmony_ci	status = le16_to_cpu(iocb->u.isp2x.status);
525462306a36Sopenharmony_ci	switch (status) {
525562306a36Sopenharmony_ci	case IMM_NTFY_LIP_RESET:
525662306a36Sopenharmony_ci	{
525762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf032,
525862306a36Sopenharmony_ci		    "qla_target(%d): LIP reset (loop %#x), subcode %x\n",
525962306a36Sopenharmony_ci		    vha->vp_idx, le16_to_cpu(iocb->u.isp24.nport_handle),
526062306a36Sopenharmony_ci		    iocb->u.isp24.status_subcode);
526162306a36Sopenharmony_ci
526262306a36Sopenharmony_ci		if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0)
526362306a36Sopenharmony_ci			send_notify_ack = 0;
526462306a36Sopenharmony_ci		break;
526562306a36Sopenharmony_ci	}
526662306a36Sopenharmony_ci
526762306a36Sopenharmony_ci	case IMM_NTFY_LIP_LINK_REINIT:
526862306a36Sopenharmony_ci	{
526962306a36Sopenharmony_ci		struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
527062306a36Sopenharmony_ci
527162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf033,
527262306a36Sopenharmony_ci		    "qla_target(%d): LINK REINIT (loop %#x, "
527362306a36Sopenharmony_ci		    "subcode %x)\n", vha->vp_idx,
527462306a36Sopenharmony_ci		    le16_to_cpu(iocb->u.isp24.nport_handle),
527562306a36Sopenharmony_ci		    iocb->u.isp24.status_subcode);
527662306a36Sopenharmony_ci		if (tgt->link_reinit_iocb_pending) {
527762306a36Sopenharmony_ci			qlt_send_notify_ack(ha->base_qpair,
527862306a36Sopenharmony_ci			    &tgt->link_reinit_iocb, 0, 0, 0, 0, 0, 0);
527962306a36Sopenharmony_ci		}
528062306a36Sopenharmony_ci		memcpy(&tgt->link_reinit_iocb, iocb, sizeof(*iocb));
528162306a36Sopenharmony_ci		tgt->link_reinit_iocb_pending = 1;
528262306a36Sopenharmony_ci		/*
528362306a36Sopenharmony_ci		 * QLogic requires to wait after LINK REINIT for possible
528462306a36Sopenharmony_ci		 * PDISC or ADISC ELS commands
528562306a36Sopenharmony_ci		 */
528662306a36Sopenharmony_ci		send_notify_ack = 0;
528762306a36Sopenharmony_ci		break;
528862306a36Sopenharmony_ci	}
528962306a36Sopenharmony_ci
529062306a36Sopenharmony_ci	case IMM_NTFY_PORT_LOGOUT:
529162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf034,
529262306a36Sopenharmony_ci		    "qla_target(%d): Port logout (loop "
529362306a36Sopenharmony_ci		    "%#x, subcode %x)\n", vha->vp_idx,
529462306a36Sopenharmony_ci		    le16_to_cpu(iocb->u.isp24.nport_handle),
529562306a36Sopenharmony_ci		    iocb->u.isp24.status_subcode);
529662306a36Sopenharmony_ci
529762306a36Sopenharmony_ci		if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS) == 0)
529862306a36Sopenharmony_ci			send_notify_ack = 0;
529962306a36Sopenharmony_ci		/* The sessions will be cleared in the callback, if needed */
530062306a36Sopenharmony_ci		break;
530162306a36Sopenharmony_ci
530262306a36Sopenharmony_ci	case IMM_NTFY_GLBL_TPRLO:
530362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf035,
530462306a36Sopenharmony_ci		    "qla_target(%d): Global TPRLO (%x)\n", vha->vp_idx, status);
530562306a36Sopenharmony_ci		if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0)
530662306a36Sopenharmony_ci			send_notify_ack = 0;
530762306a36Sopenharmony_ci		/* The sessions will be cleared in the callback, if needed */
530862306a36Sopenharmony_ci		break;
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_ci	case IMM_NTFY_PORT_CONFIG:
531162306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf036,
531262306a36Sopenharmony_ci		    "qla_target(%d): Port config changed (%x)\n", vha->vp_idx,
531362306a36Sopenharmony_ci		    status);
531462306a36Sopenharmony_ci		if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0)
531562306a36Sopenharmony_ci			send_notify_ack = 0;
531662306a36Sopenharmony_ci		/* The sessions will be cleared in the callback, if needed */
531762306a36Sopenharmony_ci		break;
531862306a36Sopenharmony_ci
531962306a36Sopenharmony_ci	case IMM_NTFY_GLBL_LOGO:
532062306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06a,
532162306a36Sopenharmony_ci		    "qla_target(%d): Link failure detected\n",
532262306a36Sopenharmony_ci		    vha->vp_idx);
532362306a36Sopenharmony_ci		/* I_T nexus loss */
532462306a36Sopenharmony_ci		if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0)
532562306a36Sopenharmony_ci			send_notify_ack = 0;
532662306a36Sopenharmony_ci		break;
532762306a36Sopenharmony_ci
532862306a36Sopenharmony_ci	case IMM_NTFY_IOCB_OVERFLOW:
532962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06b,
533062306a36Sopenharmony_ci		    "qla_target(%d): Cannot provide requested "
533162306a36Sopenharmony_ci		    "capability (IOCB overflowed the immediate notify "
533262306a36Sopenharmony_ci		    "resource count)\n", vha->vp_idx);
533362306a36Sopenharmony_ci		break;
533462306a36Sopenharmony_ci
533562306a36Sopenharmony_ci	case IMM_NTFY_ABORT_TASK:
533662306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf037,
533762306a36Sopenharmony_ci		    "qla_target(%d): Abort Task (S %08x I %#x -> "
533862306a36Sopenharmony_ci		    "L %#x)\n", vha->vp_idx,
533962306a36Sopenharmony_ci		    le16_to_cpu(iocb->u.isp2x.seq_id),
534062306a36Sopenharmony_ci		    GET_TARGET_ID(ha, (struct atio_from_isp *)iocb),
534162306a36Sopenharmony_ci		    le16_to_cpu(iocb->u.isp2x.lun));
534262306a36Sopenharmony_ci		if (qlt_abort_task(vha, iocb) == 0)
534362306a36Sopenharmony_ci			send_notify_ack = 0;
534462306a36Sopenharmony_ci		break;
534562306a36Sopenharmony_ci
534662306a36Sopenharmony_ci	case IMM_NTFY_RESOURCE:
534762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06c,
534862306a36Sopenharmony_ci		    "qla_target(%d): Out of resources, host %ld\n",
534962306a36Sopenharmony_ci		    vha->vp_idx, vha->host_no);
535062306a36Sopenharmony_ci		break;
535162306a36Sopenharmony_ci
535262306a36Sopenharmony_ci	case IMM_NTFY_MSG_RX:
535362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf038,
535462306a36Sopenharmony_ci		    "qla_target(%d): Immediate notify task %x\n",
535562306a36Sopenharmony_ci		    vha->vp_idx, iocb->u.isp2x.task_flags);
535662306a36Sopenharmony_ci		break;
535762306a36Sopenharmony_ci
535862306a36Sopenharmony_ci	case IMM_NTFY_ELS:
535962306a36Sopenharmony_ci		if (qlt_24xx_handle_els(vha, iocb) == 0)
536062306a36Sopenharmony_ci			send_notify_ack = 0;
536162306a36Sopenharmony_ci		break;
536262306a36Sopenharmony_ci	default:
536362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06d,
536462306a36Sopenharmony_ci		    "qla_target(%d): Received unknown immediate "
536562306a36Sopenharmony_ci		    "notify status %x\n", vha->vp_idx, status);
536662306a36Sopenharmony_ci		break;
536762306a36Sopenharmony_ci	}
536862306a36Sopenharmony_ci
536962306a36Sopenharmony_ci	if (send_notify_ack)
537062306a36Sopenharmony_ci		qlt_send_notify_ack(ha->base_qpair, iocb, add_flags, 0, 0, 0,
537162306a36Sopenharmony_ci		    0, 0);
537262306a36Sopenharmony_ci}
537362306a36Sopenharmony_ci
537462306a36Sopenharmony_ci/*
537562306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
537662306a36Sopenharmony_ci * This function sends busy to ISP 2xxx or 24xx.
537762306a36Sopenharmony_ci */
537862306a36Sopenharmony_cistatic int __qlt_send_busy(struct qla_qpair *qpair,
537962306a36Sopenharmony_ci	struct atio_from_isp *atio, uint16_t status)
538062306a36Sopenharmony_ci{
538162306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
538262306a36Sopenharmony_ci	struct ctio7_to_24xx *ctio24;
538362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
538462306a36Sopenharmony_ci	request_t *pkt;
538562306a36Sopenharmony_ci	struct fc_port *sess = NULL;
538662306a36Sopenharmony_ci	unsigned long flags;
538762306a36Sopenharmony_ci	u16 temp;
538862306a36Sopenharmony_ci	port_id_t id;
538962306a36Sopenharmony_ci
539062306a36Sopenharmony_ci	id = be_to_port_id(atio->u.isp24.fcp_hdr.s_id);
539162306a36Sopenharmony_ci
539262306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
539362306a36Sopenharmony_ci	sess = qla2x00_find_fcport_by_nportid(vha, &id, 1);
539462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
539562306a36Sopenharmony_ci	if (!sess) {
539662306a36Sopenharmony_ci		qlt_send_term_exchange(qpair, NULL, atio, 1, 0);
539762306a36Sopenharmony_ci		return 0;
539862306a36Sopenharmony_ci	}
539962306a36Sopenharmony_ci	/* Sending marker isn't necessary, since we called from ISR */
540062306a36Sopenharmony_ci
540162306a36Sopenharmony_ci	pkt = (request_t *)__qla2x00_alloc_iocbs(qpair, NULL);
540262306a36Sopenharmony_ci	if (!pkt) {
540362306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, vha, 0x3063,
540462306a36Sopenharmony_ci		    "qla_target(%d): %s failed: unable to allocate "
540562306a36Sopenharmony_ci		    "request packet", vha->vp_idx, __func__);
540662306a36Sopenharmony_ci		return -ENOMEM;
540762306a36Sopenharmony_ci	}
540862306a36Sopenharmony_ci
540962306a36Sopenharmony_ci	qpair->tgt_counters.num_q_full_sent++;
541062306a36Sopenharmony_ci	pkt->entry_count = 1;
541162306a36Sopenharmony_ci	pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
541262306a36Sopenharmony_ci
541362306a36Sopenharmony_ci	ctio24 = (struct ctio7_to_24xx *)pkt;
541462306a36Sopenharmony_ci	ctio24->entry_type = CTIO_TYPE7;
541562306a36Sopenharmony_ci	ctio24->nport_handle = cpu_to_le16(sess->loop_id);
541662306a36Sopenharmony_ci	ctio24->timeout = cpu_to_le16(QLA_TGT_TIMEOUT);
541762306a36Sopenharmony_ci	ctio24->vp_index = vha->vp_idx;
541862306a36Sopenharmony_ci	ctio24->initiator_id = be_id_to_le(atio->u.isp24.fcp_hdr.s_id);
541962306a36Sopenharmony_ci	ctio24->exchange_addr = atio->u.isp24.exchange_addr;
542062306a36Sopenharmony_ci	temp = (atio->u.isp24.attr << 9) |
542162306a36Sopenharmony_ci		CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS |
542262306a36Sopenharmony_ci		CTIO7_FLAGS_DONT_RET_CTIO;
542362306a36Sopenharmony_ci	ctio24->u.status1.flags = cpu_to_le16(temp);
542462306a36Sopenharmony_ci	/*
542562306a36Sopenharmony_ci	 * CTIO from fw w/o se_cmd doesn't provide enough info to retry it,
542662306a36Sopenharmony_ci	 * if the explicit conformation is used.
542762306a36Sopenharmony_ci	 */
542862306a36Sopenharmony_ci	ctio24->u.status1.ox_id =
542962306a36Sopenharmony_ci		cpu_to_le16(be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id));
543062306a36Sopenharmony_ci	ctio24->u.status1.scsi_status = cpu_to_le16(status);
543162306a36Sopenharmony_ci
543262306a36Sopenharmony_ci	ctio24->u.status1.residual = cpu_to_le32(get_datalen_for_atio(atio));
543362306a36Sopenharmony_ci
543462306a36Sopenharmony_ci	if (ctio24->u.status1.residual != 0)
543562306a36Sopenharmony_ci		ctio24->u.status1.scsi_status |= cpu_to_le16(SS_RESIDUAL_UNDER);
543662306a36Sopenharmony_ci
543762306a36Sopenharmony_ci	/* Memory Barrier */
543862306a36Sopenharmony_ci	wmb();
543962306a36Sopenharmony_ci	if (qpair->reqq_start_iocbs)
544062306a36Sopenharmony_ci		qpair->reqq_start_iocbs(qpair);
544162306a36Sopenharmony_ci	else
544262306a36Sopenharmony_ci		qla2x00_start_iocbs(vha, qpair->req);
544362306a36Sopenharmony_ci	return 0;
544462306a36Sopenharmony_ci}
544562306a36Sopenharmony_ci
544662306a36Sopenharmony_ci/*
544762306a36Sopenharmony_ci * This routine is used to allocate a command for either a QFull condition
544862306a36Sopenharmony_ci * (ie reply SAM_STAT_BUSY) or to terminate an exchange that did not go
544962306a36Sopenharmony_ci * out previously.
545062306a36Sopenharmony_ci */
545162306a36Sopenharmony_cistatic void
545262306a36Sopenharmony_ciqlt_alloc_qfull_cmd(struct scsi_qla_host *vha,
545362306a36Sopenharmony_ci	struct atio_from_isp *atio, uint16_t status, int qfull)
545462306a36Sopenharmony_ci{
545562306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
545662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
545762306a36Sopenharmony_ci	struct fc_port *sess;
545862306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd;
545962306a36Sopenharmony_ci	unsigned long flags;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci	if (unlikely(tgt->tgt_stop)) {
546262306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, vha, 0x300a,
546362306a36Sopenharmony_ci			"New command while device %p is shutting down\n", tgt);
546462306a36Sopenharmony_ci		return;
546562306a36Sopenharmony_ci	}
546662306a36Sopenharmony_ci
546762306a36Sopenharmony_ci	if ((vha->hw->tgt.num_qfull_cmds_alloc + 1) > MAX_QFULL_CMDS_ALLOC) {
546862306a36Sopenharmony_ci		vha->hw->tgt.num_qfull_cmds_dropped++;
546962306a36Sopenharmony_ci		if (vha->hw->tgt.num_qfull_cmds_dropped >
547062306a36Sopenharmony_ci			vha->qla_stats.stat_max_qfull_cmds_dropped)
547162306a36Sopenharmony_ci			vha->qla_stats.stat_max_qfull_cmds_dropped =
547262306a36Sopenharmony_ci				vha->hw->tgt.num_qfull_cmds_dropped;
547362306a36Sopenharmony_ci
547462306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, vha, 0x3068,
547562306a36Sopenharmony_ci			"qla_target(%d): %s: QFull CMD dropped[%d]\n",
547662306a36Sopenharmony_ci			vha->vp_idx, __func__,
547762306a36Sopenharmony_ci			vha->hw->tgt.num_qfull_cmds_dropped);
547862306a36Sopenharmony_ci
547962306a36Sopenharmony_ci		qlt_chk_exch_leak_thresh_hold(vha);
548062306a36Sopenharmony_ci		return;
548162306a36Sopenharmony_ci	}
548262306a36Sopenharmony_ci
548362306a36Sopenharmony_ci	sess = ha->tgt.tgt_ops->find_sess_by_s_id
548462306a36Sopenharmony_ci		(vha, atio->u.isp24.fcp_hdr.s_id);
548562306a36Sopenharmony_ci	if (!sess)
548662306a36Sopenharmony_ci		return;
548762306a36Sopenharmony_ci
548862306a36Sopenharmony_ci	cmd = ha->tgt.tgt_ops->get_cmd(sess);
548962306a36Sopenharmony_ci	if (!cmd) {
549062306a36Sopenharmony_ci		ql_dbg(ql_dbg_io, vha, 0x3009,
549162306a36Sopenharmony_ci			"qla_target(%d): %s: Allocation of cmd failed\n",
549262306a36Sopenharmony_ci			vha->vp_idx, __func__);
549362306a36Sopenharmony_ci
549462306a36Sopenharmony_ci		vha->hw->tgt.num_qfull_cmds_dropped++;
549562306a36Sopenharmony_ci		if (vha->hw->tgt.num_qfull_cmds_dropped >
549662306a36Sopenharmony_ci			vha->qla_stats.stat_max_qfull_cmds_dropped)
549762306a36Sopenharmony_ci			vha->qla_stats.stat_max_qfull_cmds_dropped =
549862306a36Sopenharmony_ci				vha->hw->tgt.num_qfull_cmds_dropped;
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_ci		qlt_chk_exch_leak_thresh_hold(vha);
550162306a36Sopenharmony_ci		return;
550262306a36Sopenharmony_ci	}
550362306a36Sopenharmony_ci
550462306a36Sopenharmony_ci	qlt_incr_num_pend_cmds(vha);
550562306a36Sopenharmony_ci	INIT_LIST_HEAD(&cmd->cmd_list);
550662306a36Sopenharmony_ci	memcpy(&cmd->atio, atio, sizeof(*atio));
550762306a36Sopenharmony_ci
550862306a36Sopenharmony_ci	cmd->tgt = vha->vha_tgt.qla_tgt;
550962306a36Sopenharmony_ci	cmd->vha = vha;
551062306a36Sopenharmony_ci	cmd->reset_count = ha->base_qpair->chip_reset;
551162306a36Sopenharmony_ci	cmd->q_full = 1;
551262306a36Sopenharmony_ci	cmd->qpair = ha->base_qpair;
551362306a36Sopenharmony_ci
551462306a36Sopenharmony_ci	if (qfull) {
551562306a36Sopenharmony_ci		cmd->q_full = 1;
551662306a36Sopenharmony_ci		/* NOTE: borrowing the state field to carry the status */
551762306a36Sopenharmony_ci		cmd->state = status;
551862306a36Sopenharmony_ci	} else
551962306a36Sopenharmony_ci		cmd->term_exchg = 1;
552062306a36Sopenharmony_ci
552162306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.q_full_lock, flags);
552262306a36Sopenharmony_ci	list_add_tail(&cmd->cmd_list, &vha->hw->tgt.q_full_list);
552362306a36Sopenharmony_ci
552462306a36Sopenharmony_ci	vha->hw->tgt.num_qfull_cmds_alloc++;
552562306a36Sopenharmony_ci	if (vha->hw->tgt.num_qfull_cmds_alloc >
552662306a36Sopenharmony_ci		vha->qla_stats.stat_max_qfull_cmds_alloc)
552762306a36Sopenharmony_ci		vha->qla_stats.stat_max_qfull_cmds_alloc =
552862306a36Sopenharmony_ci			vha->hw->tgt.num_qfull_cmds_alloc;
552962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags);
553062306a36Sopenharmony_ci}
553162306a36Sopenharmony_ci
553262306a36Sopenharmony_ciint
553362306a36Sopenharmony_ciqlt_free_qfull_cmds(struct qla_qpair *qpair)
553462306a36Sopenharmony_ci{
553562306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
553662306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
553762306a36Sopenharmony_ci	unsigned long flags;
553862306a36Sopenharmony_ci	struct qla_tgt_cmd *cmd, *tcmd;
553962306a36Sopenharmony_ci	struct list_head free_list, q_full_list;
554062306a36Sopenharmony_ci	int rc = 0;
554162306a36Sopenharmony_ci
554262306a36Sopenharmony_ci	if (list_empty(&ha->tgt.q_full_list))
554362306a36Sopenharmony_ci		return 0;
554462306a36Sopenharmony_ci
554562306a36Sopenharmony_ci	INIT_LIST_HEAD(&free_list);
554662306a36Sopenharmony_ci	INIT_LIST_HEAD(&q_full_list);
554762306a36Sopenharmony_ci
554862306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.q_full_lock, flags);
554962306a36Sopenharmony_ci	if (list_empty(&ha->tgt.q_full_list)) {
555062306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags);
555162306a36Sopenharmony_ci		return 0;
555262306a36Sopenharmony_ci	}
555362306a36Sopenharmony_ci
555462306a36Sopenharmony_ci	list_splice_init(&vha->hw->tgt.q_full_list, &q_full_list);
555562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags);
555662306a36Sopenharmony_ci
555762306a36Sopenharmony_ci	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
555862306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, tcmd, &q_full_list, cmd_list) {
555962306a36Sopenharmony_ci		if (cmd->q_full)
556062306a36Sopenharmony_ci			/* cmd->state is a borrowed field to hold status */
556162306a36Sopenharmony_ci			rc = __qlt_send_busy(qpair, &cmd->atio, cmd->state);
556262306a36Sopenharmony_ci		else if (cmd->term_exchg)
556362306a36Sopenharmony_ci			rc = __qlt_send_term_exchange(qpair, NULL, &cmd->atio);
556462306a36Sopenharmony_ci
556562306a36Sopenharmony_ci		if (rc == -ENOMEM)
556662306a36Sopenharmony_ci			break;
556762306a36Sopenharmony_ci
556862306a36Sopenharmony_ci		if (cmd->q_full)
556962306a36Sopenharmony_ci			ql_dbg(ql_dbg_io, vha, 0x3006,
557062306a36Sopenharmony_ci			    "%s: busy sent for ox_id[%04x]\n", __func__,
557162306a36Sopenharmony_ci			    be16_to_cpu(cmd->atio.u.isp24.fcp_hdr.ox_id));
557262306a36Sopenharmony_ci		else if (cmd->term_exchg)
557362306a36Sopenharmony_ci			ql_dbg(ql_dbg_io, vha, 0x3007,
557462306a36Sopenharmony_ci			    "%s: Term exchg sent for ox_id[%04x]\n", __func__,
557562306a36Sopenharmony_ci			    be16_to_cpu(cmd->atio.u.isp24.fcp_hdr.ox_id));
557662306a36Sopenharmony_ci		else
557762306a36Sopenharmony_ci			ql_dbg(ql_dbg_io, vha, 0x3008,
557862306a36Sopenharmony_ci			    "%s: Unexpected cmd in QFull list %p\n", __func__,
557962306a36Sopenharmony_ci			    cmd);
558062306a36Sopenharmony_ci
558162306a36Sopenharmony_ci		list_move_tail(&cmd->cmd_list, &free_list);
558262306a36Sopenharmony_ci
558362306a36Sopenharmony_ci		/* piggy back on hardware_lock for protection */
558462306a36Sopenharmony_ci		vha->hw->tgt.num_qfull_cmds_alloc--;
558562306a36Sopenharmony_ci	}
558662306a36Sopenharmony_ci	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
558762306a36Sopenharmony_ci
558862306a36Sopenharmony_ci	cmd = NULL;
558962306a36Sopenharmony_ci
559062306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, tcmd, &free_list, cmd_list) {
559162306a36Sopenharmony_ci		list_del(&cmd->cmd_list);
559262306a36Sopenharmony_ci		/* This cmd was never sent to TCM.  There is no need
559362306a36Sopenharmony_ci		 * to schedule free or call free_cmd
559462306a36Sopenharmony_ci		 */
559562306a36Sopenharmony_ci		qlt_free_cmd(cmd);
559662306a36Sopenharmony_ci	}
559762306a36Sopenharmony_ci
559862306a36Sopenharmony_ci	if (!list_empty(&q_full_list)) {
559962306a36Sopenharmony_ci		spin_lock_irqsave(&vha->hw->tgt.q_full_lock, flags);
560062306a36Sopenharmony_ci		list_splice(&q_full_list, &vha->hw->tgt.q_full_list);
560162306a36Sopenharmony_ci		spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags);
560262306a36Sopenharmony_ci	}
560362306a36Sopenharmony_ci
560462306a36Sopenharmony_ci	return rc;
560562306a36Sopenharmony_ci}
560662306a36Sopenharmony_ci
560762306a36Sopenharmony_cistatic void
560862306a36Sopenharmony_ciqlt_send_busy(struct qla_qpair *qpair, struct atio_from_isp *atio,
560962306a36Sopenharmony_ci    uint16_t status)
561062306a36Sopenharmony_ci{
561162306a36Sopenharmony_ci	int rc = 0;
561262306a36Sopenharmony_ci	struct scsi_qla_host *vha = qpair->vha;
561362306a36Sopenharmony_ci
561462306a36Sopenharmony_ci	rc = __qlt_send_busy(qpair, atio, status);
561562306a36Sopenharmony_ci	if (rc == -ENOMEM)
561662306a36Sopenharmony_ci		qlt_alloc_qfull_cmd(vha, atio, status, 1);
561762306a36Sopenharmony_ci}
561862306a36Sopenharmony_ci
561962306a36Sopenharmony_cistatic int
562062306a36Sopenharmony_ciqlt_chk_qfull_thresh_hold(struct scsi_qla_host *vha, struct qla_qpair *qpair,
562162306a36Sopenharmony_ci	struct atio_from_isp *atio, uint8_t ha_locked)
562262306a36Sopenharmony_ci{
562362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
562462306a36Sopenharmony_ci	unsigned long flags;
562562306a36Sopenharmony_ci
562662306a36Sopenharmony_ci	if (ha->tgt.num_pend_cmds < Q_FULL_THRESH_HOLD(ha))
562762306a36Sopenharmony_ci		return 0;
562862306a36Sopenharmony_ci
562962306a36Sopenharmony_ci	if (!ha_locked)
563062306a36Sopenharmony_ci		spin_lock_irqsave(&ha->hardware_lock, flags);
563162306a36Sopenharmony_ci	qlt_send_busy(qpair, atio, qla_sam_status);
563262306a36Sopenharmony_ci	if (!ha_locked)
563362306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->hardware_lock, flags);
563462306a36Sopenharmony_ci
563562306a36Sopenharmony_ci	return 1;
563662306a36Sopenharmony_ci}
563762306a36Sopenharmony_ci
563862306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
563962306a36Sopenharmony_ci/* called via callback from qla2xxx */
564062306a36Sopenharmony_cistatic void qlt_24xx_atio_pkt(struct scsi_qla_host *vha,
564162306a36Sopenharmony_ci	struct atio_from_isp *atio, uint8_t ha_locked)
564262306a36Sopenharmony_ci{
564362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
564462306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
564562306a36Sopenharmony_ci	int rc;
564662306a36Sopenharmony_ci	unsigned long flags = 0;
564762306a36Sopenharmony_ci
564862306a36Sopenharmony_ci	if (unlikely(tgt == NULL)) {
564962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0x3064,
565062306a36Sopenharmony_ci		    "ATIO pkt, but no tgt (ha %p)", ha);
565162306a36Sopenharmony_ci		return;
565262306a36Sopenharmony_ci	}
565362306a36Sopenharmony_ci	/*
565462306a36Sopenharmony_ci	 * In tgt_stop mode we also should allow all requests to pass.
565562306a36Sopenharmony_ci	 * Otherwise, some commands can stuck.
565662306a36Sopenharmony_ci	 */
565762306a36Sopenharmony_ci
565862306a36Sopenharmony_ci	tgt->atio_irq_cmd_count++;
565962306a36Sopenharmony_ci
566062306a36Sopenharmony_ci	switch (atio->u.raw.entry_type) {
566162306a36Sopenharmony_ci	case ATIO_TYPE7:
566262306a36Sopenharmony_ci		if (unlikely(atio->u.isp24.exchange_addr ==
566362306a36Sopenharmony_ci			     cpu_to_le32(ATIO_EXCHANGE_ADDRESS_UNKNOWN))) {
566462306a36Sopenharmony_ci			ql_dbg(ql_dbg_io, vha, 0x3065,
566562306a36Sopenharmony_ci			    "qla_target(%d): ATIO_TYPE7 "
566662306a36Sopenharmony_ci			    "received with UNKNOWN exchange address, "
566762306a36Sopenharmony_ci			    "sending QUEUE_FULL\n", vha->vp_idx);
566862306a36Sopenharmony_ci			if (!ha_locked)
566962306a36Sopenharmony_ci				spin_lock_irqsave(&ha->hardware_lock, flags);
567062306a36Sopenharmony_ci			qlt_send_busy(ha->base_qpair, atio, qla_sam_status);
567162306a36Sopenharmony_ci			if (!ha_locked)
567262306a36Sopenharmony_ci				spin_unlock_irqrestore(&ha->hardware_lock,
567362306a36Sopenharmony_ci				    flags);
567462306a36Sopenharmony_ci			break;
567562306a36Sopenharmony_ci		}
567662306a36Sopenharmony_ci
567762306a36Sopenharmony_ci		if (likely(atio->u.isp24.fcp_cmnd.task_mgmt_flags == 0)) {
567862306a36Sopenharmony_ci			rc = qlt_chk_qfull_thresh_hold(vha, ha->base_qpair,
567962306a36Sopenharmony_ci			    atio, ha_locked);
568062306a36Sopenharmony_ci			if (rc != 0) {
568162306a36Sopenharmony_ci				tgt->atio_irq_cmd_count--;
568262306a36Sopenharmony_ci				return;
568362306a36Sopenharmony_ci			}
568462306a36Sopenharmony_ci			rc = qlt_handle_cmd_for_atio(vha, atio);
568562306a36Sopenharmony_ci		} else {
568662306a36Sopenharmony_ci			rc = qlt_handle_task_mgmt(vha, atio);
568762306a36Sopenharmony_ci		}
568862306a36Sopenharmony_ci		if (unlikely(rc != 0)) {
568962306a36Sopenharmony_ci			if (!ha_locked)
569062306a36Sopenharmony_ci				spin_lock_irqsave(&ha->hardware_lock, flags);
569162306a36Sopenharmony_ci			switch (rc) {
569262306a36Sopenharmony_ci			case -ENODEV:
569362306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe05f,
569462306a36Sopenharmony_ci				    "qla_target: Unable to send command to target\n");
569562306a36Sopenharmony_ci				break;
569662306a36Sopenharmony_ci			case -EBADF:
569762306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe05f,
569862306a36Sopenharmony_ci				    "qla_target: Unable to send command to target, sending TERM EXCHANGE for rsp\n");
569962306a36Sopenharmony_ci				qlt_send_term_exchange(ha->base_qpair, NULL,
570062306a36Sopenharmony_ci				    atio, 1, 0);
570162306a36Sopenharmony_ci				break;
570262306a36Sopenharmony_ci			case -EBUSY:
570362306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe060,
570462306a36Sopenharmony_ci				    "qla_target(%d): Unable to send command to target, sending BUSY status\n",
570562306a36Sopenharmony_ci				    vha->vp_idx);
570662306a36Sopenharmony_ci				qlt_send_busy(ha->base_qpair, atio,
570762306a36Sopenharmony_ci				    tc_sam_status);
570862306a36Sopenharmony_ci				break;
570962306a36Sopenharmony_ci			default:
571062306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe060,
571162306a36Sopenharmony_ci				    "qla_target(%d): Unable to send command to target, sending BUSY status\n",
571262306a36Sopenharmony_ci				    vha->vp_idx);
571362306a36Sopenharmony_ci				qlt_send_busy(ha->base_qpair, atio,
571462306a36Sopenharmony_ci				    qla_sam_status);
571562306a36Sopenharmony_ci				break;
571662306a36Sopenharmony_ci			}
571762306a36Sopenharmony_ci			if (!ha_locked)
571862306a36Sopenharmony_ci				spin_unlock_irqrestore(&ha->hardware_lock,
571962306a36Sopenharmony_ci				    flags);
572062306a36Sopenharmony_ci		}
572162306a36Sopenharmony_ci		break;
572262306a36Sopenharmony_ci
572362306a36Sopenharmony_ci	case IMMED_NOTIFY_TYPE:
572462306a36Sopenharmony_ci	{
572562306a36Sopenharmony_ci		if (unlikely(atio->u.isp2x.entry_status != 0)) {
572662306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe05b,
572762306a36Sopenharmony_ci			    "qla_target(%d): Received ATIO packet %x "
572862306a36Sopenharmony_ci			    "with error status %x\n", vha->vp_idx,
572962306a36Sopenharmony_ci			    atio->u.raw.entry_type,
573062306a36Sopenharmony_ci			    atio->u.isp2x.entry_status);
573162306a36Sopenharmony_ci			break;
573262306a36Sopenharmony_ci		}
573362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe02e, "%s", "IMMED_NOTIFY ATIO");
573462306a36Sopenharmony_ci
573562306a36Sopenharmony_ci		if (!ha_locked)
573662306a36Sopenharmony_ci			spin_lock_irqsave(&ha->hardware_lock, flags);
573762306a36Sopenharmony_ci		qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)atio);
573862306a36Sopenharmony_ci		if (!ha_locked)
573962306a36Sopenharmony_ci			spin_unlock_irqrestore(&ha->hardware_lock, flags);
574062306a36Sopenharmony_ci		break;
574162306a36Sopenharmony_ci	}
574262306a36Sopenharmony_ci
574362306a36Sopenharmony_ci	default:
574462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe05c,
574562306a36Sopenharmony_ci		    "qla_target(%d): Received unknown ATIO atio "
574662306a36Sopenharmony_ci		    "type %x\n", vha->vp_idx, atio->u.raw.entry_type);
574762306a36Sopenharmony_ci		break;
574862306a36Sopenharmony_ci	}
574962306a36Sopenharmony_ci
575062306a36Sopenharmony_ci	tgt->atio_irq_cmd_count--;
575162306a36Sopenharmony_ci}
575262306a36Sopenharmony_ci
575362306a36Sopenharmony_ci/*
575462306a36Sopenharmony_ci * qpair lock is assume to be held
575562306a36Sopenharmony_ci * rc = 0 : send terminate & abts respond
575662306a36Sopenharmony_ci * rc != 0: do not send term & abts respond
575762306a36Sopenharmony_ci */
575862306a36Sopenharmony_cistatic int qlt_chk_unresolv_exchg(struct scsi_qla_host *vha,
575962306a36Sopenharmony_ci    struct qla_qpair *qpair, struct abts_resp_from_24xx_fw *entry)
576062306a36Sopenharmony_ci{
576162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
576262306a36Sopenharmony_ci	int rc = 0;
576362306a36Sopenharmony_ci
576462306a36Sopenharmony_ci	/*
576562306a36Sopenharmony_ci	 * Detect unresolved exchange. If the same ABTS is unable
576662306a36Sopenharmony_ci	 * to terminate an existing command and the same ABTS loops
576762306a36Sopenharmony_ci	 * between FW & Driver, then force FW dump. Under 1 jiff,
576862306a36Sopenharmony_ci	 * we should see multiple loops.
576962306a36Sopenharmony_ci	 */
577062306a36Sopenharmony_ci	if (qpair->retry_term_exchg_addr == entry->exchange_addr_to_abort &&
577162306a36Sopenharmony_ci	    qpair->retry_term_jiff == jiffies) {
577262306a36Sopenharmony_ci		/* found existing exchange */
577362306a36Sopenharmony_ci		qpair->retry_term_cnt++;
577462306a36Sopenharmony_ci		if (qpair->retry_term_cnt >= 5) {
577562306a36Sopenharmony_ci			rc = -EIO;
577662306a36Sopenharmony_ci			qpair->retry_term_cnt = 0;
577762306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0xffff,
577862306a36Sopenharmony_ci			    "Unable to send ABTS Respond. Dumping firmware.\n");
577962306a36Sopenharmony_ci			ql_dump_buffer(ql_dbg_tgt_mgt + ql_dbg_buffer,
578062306a36Sopenharmony_ci			    vha, 0xffff, (uint8_t *)entry, sizeof(*entry));
578162306a36Sopenharmony_ci
578262306a36Sopenharmony_ci			if (qpair == ha->base_qpair)
578362306a36Sopenharmony_ci				ha->isp_ops->fw_dump(vha);
578462306a36Sopenharmony_ci			else
578562306a36Sopenharmony_ci				qla2xxx_dump_fw(vha);
578662306a36Sopenharmony_ci
578762306a36Sopenharmony_ci			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
578862306a36Sopenharmony_ci			qla2xxx_wake_dpc(vha);
578962306a36Sopenharmony_ci		}
579062306a36Sopenharmony_ci	} else if (qpair->retry_term_jiff != jiffies) {
579162306a36Sopenharmony_ci		qpair->retry_term_exchg_addr = entry->exchange_addr_to_abort;
579262306a36Sopenharmony_ci		qpair->retry_term_cnt = 0;
579362306a36Sopenharmony_ci		qpair->retry_term_jiff = jiffies;
579462306a36Sopenharmony_ci	}
579562306a36Sopenharmony_ci
579662306a36Sopenharmony_ci	return rc;
579762306a36Sopenharmony_ci}
579862306a36Sopenharmony_ci
579962306a36Sopenharmony_ci
580062306a36Sopenharmony_cistatic void qlt_handle_abts_completion(struct scsi_qla_host *vha,
580162306a36Sopenharmony_ci	struct rsp_que *rsp, response_t *pkt)
580262306a36Sopenharmony_ci{
580362306a36Sopenharmony_ci	struct abts_resp_from_24xx_fw *entry =
580462306a36Sopenharmony_ci		(struct abts_resp_from_24xx_fw *)pkt;
580562306a36Sopenharmony_ci	u32 h = pkt->handle & ~QLA_TGT_HANDLE_MASK;
580662306a36Sopenharmony_ci	struct qla_tgt_mgmt_cmd *mcmd;
580762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
580862306a36Sopenharmony_ci
580962306a36Sopenharmony_ci	mcmd = qlt_ctio_to_cmd(vha, rsp, pkt->handle, pkt);
581062306a36Sopenharmony_ci	if (mcmd == NULL && h != QLA_TGT_SKIP_HANDLE) {
581162306a36Sopenharmony_ci		ql_dbg(ql_dbg_async, vha, 0xe064,
581262306a36Sopenharmony_ci		    "qla_target(%d): ABTS Comp without mcmd\n",
581362306a36Sopenharmony_ci		    vha->vp_idx);
581462306a36Sopenharmony_ci		return;
581562306a36Sopenharmony_ci	}
581662306a36Sopenharmony_ci
581762306a36Sopenharmony_ci	if (mcmd)
581862306a36Sopenharmony_ci		vha  = mcmd->vha;
581962306a36Sopenharmony_ci	vha->vha_tgt.qla_tgt->abts_resp_expected--;
582062306a36Sopenharmony_ci
582162306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe038,
582262306a36Sopenharmony_ci	    "ABTS_RESP_24XX: compl_status %x\n",
582362306a36Sopenharmony_ci	    entry->compl_status);
582462306a36Sopenharmony_ci
582562306a36Sopenharmony_ci	if (le16_to_cpu(entry->compl_status) != ABTS_RESP_COMPL_SUCCESS) {
582662306a36Sopenharmony_ci		if (le32_to_cpu(entry->error_subcode1) == 0x1E &&
582762306a36Sopenharmony_ci		    le32_to_cpu(entry->error_subcode2) == 0) {
582862306a36Sopenharmony_ci			if (qlt_chk_unresolv_exchg(vha, rsp->qpair, entry)) {
582962306a36Sopenharmony_ci				ha->tgt.tgt_ops->free_mcmd(mcmd);
583062306a36Sopenharmony_ci				return;
583162306a36Sopenharmony_ci			}
583262306a36Sopenharmony_ci			qlt_24xx_retry_term_exchange(vha, rsp->qpair,
583362306a36Sopenharmony_ci			    pkt, mcmd);
583462306a36Sopenharmony_ci		} else {
583562306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe063,
583662306a36Sopenharmony_ci			    "qla_target(%d): ABTS_RESP_24XX failed %x (subcode %x:%x)",
583762306a36Sopenharmony_ci			    vha->vp_idx, entry->compl_status,
583862306a36Sopenharmony_ci			    entry->error_subcode1,
583962306a36Sopenharmony_ci			    entry->error_subcode2);
584062306a36Sopenharmony_ci			ha->tgt.tgt_ops->free_mcmd(mcmd);
584162306a36Sopenharmony_ci		}
584262306a36Sopenharmony_ci	} else if (mcmd) {
584362306a36Sopenharmony_ci		ha->tgt.tgt_ops->free_mcmd(mcmd);
584462306a36Sopenharmony_ci	}
584562306a36Sopenharmony_ci}
584662306a36Sopenharmony_ci
584762306a36Sopenharmony_ci/* ha->hardware_lock supposed to be held on entry */
584862306a36Sopenharmony_ci/* called via callback from qla2xxx */
584962306a36Sopenharmony_cistatic void qlt_response_pkt(struct scsi_qla_host *vha,
585062306a36Sopenharmony_ci	struct rsp_que *rsp, response_t *pkt)
585162306a36Sopenharmony_ci{
585262306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
585362306a36Sopenharmony_ci
585462306a36Sopenharmony_ci	if (unlikely(tgt == NULL)) {
585562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe05d,
585662306a36Sopenharmony_ci		    "qla_target(%d): Response pkt %x received, but no tgt (ha %p)\n",
585762306a36Sopenharmony_ci		    vha->vp_idx, pkt->entry_type, vha->hw);
585862306a36Sopenharmony_ci		return;
585962306a36Sopenharmony_ci	}
586062306a36Sopenharmony_ci
586162306a36Sopenharmony_ci	/*
586262306a36Sopenharmony_ci	 * In tgt_stop mode we also should allow all requests to pass.
586362306a36Sopenharmony_ci	 * Otherwise, some commands can stuck.
586462306a36Sopenharmony_ci	 */
586562306a36Sopenharmony_ci
586662306a36Sopenharmony_ci	switch (pkt->entry_type) {
586762306a36Sopenharmony_ci	case CTIO_CRC2:
586862306a36Sopenharmony_ci	case CTIO_TYPE7:
586962306a36Sopenharmony_ci	{
587062306a36Sopenharmony_ci		struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt;
587162306a36Sopenharmony_ci
587262306a36Sopenharmony_ci		qlt_do_ctio_completion(vha, rsp, entry->handle,
587362306a36Sopenharmony_ci		    le16_to_cpu(entry->status)|(pkt->entry_status << 16),
587462306a36Sopenharmony_ci		    entry);
587562306a36Sopenharmony_ci		break;
587662306a36Sopenharmony_ci	}
587762306a36Sopenharmony_ci
587862306a36Sopenharmony_ci	case ACCEPT_TGT_IO_TYPE:
587962306a36Sopenharmony_ci	{
588062306a36Sopenharmony_ci		struct atio_from_isp *atio = (struct atio_from_isp *)pkt;
588162306a36Sopenharmony_ci		int rc;
588262306a36Sopenharmony_ci
588362306a36Sopenharmony_ci		if (atio->u.isp2x.status !=
588462306a36Sopenharmony_ci		    cpu_to_le16(ATIO_CDB_VALID)) {
588562306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe05e,
588662306a36Sopenharmony_ci			    "qla_target(%d): ATIO with error "
588762306a36Sopenharmony_ci			    "status %x received\n", vha->vp_idx,
588862306a36Sopenharmony_ci			    le16_to_cpu(atio->u.isp2x.status));
588962306a36Sopenharmony_ci			break;
589062306a36Sopenharmony_ci		}
589162306a36Sopenharmony_ci
589262306a36Sopenharmony_ci		rc = qlt_chk_qfull_thresh_hold(vha, rsp->qpair, atio, 1);
589362306a36Sopenharmony_ci		if (rc != 0)
589462306a36Sopenharmony_ci			return;
589562306a36Sopenharmony_ci
589662306a36Sopenharmony_ci		rc = qlt_handle_cmd_for_atio(vha, atio);
589762306a36Sopenharmony_ci		if (unlikely(rc != 0)) {
589862306a36Sopenharmony_ci			switch (rc) {
589962306a36Sopenharmony_ci			case -ENODEV:
590062306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe05f,
590162306a36Sopenharmony_ci				    "qla_target: Unable to send command to target\n");
590262306a36Sopenharmony_ci				break;
590362306a36Sopenharmony_ci			case -EBADF:
590462306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe05f,
590562306a36Sopenharmony_ci				    "qla_target: Unable to send command to target, sending TERM EXCHANGE for rsp\n");
590662306a36Sopenharmony_ci				qlt_send_term_exchange(rsp->qpair, NULL,
590762306a36Sopenharmony_ci				    atio, 1, 0);
590862306a36Sopenharmony_ci				break;
590962306a36Sopenharmony_ci			case -EBUSY:
591062306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe060,
591162306a36Sopenharmony_ci				    "qla_target(%d): Unable to send command to target, sending BUSY status\n",
591262306a36Sopenharmony_ci				    vha->vp_idx);
591362306a36Sopenharmony_ci				qlt_send_busy(rsp->qpair, atio,
591462306a36Sopenharmony_ci				    tc_sam_status);
591562306a36Sopenharmony_ci				break;
591662306a36Sopenharmony_ci			default:
591762306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe060,
591862306a36Sopenharmony_ci				    "qla_target(%d): Unable to send command to target, sending BUSY status\n",
591962306a36Sopenharmony_ci				    vha->vp_idx);
592062306a36Sopenharmony_ci				qlt_send_busy(rsp->qpair, atio,
592162306a36Sopenharmony_ci				    qla_sam_status);
592262306a36Sopenharmony_ci				break;
592362306a36Sopenharmony_ci			}
592462306a36Sopenharmony_ci		}
592562306a36Sopenharmony_ci	}
592662306a36Sopenharmony_ci	break;
592762306a36Sopenharmony_ci
592862306a36Sopenharmony_ci	case CONTINUE_TGT_IO_TYPE:
592962306a36Sopenharmony_ci	{
593062306a36Sopenharmony_ci		struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt;
593162306a36Sopenharmony_ci
593262306a36Sopenharmony_ci		qlt_do_ctio_completion(vha, rsp, entry->handle,
593362306a36Sopenharmony_ci		    le16_to_cpu(entry->status)|(pkt->entry_status << 16),
593462306a36Sopenharmony_ci		    entry);
593562306a36Sopenharmony_ci		break;
593662306a36Sopenharmony_ci	}
593762306a36Sopenharmony_ci
593862306a36Sopenharmony_ci	case CTIO_A64_TYPE:
593962306a36Sopenharmony_ci	{
594062306a36Sopenharmony_ci		struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt;
594162306a36Sopenharmony_ci
594262306a36Sopenharmony_ci		qlt_do_ctio_completion(vha, rsp, entry->handle,
594362306a36Sopenharmony_ci		    le16_to_cpu(entry->status)|(pkt->entry_status << 16),
594462306a36Sopenharmony_ci		    entry);
594562306a36Sopenharmony_ci		break;
594662306a36Sopenharmony_ci	}
594762306a36Sopenharmony_ci
594862306a36Sopenharmony_ci	case IMMED_NOTIFY_TYPE:
594962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe035, "%s", "IMMED_NOTIFY\n");
595062306a36Sopenharmony_ci		qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)pkt);
595162306a36Sopenharmony_ci		break;
595262306a36Sopenharmony_ci
595362306a36Sopenharmony_ci	case NOTIFY_ACK_TYPE:
595462306a36Sopenharmony_ci		if (tgt->notify_ack_expected > 0) {
595562306a36Sopenharmony_ci			struct nack_to_isp *entry = (struct nack_to_isp *)pkt;
595662306a36Sopenharmony_ci
595762306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe036,
595862306a36Sopenharmony_ci			    "NOTIFY_ACK seq %08x status %x\n",
595962306a36Sopenharmony_ci			    le16_to_cpu(entry->u.isp2x.seq_id),
596062306a36Sopenharmony_ci			    le16_to_cpu(entry->u.isp2x.status));
596162306a36Sopenharmony_ci			tgt->notify_ack_expected--;
596262306a36Sopenharmony_ci			if (entry->u.isp2x.status !=
596362306a36Sopenharmony_ci			    cpu_to_le16(NOTIFY_ACK_SUCCESS)) {
596462306a36Sopenharmony_ci				ql_dbg(ql_dbg_tgt, vha, 0xe061,
596562306a36Sopenharmony_ci				    "qla_target(%d): NOTIFY_ACK "
596662306a36Sopenharmony_ci				    "failed %x\n", vha->vp_idx,
596762306a36Sopenharmony_ci				    le16_to_cpu(entry->u.isp2x.status));
596862306a36Sopenharmony_ci			}
596962306a36Sopenharmony_ci		} else {
597062306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe062,
597162306a36Sopenharmony_ci			    "qla_target(%d): Unexpected NOTIFY_ACK received\n",
597262306a36Sopenharmony_ci			    vha->vp_idx);
597362306a36Sopenharmony_ci		}
597462306a36Sopenharmony_ci		break;
597562306a36Sopenharmony_ci
597662306a36Sopenharmony_ci	case ABTS_RECV_24XX:
597762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe037,
597862306a36Sopenharmony_ci		    "ABTS_RECV_24XX: instance %d\n", vha->vp_idx);
597962306a36Sopenharmony_ci		qlt_24xx_handle_abts(vha, (struct abts_recv_from_24xx *)pkt);
598062306a36Sopenharmony_ci		break;
598162306a36Sopenharmony_ci
598262306a36Sopenharmony_ci	case ABTS_RESP_24XX:
598362306a36Sopenharmony_ci		if (tgt->abts_resp_expected > 0) {
598462306a36Sopenharmony_ci			qlt_handle_abts_completion(vha, rsp, pkt);
598562306a36Sopenharmony_ci		} else {
598662306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe064,
598762306a36Sopenharmony_ci			    "qla_target(%d): Unexpected ABTS_RESP_24XX "
598862306a36Sopenharmony_ci			    "received\n", vha->vp_idx);
598962306a36Sopenharmony_ci		}
599062306a36Sopenharmony_ci		break;
599162306a36Sopenharmony_ci
599262306a36Sopenharmony_ci	default:
599362306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe065,
599462306a36Sopenharmony_ci		    "qla_target(%d): Received unknown response pkt "
599562306a36Sopenharmony_ci		    "type %x\n", vha->vp_idx, pkt->entry_type);
599662306a36Sopenharmony_ci		break;
599762306a36Sopenharmony_ci	}
599862306a36Sopenharmony_ci
599962306a36Sopenharmony_ci}
600062306a36Sopenharmony_ci
600162306a36Sopenharmony_ci/*
600262306a36Sopenharmony_ci * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
600362306a36Sopenharmony_ci */
600462306a36Sopenharmony_civoid qlt_async_event(uint16_t code, struct scsi_qla_host *vha,
600562306a36Sopenharmony_ci	uint16_t *mailbox)
600662306a36Sopenharmony_ci{
600762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
600862306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
600962306a36Sopenharmony_ci	int login_code;
601062306a36Sopenharmony_ci
601162306a36Sopenharmony_ci	if (!tgt || tgt->tgt_stop || tgt->tgt_stopped)
601262306a36Sopenharmony_ci		return;
601362306a36Sopenharmony_ci
601462306a36Sopenharmony_ci	if (((code == MBA_POINT_TO_POINT) || (code == MBA_CHG_IN_CONNECTION)) &&
601562306a36Sopenharmony_ci	    IS_QLA2100(ha))
601662306a36Sopenharmony_ci		return;
601762306a36Sopenharmony_ci	/*
601862306a36Sopenharmony_ci	 * In tgt_stop mode we also should allow all requests to pass.
601962306a36Sopenharmony_ci	 * Otherwise, some commands can stuck.
602062306a36Sopenharmony_ci	 */
602162306a36Sopenharmony_ci
602262306a36Sopenharmony_ci
602362306a36Sopenharmony_ci	switch (code) {
602462306a36Sopenharmony_ci	case MBA_RESET:			/* Reset */
602562306a36Sopenharmony_ci	case MBA_SYSTEM_ERR:		/* System Error */
602662306a36Sopenharmony_ci	case MBA_REQ_TRANSFER_ERR:	/* Request Transfer Error */
602762306a36Sopenharmony_ci	case MBA_RSP_TRANSFER_ERR:	/* Response Transfer Error */
602862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03a,
602962306a36Sopenharmony_ci		    "qla_target(%d): System error async event %#x "
603062306a36Sopenharmony_ci		    "occurred", vha->vp_idx, code);
603162306a36Sopenharmony_ci		break;
603262306a36Sopenharmony_ci	case MBA_WAKEUP_THRES:		/* Request Queue Wake-up. */
603362306a36Sopenharmony_ci		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
603462306a36Sopenharmony_ci		break;
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_ci	case MBA_LOOP_UP:
603762306a36Sopenharmony_ci	{
603862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03b,
603962306a36Sopenharmony_ci		    "qla_target(%d): Async LOOP_UP occurred "
604062306a36Sopenharmony_ci		    "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx,
604162306a36Sopenharmony_ci		    mailbox[0], mailbox[1], mailbox[2], mailbox[3]);
604262306a36Sopenharmony_ci		if (tgt->link_reinit_iocb_pending) {
604362306a36Sopenharmony_ci			qlt_send_notify_ack(ha->base_qpair,
604462306a36Sopenharmony_ci			    &tgt->link_reinit_iocb,
604562306a36Sopenharmony_ci			    0, 0, 0, 0, 0, 0);
604662306a36Sopenharmony_ci			tgt->link_reinit_iocb_pending = 0;
604762306a36Sopenharmony_ci		}
604862306a36Sopenharmony_ci		break;
604962306a36Sopenharmony_ci	}
605062306a36Sopenharmony_ci
605162306a36Sopenharmony_ci	case MBA_LIP_OCCURRED:
605262306a36Sopenharmony_ci	case MBA_LOOP_DOWN:
605362306a36Sopenharmony_ci	case MBA_LIP_RESET:
605462306a36Sopenharmony_ci	case MBA_RSCN_UPDATE:
605562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03c,
605662306a36Sopenharmony_ci		    "qla_target(%d): Async event %#x occurred "
605762306a36Sopenharmony_ci		    "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, code,
605862306a36Sopenharmony_ci		    mailbox[0], mailbox[1], mailbox[2], mailbox[3]);
605962306a36Sopenharmony_ci		break;
606062306a36Sopenharmony_ci
606162306a36Sopenharmony_ci	case MBA_REJECTED_FCP_CMD:
606262306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf017,
606362306a36Sopenharmony_ci		    "qla_target(%d): Async event LS_REJECT occurred (m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)",
606462306a36Sopenharmony_ci		    vha->vp_idx,
606562306a36Sopenharmony_ci		    mailbox[0], mailbox[1], mailbox[2], mailbox[3]);
606662306a36Sopenharmony_ci
606762306a36Sopenharmony_ci		if (mailbox[3] == 1) {
606862306a36Sopenharmony_ci			/* exchange starvation. */
606962306a36Sopenharmony_ci			vha->hw->exch_starvation++;
607062306a36Sopenharmony_ci			if (vha->hw->exch_starvation > 5) {
607162306a36Sopenharmony_ci				ql_log(ql_log_warn, vha, 0xd03a,
607262306a36Sopenharmony_ci				    "Exchange starvation-. Resetting RISC\n");
607362306a36Sopenharmony_ci
607462306a36Sopenharmony_ci				vha->hw->exch_starvation = 0;
607562306a36Sopenharmony_ci				if (IS_P3P_TYPE(vha->hw))
607662306a36Sopenharmony_ci					set_bit(FCOE_CTX_RESET_NEEDED,
607762306a36Sopenharmony_ci					    &vha->dpc_flags);
607862306a36Sopenharmony_ci				else
607962306a36Sopenharmony_ci					set_bit(ISP_ABORT_NEEDED,
608062306a36Sopenharmony_ci					    &vha->dpc_flags);
608162306a36Sopenharmony_ci				qla2xxx_wake_dpc(vha);
608262306a36Sopenharmony_ci			}
608362306a36Sopenharmony_ci		}
608462306a36Sopenharmony_ci		break;
608562306a36Sopenharmony_ci
608662306a36Sopenharmony_ci	case MBA_PORT_UPDATE:
608762306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d,
608862306a36Sopenharmony_ci		    "qla_target(%d): Port update async event %#x "
608962306a36Sopenharmony_ci		    "occurred: updating the ports database (m[0]=%x, m[1]=%x, "
609062306a36Sopenharmony_ci		    "m[2]=%x, m[3]=%x)", vha->vp_idx, code,
609162306a36Sopenharmony_ci		    mailbox[0], mailbox[1], mailbox[2], mailbox[3]);
609262306a36Sopenharmony_ci
609362306a36Sopenharmony_ci		login_code = mailbox[2];
609462306a36Sopenharmony_ci		if (login_code == 0x4) {
609562306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03e,
609662306a36Sopenharmony_ci			    "Async MB 2: Got PLOGI Complete\n");
609762306a36Sopenharmony_ci			vha->hw->exch_starvation = 0;
609862306a36Sopenharmony_ci		} else if (login_code == 0x7)
609962306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f,
610062306a36Sopenharmony_ci			    "Async MB 2: Port Logged Out\n");
610162306a36Sopenharmony_ci		break;
610262306a36Sopenharmony_ci	default:
610362306a36Sopenharmony_ci		break;
610462306a36Sopenharmony_ci	}
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci}
610762306a36Sopenharmony_ci
610862306a36Sopenharmony_cistatic fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha,
610962306a36Sopenharmony_ci	uint16_t loop_id)
611062306a36Sopenharmony_ci{
611162306a36Sopenharmony_ci	fc_port_t *fcport, *tfcp, *del;
611262306a36Sopenharmony_ci	int rc;
611362306a36Sopenharmony_ci	unsigned long flags;
611462306a36Sopenharmony_ci	u8 newfcport = 0;
611562306a36Sopenharmony_ci
611662306a36Sopenharmony_ci	fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
611762306a36Sopenharmony_ci	if (!fcport) {
611862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f,
611962306a36Sopenharmony_ci		    "qla_target(%d): Allocation of tmp FC port failed",
612062306a36Sopenharmony_ci		    vha->vp_idx);
612162306a36Sopenharmony_ci		return NULL;
612262306a36Sopenharmony_ci	}
612362306a36Sopenharmony_ci
612462306a36Sopenharmony_ci	fcport->loop_id = loop_id;
612562306a36Sopenharmony_ci
612662306a36Sopenharmony_ci	rc = qla24xx_gpdb_wait(vha, fcport, 0);
612762306a36Sopenharmony_ci	if (rc != QLA_SUCCESS) {
612862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf070,
612962306a36Sopenharmony_ci		    "qla_target(%d): Failed to retrieve fcport "
613062306a36Sopenharmony_ci		    "information -- get_port_database() returned %x "
613162306a36Sopenharmony_ci		    "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id);
613262306a36Sopenharmony_ci		kfree(fcport);
613362306a36Sopenharmony_ci		return NULL;
613462306a36Sopenharmony_ci	}
613562306a36Sopenharmony_ci
613662306a36Sopenharmony_ci	del = NULL;
613762306a36Sopenharmony_ci	spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
613862306a36Sopenharmony_ci	tfcp = qla2x00_find_fcport_by_wwpn(vha, fcport->port_name, 1);
613962306a36Sopenharmony_ci
614062306a36Sopenharmony_ci	if (tfcp) {
614162306a36Sopenharmony_ci		tfcp->d_id = fcport->d_id;
614262306a36Sopenharmony_ci		tfcp->port_type = fcport->port_type;
614362306a36Sopenharmony_ci		tfcp->supported_classes = fcport->supported_classes;
614462306a36Sopenharmony_ci		tfcp->flags |= fcport->flags;
614562306a36Sopenharmony_ci		tfcp->scan_state = QLA_FCPORT_FOUND;
614662306a36Sopenharmony_ci
614762306a36Sopenharmony_ci		del = fcport;
614862306a36Sopenharmony_ci		fcport = tfcp;
614962306a36Sopenharmony_ci	} else {
615062306a36Sopenharmony_ci		if (vha->hw->current_topology == ISP_CFG_F)
615162306a36Sopenharmony_ci			fcport->flags |= FCF_FABRIC_DEVICE;
615262306a36Sopenharmony_ci
615362306a36Sopenharmony_ci		list_add_tail(&fcport->list, &vha->vp_fcports);
615462306a36Sopenharmony_ci		if (!IS_SW_RESV_ADDR(fcport->d_id))
615562306a36Sopenharmony_ci		   vha->fcport_count++;
615662306a36Sopenharmony_ci		fcport->login_gen++;
615762306a36Sopenharmony_ci		qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_COMPLETE);
615862306a36Sopenharmony_ci		fcport->login_succ = 1;
615962306a36Sopenharmony_ci		newfcport = 1;
616062306a36Sopenharmony_ci	}
616162306a36Sopenharmony_ci
616262306a36Sopenharmony_ci	fcport->deleted = 0;
616362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
616462306a36Sopenharmony_ci
616562306a36Sopenharmony_ci	switch (vha->host->active_mode) {
616662306a36Sopenharmony_ci	case MODE_INITIATOR:
616762306a36Sopenharmony_ci	case MODE_DUAL:
616862306a36Sopenharmony_ci		if (newfcport) {
616962306a36Sopenharmony_ci			if (!IS_IIDMA_CAPABLE(vha->hw) || !vha->hw->flags.gpsc_supported) {
617062306a36Sopenharmony_ci				qla24xx_sched_upd_fcport(fcport);
617162306a36Sopenharmony_ci			} else {
617262306a36Sopenharmony_ci				ql_dbg(ql_dbg_disc, vha, 0x20ff,
617362306a36Sopenharmony_ci				   "%s %d %8phC post gpsc fcp_cnt %d\n",
617462306a36Sopenharmony_ci				   __func__, __LINE__, fcport->port_name, vha->fcport_count);
617562306a36Sopenharmony_ci				qla24xx_post_gpsc_work(vha, fcport);
617662306a36Sopenharmony_ci			}
617762306a36Sopenharmony_ci		}
617862306a36Sopenharmony_ci		break;
617962306a36Sopenharmony_ci
618062306a36Sopenharmony_ci	case MODE_TARGET:
618162306a36Sopenharmony_ci	default:
618262306a36Sopenharmony_ci		break;
618362306a36Sopenharmony_ci	}
618462306a36Sopenharmony_ci	if (del)
618562306a36Sopenharmony_ci		qla2x00_free_fcport(del);
618662306a36Sopenharmony_ci
618762306a36Sopenharmony_ci	return fcport;
618862306a36Sopenharmony_ci}
618962306a36Sopenharmony_ci
619062306a36Sopenharmony_ci/* Must be called under tgt_mutex */
619162306a36Sopenharmony_cistatic struct fc_port *qlt_make_local_sess(struct scsi_qla_host *vha,
619262306a36Sopenharmony_ci					   be_id_t s_id)
619362306a36Sopenharmony_ci{
619462306a36Sopenharmony_ci	struct fc_port *sess = NULL;
619562306a36Sopenharmony_ci	fc_port_t *fcport = NULL;
619662306a36Sopenharmony_ci	int rc, global_resets;
619762306a36Sopenharmony_ci	uint16_t loop_id = 0;
619862306a36Sopenharmony_ci
619962306a36Sopenharmony_ci	if (s_id.domain == 0xFF && s_id.area == 0xFC) {
620062306a36Sopenharmony_ci		/*
620162306a36Sopenharmony_ci		 * This is Domain Controller, so it should be
620262306a36Sopenharmony_ci		 * OK to drop SCSI commands from it.
620362306a36Sopenharmony_ci		 */
620462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042,
620562306a36Sopenharmony_ci		    "Unable to find initiator with S_ID %x:%x:%x",
620662306a36Sopenharmony_ci		    s_id.domain, s_id.area, s_id.al_pa);
620762306a36Sopenharmony_ci		return NULL;
620862306a36Sopenharmony_ci	}
620962306a36Sopenharmony_ci
621062306a36Sopenharmony_ci	mutex_lock(&vha->vha_tgt.tgt_mutex);
621162306a36Sopenharmony_ci
621262306a36Sopenharmony_ciretry:
621362306a36Sopenharmony_ci	global_resets =
621462306a36Sopenharmony_ci	    atomic_read(&vha->vha_tgt.qla_tgt->tgt_global_resets_count);
621562306a36Sopenharmony_ci
621662306a36Sopenharmony_ci	rc = qla24xx_get_loop_id(vha, s_id, &loop_id);
621762306a36Sopenharmony_ci	if (rc != 0) {
621862306a36Sopenharmony_ci		mutex_unlock(&vha->vha_tgt.tgt_mutex);
621962306a36Sopenharmony_ci
622062306a36Sopenharmony_ci		ql_log(ql_log_info, vha, 0xf071,
622162306a36Sopenharmony_ci		    "qla_target(%d): Unable to find "
622262306a36Sopenharmony_ci		    "initiator with S_ID %x:%x:%x",
622362306a36Sopenharmony_ci		    vha->vp_idx, s_id.domain, s_id.area, s_id.al_pa);
622462306a36Sopenharmony_ci
622562306a36Sopenharmony_ci		if (rc == -ENOENT) {
622662306a36Sopenharmony_ci			qlt_port_logo_t logo;
622762306a36Sopenharmony_ci
622862306a36Sopenharmony_ci			logo.id = be_to_port_id(s_id);
622962306a36Sopenharmony_ci			logo.cmd_count = 1;
623062306a36Sopenharmony_ci			qlt_send_first_logo(vha, &logo);
623162306a36Sopenharmony_ci		}
623262306a36Sopenharmony_ci
623362306a36Sopenharmony_ci		return NULL;
623462306a36Sopenharmony_ci	}
623562306a36Sopenharmony_ci
623662306a36Sopenharmony_ci	fcport = qlt_get_port_database(vha, loop_id);
623762306a36Sopenharmony_ci	if (!fcport) {
623862306a36Sopenharmony_ci		mutex_unlock(&vha->vha_tgt.tgt_mutex);
623962306a36Sopenharmony_ci		return NULL;
624062306a36Sopenharmony_ci	}
624162306a36Sopenharmony_ci
624262306a36Sopenharmony_ci	if (global_resets !=
624362306a36Sopenharmony_ci	    atomic_read(&vha->vha_tgt.qla_tgt->tgt_global_resets_count)) {
624462306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf043,
624562306a36Sopenharmony_ci		    "qla_target(%d): global reset during session discovery "
624662306a36Sopenharmony_ci		    "(counter was %d, new %d), retrying", vha->vp_idx,
624762306a36Sopenharmony_ci		    global_resets,
624862306a36Sopenharmony_ci		    atomic_read(&vha->vha_tgt.
624962306a36Sopenharmony_ci			qla_tgt->tgt_global_resets_count));
625062306a36Sopenharmony_ci		goto retry;
625162306a36Sopenharmony_ci	}
625262306a36Sopenharmony_ci
625362306a36Sopenharmony_ci	sess = qlt_create_sess(vha, fcport, true);
625462306a36Sopenharmony_ci
625562306a36Sopenharmony_ci	mutex_unlock(&vha->vha_tgt.tgt_mutex);
625662306a36Sopenharmony_ci
625762306a36Sopenharmony_ci	return sess;
625862306a36Sopenharmony_ci}
625962306a36Sopenharmony_ci
626062306a36Sopenharmony_cistatic void qlt_abort_work(struct qla_tgt *tgt,
626162306a36Sopenharmony_ci	struct qla_tgt_sess_work_param *prm)
626262306a36Sopenharmony_ci{
626362306a36Sopenharmony_ci	struct scsi_qla_host *vha = tgt->vha;
626462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
626562306a36Sopenharmony_ci	struct fc_port *sess = NULL;
626662306a36Sopenharmony_ci	unsigned long flags = 0, flags2 = 0;
626762306a36Sopenharmony_ci	be_id_t s_id;
626862306a36Sopenharmony_ci	int rc;
626962306a36Sopenharmony_ci
627062306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.sess_lock, flags2);
627162306a36Sopenharmony_ci
627262306a36Sopenharmony_ci	if (tgt->tgt_stop)
627362306a36Sopenharmony_ci		goto out_term2;
627462306a36Sopenharmony_ci
627562306a36Sopenharmony_ci	s_id = le_id_to_be(prm->abts.fcp_hdr_le.s_id);
627662306a36Sopenharmony_ci
627762306a36Sopenharmony_ci	sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
627862306a36Sopenharmony_ci	if (!sess) {
627962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2);
628062306a36Sopenharmony_ci
628162306a36Sopenharmony_ci		sess = qlt_make_local_sess(vha, s_id);
628262306a36Sopenharmony_ci		/* sess has got an extra creation ref */
628362306a36Sopenharmony_ci
628462306a36Sopenharmony_ci		spin_lock_irqsave(&ha->tgt.sess_lock, flags2);
628562306a36Sopenharmony_ci		if (!sess)
628662306a36Sopenharmony_ci			goto out_term2;
628762306a36Sopenharmony_ci	} else {
628862306a36Sopenharmony_ci		if (sess->deleted) {
628962306a36Sopenharmony_ci			sess = NULL;
629062306a36Sopenharmony_ci			goto out_term2;
629162306a36Sopenharmony_ci		}
629262306a36Sopenharmony_ci
629362306a36Sopenharmony_ci		if (!kref_get_unless_zero(&sess->sess_kref)) {
629462306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt_tmr, vha, 0xf01c,
629562306a36Sopenharmony_ci			    "%s: kref_get fail %8phC \n",
629662306a36Sopenharmony_ci			     __func__, sess->port_name);
629762306a36Sopenharmony_ci			sess = NULL;
629862306a36Sopenharmony_ci			goto out_term2;
629962306a36Sopenharmony_ci		}
630062306a36Sopenharmony_ci	}
630162306a36Sopenharmony_ci
630262306a36Sopenharmony_ci	rc = __qlt_24xx_handle_abts(vha, &prm->abts, sess);
630362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2);
630462306a36Sopenharmony_ci
630562306a36Sopenharmony_ci	ha->tgt.tgt_ops->put_sess(sess);
630662306a36Sopenharmony_ci
630762306a36Sopenharmony_ci	if (rc != 0)
630862306a36Sopenharmony_ci		goto out_term;
630962306a36Sopenharmony_ci	return;
631062306a36Sopenharmony_ci
631162306a36Sopenharmony_ciout_term2:
631262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags2);
631362306a36Sopenharmony_ci
631462306a36Sopenharmony_ciout_term:
631562306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
631662306a36Sopenharmony_ci	qlt_24xx_send_abts_resp(ha->base_qpair, &prm->abts,
631762306a36Sopenharmony_ci	    FCP_TMF_REJECTED, false);
631862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
631962306a36Sopenharmony_ci}
632062306a36Sopenharmony_ci
632162306a36Sopenharmony_cistatic void qlt_sess_work_fn(struct work_struct *work)
632262306a36Sopenharmony_ci{
632362306a36Sopenharmony_ci	struct qla_tgt *tgt = container_of(work, struct qla_tgt, sess_work);
632462306a36Sopenharmony_ci	struct scsi_qla_host *vha = tgt->vha;
632562306a36Sopenharmony_ci	unsigned long flags;
632662306a36Sopenharmony_ci
632762306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf000, "Sess work (tgt %p)", tgt);
632862306a36Sopenharmony_ci
632962306a36Sopenharmony_ci	spin_lock_irqsave(&tgt->sess_work_lock, flags);
633062306a36Sopenharmony_ci	while (!list_empty(&tgt->sess_works_list)) {
633162306a36Sopenharmony_ci		struct qla_tgt_sess_work_param *prm = list_entry(
633262306a36Sopenharmony_ci		    tgt->sess_works_list.next, typeof(*prm),
633362306a36Sopenharmony_ci		    sess_works_list_entry);
633462306a36Sopenharmony_ci
633562306a36Sopenharmony_ci		/*
633662306a36Sopenharmony_ci		 * This work can be scheduled on several CPUs at time, so we
633762306a36Sopenharmony_ci		 * must delete the entry to eliminate double processing
633862306a36Sopenharmony_ci		 */
633962306a36Sopenharmony_ci		list_del(&prm->sess_works_list_entry);
634062306a36Sopenharmony_ci
634162306a36Sopenharmony_ci		spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
634262306a36Sopenharmony_ci
634362306a36Sopenharmony_ci		switch (prm->type) {
634462306a36Sopenharmony_ci		case QLA_TGT_SESS_WORK_ABORT:
634562306a36Sopenharmony_ci			qlt_abort_work(tgt, prm);
634662306a36Sopenharmony_ci			break;
634762306a36Sopenharmony_ci		default:
634862306a36Sopenharmony_ci			BUG_ON(1);
634962306a36Sopenharmony_ci			break;
635062306a36Sopenharmony_ci		}
635162306a36Sopenharmony_ci
635262306a36Sopenharmony_ci		spin_lock_irqsave(&tgt->sess_work_lock, flags);
635362306a36Sopenharmony_ci
635462306a36Sopenharmony_ci		kfree(prm);
635562306a36Sopenharmony_ci	}
635662306a36Sopenharmony_ci	spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
635762306a36Sopenharmony_ci}
635862306a36Sopenharmony_ci
635962306a36Sopenharmony_ci/* Must be called under tgt_host_action_mutex */
636062306a36Sopenharmony_ciint qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha)
636162306a36Sopenharmony_ci{
636262306a36Sopenharmony_ci	struct qla_tgt *tgt;
636362306a36Sopenharmony_ci	int rc, i;
636462306a36Sopenharmony_ci	struct qla_qpair_hint *h;
636562306a36Sopenharmony_ci
636662306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
636762306a36Sopenharmony_ci		return 0;
636862306a36Sopenharmony_ci
636962306a36Sopenharmony_ci	if (!IS_TGT_MODE_CAPABLE(ha)) {
637062306a36Sopenharmony_ci		ql_log(ql_log_warn, base_vha, 0xe070,
637162306a36Sopenharmony_ci		    "This adapter does not support target mode.\n");
637262306a36Sopenharmony_ci		return 0;
637362306a36Sopenharmony_ci	}
637462306a36Sopenharmony_ci
637562306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, base_vha, 0xe03b,
637662306a36Sopenharmony_ci	    "Registering target for host %ld(%p).\n", base_vha->host_no, ha);
637762306a36Sopenharmony_ci
637862306a36Sopenharmony_ci	BUG_ON(base_vha->vha_tgt.qla_tgt != NULL);
637962306a36Sopenharmony_ci
638062306a36Sopenharmony_ci	tgt = kzalloc(sizeof(struct qla_tgt), GFP_KERNEL);
638162306a36Sopenharmony_ci	if (!tgt) {
638262306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, base_vha, 0xe066,
638362306a36Sopenharmony_ci		    "Unable to allocate struct qla_tgt\n");
638462306a36Sopenharmony_ci		return -ENOMEM;
638562306a36Sopenharmony_ci	}
638662306a36Sopenharmony_ci
638762306a36Sopenharmony_ci	tgt->qphints = kcalloc(ha->max_qpairs + 1,
638862306a36Sopenharmony_ci			       sizeof(struct qla_qpair_hint),
638962306a36Sopenharmony_ci			       GFP_KERNEL);
639062306a36Sopenharmony_ci	if (!tgt->qphints) {
639162306a36Sopenharmony_ci		kfree(tgt);
639262306a36Sopenharmony_ci		ql_log(ql_log_warn, base_vha, 0x0197,
639362306a36Sopenharmony_ci		    "Unable to allocate qpair hints.\n");
639462306a36Sopenharmony_ci		return -ENOMEM;
639562306a36Sopenharmony_ci	}
639662306a36Sopenharmony_ci
639762306a36Sopenharmony_ci	qla2xxx_driver_template.supported_mode |= MODE_TARGET;
639862306a36Sopenharmony_ci
639962306a36Sopenharmony_ci	rc = btree_init64(&tgt->lun_qpair_map);
640062306a36Sopenharmony_ci	if (rc) {
640162306a36Sopenharmony_ci		kfree(tgt->qphints);
640262306a36Sopenharmony_ci		kfree(tgt);
640362306a36Sopenharmony_ci		ql_log(ql_log_info, base_vha, 0x0198,
640462306a36Sopenharmony_ci			"Unable to initialize lun_qpair_map btree\n");
640562306a36Sopenharmony_ci		return -EIO;
640662306a36Sopenharmony_ci	}
640762306a36Sopenharmony_ci	h = &tgt->qphints[0];
640862306a36Sopenharmony_ci	h->qpair = ha->base_qpair;
640962306a36Sopenharmony_ci	INIT_LIST_HEAD(&h->hint_elem);
641062306a36Sopenharmony_ci	h->cpuid = ha->base_qpair->cpuid;
641162306a36Sopenharmony_ci	list_add_tail(&h->hint_elem, &ha->base_qpair->hints_list);
641262306a36Sopenharmony_ci
641362306a36Sopenharmony_ci	for (i = 0; i < ha->max_qpairs; i++) {
641462306a36Sopenharmony_ci		unsigned long flags;
641562306a36Sopenharmony_ci
641662306a36Sopenharmony_ci		struct qla_qpair *qpair = ha->queue_pair_map[i];
641762306a36Sopenharmony_ci
641862306a36Sopenharmony_ci		h = &tgt->qphints[i + 1];
641962306a36Sopenharmony_ci		INIT_LIST_HEAD(&h->hint_elem);
642062306a36Sopenharmony_ci		if (qpair) {
642162306a36Sopenharmony_ci			h->qpair = qpair;
642262306a36Sopenharmony_ci			spin_lock_irqsave(qpair->qp_lock_ptr, flags);
642362306a36Sopenharmony_ci			list_add_tail(&h->hint_elem, &qpair->hints_list);
642462306a36Sopenharmony_ci			spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
642562306a36Sopenharmony_ci			h->cpuid = qpair->cpuid;
642662306a36Sopenharmony_ci		}
642762306a36Sopenharmony_ci	}
642862306a36Sopenharmony_ci
642962306a36Sopenharmony_ci	tgt->ha = ha;
643062306a36Sopenharmony_ci	tgt->vha = base_vha;
643162306a36Sopenharmony_ci	init_waitqueue_head(&tgt->waitQ);
643262306a36Sopenharmony_ci	spin_lock_init(&tgt->sess_work_lock);
643362306a36Sopenharmony_ci	INIT_WORK(&tgt->sess_work, qlt_sess_work_fn);
643462306a36Sopenharmony_ci	INIT_LIST_HEAD(&tgt->sess_works_list);
643562306a36Sopenharmony_ci	atomic_set(&tgt->tgt_global_resets_count, 0);
643662306a36Sopenharmony_ci
643762306a36Sopenharmony_ci	base_vha->vha_tgt.qla_tgt = tgt;
643862306a36Sopenharmony_ci
643962306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, base_vha, 0xe067,
644062306a36Sopenharmony_ci		"qla_target(%d): using 64 Bit PCI addressing",
644162306a36Sopenharmony_ci		base_vha->vp_idx);
644262306a36Sopenharmony_ci	/* 3 is reserved */
644362306a36Sopenharmony_ci	tgt->sg_tablesize = QLA_TGT_MAX_SG_24XX(base_vha->req->length - 3);
644462306a36Sopenharmony_ci
644562306a36Sopenharmony_ci	mutex_lock(&qla_tgt_mutex);
644662306a36Sopenharmony_ci	list_add_tail(&tgt->tgt_list_entry, &qla_tgt_glist);
644762306a36Sopenharmony_ci	mutex_unlock(&qla_tgt_mutex);
644862306a36Sopenharmony_ci
644962306a36Sopenharmony_ci	if (ha->tgt.tgt_ops && ha->tgt.tgt_ops->add_target)
645062306a36Sopenharmony_ci		ha->tgt.tgt_ops->add_target(base_vha);
645162306a36Sopenharmony_ci
645262306a36Sopenharmony_ci	return 0;
645362306a36Sopenharmony_ci}
645462306a36Sopenharmony_ci
645562306a36Sopenharmony_ci/* Must be called under tgt_host_action_mutex */
645662306a36Sopenharmony_ciint qlt_remove_target(struct qla_hw_data *ha, struct scsi_qla_host *vha)
645762306a36Sopenharmony_ci{
645862306a36Sopenharmony_ci	if (!vha->vha_tgt.qla_tgt)
645962306a36Sopenharmony_ci		return 0;
646062306a36Sopenharmony_ci
646162306a36Sopenharmony_ci	if (vha->fc_vport) {
646262306a36Sopenharmony_ci		qlt_release(vha->vha_tgt.qla_tgt);
646362306a36Sopenharmony_ci		return 0;
646462306a36Sopenharmony_ci	}
646562306a36Sopenharmony_ci
646662306a36Sopenharmony_ci	/* free left over qfull cmds */
646762306a36Sopenharmony_ci	qlt_init_term_exchange(vha);
646862306a36Sopenharmony_ci
646962306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt, vha, 0xe03c, "Unregistering target for host %ld(%p)",
647062306a36Sopenharmony_ci	    vha->host_no, ha);
647162306a36Sopenharmony_ci	qlt_release(vha->vha_tgt.qla_tgt);
647262306a36Sopenharmony_ci
647362306a36Sopenharmony_ci	return 0;
647462306a36Sopenharmony_ci}
647562306a36Sopenharmony_ci
647662306a36Sopenharmony_civoid qla_remove_hostmap(struct qla_hw_data *ha)
647762306a36Sopenharmony_ci{
647862306a36Sopenharmony_ci	struct scsi_qla_host *node;
647962306a36Sopenharmony_ci	u32 key = 0;
648062306a36Sopenharmony_ci
648162306a36Sopenharmony_ci	btree_for_each_safe32(&ha->host_map, key, node)
648262306a36Sopenharmony_ci		btree_remove32(&ha->host_map, key);
648362306a36Sopenharmony_ci
648462306a36Sopenharmony_ci	btree_destroy32(&ha->host_map);
648562306a36Sopenharmony_ci}
648662306a36Sopenharmony_ci
648762306a36Sopenharmony_cistatic void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn,
648862306a36Sopenharmony_ci	unsigned char *b)
648962306a36Sopenharmony_ci{
649062306a36Sopenharmony_ci	pr_debug("qla2xxx HW vha->node_name: %8phC\n", vha->node_name);
649162306a36Sopenharmony_ci	pr_debug("qla2xxx HW vha->port_name: %8phC\n", vha->port_name);
649262306a36Sopenharmony_ci	put_unaligned_be64(wwpn, b);
649362306a36Sopenharmony_ci	pr_debug("qla2xxx passed configfs WWPN: %8phC\n", b);
649462306a36Sopenharmony_ci}
649562306a36Sopenharmony_ci
649662306a36Sopenharmony_ci/**
649762306a36Sopenharmony_ci * qlt_lport_register - register lport with external module
649862306a36Sopenharmony_ci *
649962306a36Sopenharmony_ci * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data
650062306a36Sopenharmony_ci * @phys_wwpn: physical port WWPN
650162306a36Sopenharmony_ci * @npiv_wwpn: NPIV WWPN
650262306a36Sopenharmony_ci * @npiv_wwnn: NPIV WWNN
650362306a36Sopenharmony_ci * @callback:  lport initialization callback for tcm_qla2xxx code
650462306a36Sopenharmony_ci */
650562306a36Sopenharmony_ciint qlt_lport_register(void *target_lport_ptr, u64 phys_wwpn,
650662306a36Sopenharmony_ci		       u64 npiv_wwpn, u64 npiv_wwnn,
650762306a36Sopenharmony_ci		       int (*callback)(struct scsi_qla_host *, void *, u64, u64))
650862306a36Sopenharmony_ci{
650962306a36Sopenharmony_ci	struct qla_tgt *tgt;
651062306a36Sopenharmony_ci	struct scsi_qla_host *vha;
651162306a36Sopenharmony_ci	struct qla_hw_data *ha;
651262306a36Sopenharmony_ci	struct Scsi_Host *host;
651362306a36Sopenharmony_ci	unsigned long flags;
651462306a36Sopenharmony_ci	int rc;
651562306a36Sopenharmony_ci	u8 b[WWN_SIZE];
651662306a36Sopenharmony_ci
651762306a36Sopenharmony_ci	mutex_lock(&qla_tgt_mutex);
651862306a36Sopenharmony_ci	list_for_each_entry(tgt, &qla_tgt_glist, tgt_list_entry) {
651962306a36Sopenharmony_ci		vha = tgt->vha;
652062306a36Sopenharmony_ci		ha = vha->hw;
652162306a36Sopenharmony_ci
652262306a36Sopenharmony_ci		host = vha->host;
652362306a36Sopenharmony_ci		if (!host)
652462306a36Sopenharmony_ci			continue;
652562306a36Sopenharmony_ci
652662306a36Sopenharmony_ci		if (!(host->hostt->supported_mode & MODE_TARGET))
652762306a36Sopenharmony_ci			continue;
652862306a36Sopenharmony_ci
652962306a36Sopenharmony_ci		if (vha->qlini_mode == QLA2XXX_INI_MODE_ENABLED)
653062306a36Sopenharmony_ci			continue;
653162306a36Sopenharmony_ci
653262306a36Sopenharmony_ci		spin_lock_irqsave(&ha->hardware_lock, flags);
653362306a36Sopenharmony_ci		if ((!npiv_wwpn || !npiv_wwnn) && host->active_mode & MODE_TARGET) {
653462306a36Sopenharmony_ci			pr_debug("MODE_TARGET already active on qla2xxx(%d)\n",
653562306a36Sopenharmony_ci			    host->host_no);
653662306a36Sopenharmony_ci			spin_unlock_irqrestore(&ha->hardware_lock, flags);
653762306a36Sopenharmony_ci			continue;
653862306a36Sopenharmony_ci		}
653962306a36Sopenharmony_ci		if (tgt->tgt_stop) {
654062306a36Sopenharmony_ci			pr_debug("MODE_TARGET in shutdown on qla2xxx(%d)\n",
654162306a36Sopenharmony_ci				 host->host_no);
654262306a36Sopenharmony_ci			spin_unlock_irqrestore(&ha->hardware_lock, flags);
654362306a36Sopenharmony_ci			continue;
654462306a36Sopenharmony_ci		}
654562306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->hardware_lock, flags);
654662306a36Sopenharmony_ci
654762306a36Sopenharmony_ci		if (!scsi_host_get(host)) {
654862306a36Sopenharmony_ci			ql_dbg(ql_dbg_tgt, vha, 0xe068,
654962306a36Sopenharmony_ci			    "Unable to scsi_host_get() for"
655062306a36Sopenharmony_ci			    " qla2xxx scsi_host\n");
655162306a36Sopenharmony_ci			continue;
655262306a36Sopenharmony_ci		}
655362306a36Sopenharmony_ci		qlt_lport_dump(vha, phys_wwpn, b);
655462306a36Sopenharmony_ci
655562306a36Sopenharmony_ci		if (memcmp(vha->port_name, b, WWN_SIZE)) {
655662306a36Sopenharmony_ci			scsi_host_put(host);
655762306a36Sopenharmony_ci			continue;
655862306a36Sopenharmony_ci		}
655962306a36Sopenharmony_ci		rc = (*callback)(vha, target_lport_ptr, npiv_wwpn, npiv_wwnn);
656062306a36Sopenharmony_ci		if (rc != 0)
656162306a36Sopenharmony_ci			scsi_host_put(host);
656262306a36Sopenharmony_ci
656362306a36Sopenharmony_ci		mutex_unlock(&qla_tgt_mutex);
656462306a36Sopenharmony_ci		return rc;
656562306a36Sopenharmony_ci	}
656662306a36Sopenharmony_ci	mutex_unlock(&qla_tgt_mutex);
656762306a36Sopenharmony_ci
656862306a36Sopenharmony_ci	return -ENODEV;
656962306a36Sopenharmony_ci}
657062306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_lport_register);
657162306a36Sopenharmony_ci
657262306a36Sopenharmony_ci/**
657362306a36Sopenharmony_ci * qlt_lport_deregister - Degister lport
657462306a36Sopenharmony_ci *
657562306a36Sopenharmony_ci * @vha:  Registered scsi_qla_host pointer
657662306a36Sopenharmony_ci */
657762306a36Sopenharmony_civoid qlt_lport_deregister(struct scsi_qla_host *vha)
657862306a36Sopenharmony_ci{
657962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
658062306a36Sopenharmony_ci	struct Scsi_Host *sh = vha->host;
658162306a36Sopenharmony_ci	/*
658262306a36Sopenharmony_ci	 * Clear the target_lport_ptr qla_target_template pointer in qla_hw_data
658362306a36Sopenharmony_ci	 */
658462306a36Sopenharmony_ci	vha->vha_tgt.target_lport_ptr = NULL;
658562306a36Sopenharmony_ci	ha->tgt.tgt_ops = NULL;
658662306a36Sopenharmony_ci	/*
658762306a36Sopenharmony_ci	 * Release the Scsi_Host reference for the underlying qla2xxx host
658862306a36Sopenharmony_ci	 */
658962306a36Sopenharmony_ci	scsi_host_put(sh);
659062306a36Sopenharmony_ci}
659162306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_lport_deregister);
659262306a36Sopenharmony_ci
659362306a36Sopenharmony_ci/* Must be called under HW lock */
659462306a36Sopenharmony_civoid qlt_set_mode(struct scsi_qla_host *vha)
659562306a36Sopenharmony_ci{
659662306a36Sopenharmony_ci	switch (vha->qlini_mode) {
659762306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_DISABLED:
659862306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_EXCLUSIVE:
659962306a36Sopenharmony_ci		vha->host->active_mode = MODE_TARGET;
660062306a36Sopenharmony_ci		break;
660162306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_ENABLED:
660262306a36Sopenharmony_ci		vha->host->active_mode = MODE_INITIATOR;
660362306a36Sopenharmony_ci		break;
660462306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_DUAL:
660562306a36Sopenharmony_ci		vha->host->active_mode = MODE_DUAL;
660662306a36Sopenharmony_ci		break;
660762306a36Sopenharmony_ci	default:
660862306a36Sopenharmony_ci		break;
660962306a36Sopenharmony_ci	}
661062306a36Sopenharmony_ci}
661162306a36Sopenharmony_ci
661262306a36Sopenharmony_ci/* Must be called under HW lock */
661362306a36Sopenharmony_cistatic void qlt_clear_mode(struct scsi_qla_host *vha)
661462306a36Sopenharmony_ci{
661562306a36Sopenharmony_ci	switch (vha->qlini_mode) {
661662306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_DISABLED:
661762306a36Sopenharmony_ci		vha->host->active_mode = MODE_UNKNOWN;
661862306a36Sopenharmony_ci		break;
661962306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_EXCLUSIVE:
662062306a36Sopenharmony_ci		vha->host->active_mode = MODE_INITIATOR;
662162306a36Sopenharmony_ci		break;
662262306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_ENABLED:
662362306a36Sopenharmony_ci	case QLA2XXX_INI_MODE_DUAL:
662462306a36Sopenharmony_ci		vha->host->active_mode = MODE_INITIATOR;
662562306a36Sopenharmony_ci		break;
662662306a36Sopenharmony_ci	default:
662762306a36Sopenharmony_ci		break;
662862306a36Sopenharmony_ci	}
662962306a36Sopenharmony_ci}
663062306a36Sopenharmony_ci
663162306a36Sopenharmony_ci/*
663262306a36Sopenharmony_ci * qla_tgt_enable_vha - NO LOCK HELD
663362306a36Sopenharmony_ci *
663462306a36Sopenharmony_ci * host_reset, bring up w/ Target Mode Enabled
663562306a36Sopenharmony_ci */
663662306a36Sopenharmony_civoid
663762306a36Sopenharmony_ciqlt_enable_vha(struct scsi_qla_host *vha)
663862306a36Sopenharmony_ci{
663962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
664062306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
664162306a36Sopenharmony_ci	unsigned long flags;
664262306a36Sopenharmony_ci	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
664362306a36Sopenharmony_ci
664462306a36Sopenharmony_ci	if (!tgt) {
664562306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe069,
664662306a36Sopenharmony_ci		    "Unable to locate qla_tgt pointer from"
664762306a36Sopenharmony_ci		    " struct qla_hw_data\n");
664862306a36Sopenharmony_ci		dump_stack();
664962306a36Sopenharmony_ci		return;
665062306a36Sopenharmony_ci	}
665162306a36Sopenharmony_ci	if (vha->qlini_mode == QLA2XXX_INI_MODE_ENABLED)
665262306a36Sopenharmony_ci		return;
665362306a36Sopenharmony_ci
665462306a36Sopenharmony_ci	if (ha->tgt.num_act_qpairs > ha->max_qpairs)
665562306a36Sopenharmony_ci		ha->tgt.num_act_qpairs = ha->max_qpairs;
665662306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
665762306a36Sopenharmony_ci	tgt->tgt_stopped = 0;
665862306a36Sopenharmony_ci	qlt_set_mode(vha);
665962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
666062306a36Sopenharmony_ci
666162306a36Sopenharmony_ci	mutex_lock(&ha->optrom_mutex);
666262306a36Sopenharmony_ci	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf021,
666362306a36Sopenharmony_ci	    "%s.\n", __func__);
666462306a36Sopenharmony_ci	if (vha->vp_idx) {
666562306a36Sopenharmony_ci		qla24xx_disable_vp(vha);
666662306a36Sopenharmony_ci		qla24xx_enable_vp(vha);
666762306a36Sopenharmony_ci	} else {
666862306a36Sopenharmony_ci		set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
666962306a36Sopenharmony_ci		qla2xxx_wake_dpc(base_vha);
667062306a36Sopenharmony_ci		WARN_ON_ONCE(qla2x00_wait_for_hba_online(base_vha) !=
667162306a36Sopenharmony_ci			     QLA_SUCCESS);
667262306a36Sopenharmony_ci	}
667362306a36Sopenharmony_ci	mutex_unlock(&ha->optrom_mutex);
667462306a36Sopenharmony_ci}
667562306a36Sopenharmony_ciEXPORT_SYMBOL(qlt_enable_vha);
667662306a36Sopenharmony_ci
667762306a36Sopenharmony_ci/*
667862306a36Sopenharmony_ci * qla_tgt_disable_vha - NO LOCK HELD
667962306a36Sopenharmony_ci *
668062306a36Sopenharmony_ci * Disable Target Mode and reset the adapter
668162306a36Sopenharmony_ci */
668262306a36Sopenharmony_cistatic void qlt_disable_vha(struct scsi_qla_host *vha)
668362306a36Sopenharmony_ci{
668462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
668562306a36Sopenharmony_ci	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
668662306a36Sopenharmony_ci	unsigned long flags;
668762306a36Sopenharmony_ci
668862306a36Sopenharmony_ci	if (!tgt) {
668962306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe06a,
669062306a36Sopenharmony_ci		    "Unable to locate qla_tgt pointer from"
669162306a36Sopenharmony_ci		    " struct qla_hw_data\n");
669262306a36Sopenharmony_ci		dump_stack();
669362306a36Sopenharmony_ci		return;
669462306a36Sopenharmony_ci	}
669562306a36Sopenharmony_ci
669662306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
669762306a36Sopenharmony_ci	qlt_clear_mode(vha);
669862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
669962306a36Sopenharmony_ci
670062306a36Sopenharmony_ci	set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
670162306a36Sopenharmony_ci	qla2xxx_wake_dpc(vha);
670262306a36Sopenharmony_ci
670362306a36Sopenharmony_ci	/*
670462306a36Sopenharmony_ci	 * We are expecting the offline state.
670562306a36Sopenharmony_ci	 * QLA_FUNCTION_FAILED means that adapter is offline.
670662306a36Sopenharmony_ci	 */
670762306a36Sopenharmony_ci	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS)
670862306a36Sopenharmony_ci		ql_dbg(ql_dbg_tgt, vha, 0xe081,
670962306a36Sopenharmony_ci		       "adapter is offline\n");
671062306a36Sopenharmony_ci}
671162306a36Sopenharmony_ci
671262306a36Sopenharmony_ci/*
671362306a36Sopenharmony_ci * Called from qla_init.c:qla24xx_vport_create() contex to setup
671462306a36Sopenharmony_ci * the target mode specific struct scsi_qla_host and struct qla_hw_data
671562306a36Sopenharmony_ci * members.
671662306a36Sopenharmony_ci */
671762306a36Sopenharmony_civoid
671862306a36Sopenharmony_ciqlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha)
671962306a36Sopenharmony_ci{
672062306a36Sopenharmony_ci	vha->vha_tgt.qla_tgt = NULL;
672162306a36Sopenharmony_ci
672262306a36Sopenharmony_ci	mutex_init(&vha->vha_tgt.tgt_mutex);
672362306a36Sopenharmony_ci	mutex_init(&vha->vha_tgt.tgt_host_action_mutex);
672462306a36Sopenharmony_ci
672562306a36Sopenharmony_ci	INIT_LIST_HEAD(&vha->unknown_atio_list);
672662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&vha->unknown_atio_work, qlt_unknown_atio_work_fn);
672762306a36Sopenharmony_ci
672862306a36Sopenharmony_ci	qlt_clear_mode(vha);
672962306a36Sopenharmony_ci
673062306a36Sopenharmony_ci	/*
673162306a36Sopenharmony_ci	 * NOTE: Currently the value is kept the same for <24xx and
673262306a36Sopenharmony_ci	 * >=24xx ISPs. If it is necessary to change it,
673362306a36Sopenharmony_ci	 * the check should be added for specific ISPs,
673462306a36Sopenharmony_ci	 * assigning the value appropriately.
673562306a36Sopenharmony_ci	 */
673662306a36Sopenharmony_ci	ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
673762306a36Sopenharmony_ci
673862306a36Sopenharmony_ci	qlt_add_target(ha, vha);
673962306a36Sopenharmony_ci}
674062306a36Sopenharmony_ci
674162306a36Sopenharmony_ciu8
674262306a36Sopenharmony_ciqlt_rff_id(struct scsi_qla_host *vha)
674362306a36Sopenharmony_ci{
674462306a36Sopenharmony_ci	u8 fc4_feature = 0;
674562306a36Sopenharmony_ci	/*
674662306a36Sopenharmony_ci	 * FC-4 Feature bit 0 indicates target functionality to the name server.
674762306a36Sopenharmony_ci	 */
674862306a36Sopenharmony_ci	if (qla_tgt_mode_enabled(vha)) {
674962306a36Sopenharmony_ci		fc4_feature = BIT_0;
675062306a36Sopenharmony_ci	} else if (qla_ini_mode_enabled(vha)) {
675162306a36Sopenharmony_ci		fc4_feature = BIT_1;
675262306a36Sopenharmony_ci	} else if (qla_dual_mode_enabled(vha))
675362306a36Sopenharmony_ci		fc4_feature = BIT_0 | BIT_1;
675462306a36Sopenharmony_ci
675562306a36Sopenharmony_ci	return fc4_feature;
675662306a36Sopenharmony_ci}
675762306a36Sopenharmony_ci
675862306a36Sopenharmony_ci/*
675962306a36Sopenharmony_ci * qlt_init_atio_q_entries() - Initializes ATIO queue entries.
676062306a36Sopenharmony_ci * @ha: HA context
676162306a36Sopenharmony_ci *
676262306a36Sopenharmony_ci * Beginning of ATIO ring has initialization control block already built
676362306a36Sopenharmony_ci * by nvram config routine.
676462306a36Sopenharmony_ci *
676562306a36Sopenharmony_ci * Returns 0 on success.
676662306a36Sopenharmony_ci */
676762306a36Sopenharmony_civoid
676862306a36Sopenharmony_ciqlt_init_atio_q_entries(struct scsi_qla_host *vha)
676962306a36Sopenharmony_ci{
677062306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
677162306a36Sopenharmony_ci	uint16_t cnt;
677262306a36Sopenharmony_ci	struct atio_from_isp *pkt = (struct atio_from_isp *)ha->tgt.atio_ring;
677362306a36Sopenharmony_ci
677462306a36Sopenharmony_ci	if (qla_ini_mode_enabled(vha))
677562306a36Sopenharmony_ci		return;
677662306a36Sopenharmony_ci
677762306a36Sopenharmony_ci	for (cnt = 0; cnt < ha->tgt.atio_q_length; cnt++) {
677862306a36Sopenharmony_ci		pkt->u.raw.signature = cpu_to_le32(ATIO_PROCESSED);
677962306a36Sopenharmony_ci		pkt++;
678062306a36Sopenharmony_ci	}
678162306a36Sopenharmony_ci
678262306a36Sopenharmony_ci}
678362306a36Sopenharmony_ci
678462306a36Sopenharmony_ci/*
678562306a36Sopenharmony_ci * qlt_24xx_process_atio_queue() - Process ATIO queue entries.
678662306a36Sopenharmony_ci * @ha: SCSI driver HA context
678762306a36Sopenharmony_ci */
678862306a36Sopenharmony_civoid
678962306a36Sopenharmony_ciqlt_24xx_process_atio_queue(struct scsi_qla_host *vha, uint8_t ha_locked)
679062306a36Sopenharmony_ci{
679162306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
679262306a36Sopenharmony_ci	struct atio_from_isp *pkt;
679362306a36Sopenharmony_ci	int cnt, i;
679462306a36Sopenharmony_ci
679562306a36Sopenharmony_ci	if (!ha->flags.fw_started)
679662306a36Sopenharmony_ci		return;
679762306a36Sopenharmony_ci
679862306a36Sopenharmony_ci	while ((ha->tgt.atio_ring_ptr->signature != ATIO_PROCESSED) ||
679962306a36Sopenharmony_ci	    fcpcmd_is_corrupted(ha->tgt.atio_ring_ptr)) {
680062306a36Sopenharmony_ci		pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr;
680162306a36Sopenharmony_ci		cnt = pkt->u.raw.entry_count;
680262306a36Sopenharmony_ci
680362306a36Sopenharmony_ci		if (unlikely(fcpcmd_is_corrupted(ha->tgt.atio_ring_ptr))) {
680462306a36Sopenharmony_ci			/*
680562306a36Sopenharmony_ci			 * This packet is corrupted. The header + payload
680662306a36Sopenharmony_ci			 * can not be trusted. There is no point in passing
680762306a36Sopenharmony_ci			 * it further up.
680862306a36Sopenharmony_ci			 */
680962306a36Sopenharmony_ci			ql_log(ql_log_warn, vha, 0xd03c,
681062306a36Sopenharmony_ci			    "corrupted fcp frame SID[%3phN] OXID[%04x] EXCG[%x] %64phN\n",
681162306a36Sopenharmony_ci			    &pkt->u.isp24.fcp_hdr.s_id,
681262306a36Sopenharmony_ci			    be16_to_cpu(pkt->u.isp24.fcp_hdr.ox_id),
681362306a36Sopenharmony_ci			    pkt->u.isp24.exchange_addr, pkt);
681462306a36Sopenharmony_ci
681562306a36Sopenharmony_ci			adjust_corrupted_atio(pkt);
681662306a36Sopenharmony_ci			qlt_send_term_exchange(ha->base_qpair, NULL, pkt,
681762306a36Sopenharmony_ci			    ha_locked, 0);
681862306a36Sopenharmony_ci		} else {
681962306a36Sopenharmony_ci			qlt_24xx_atio_pkt_all_vps(vha,
682062306a36Sopenharmony_ci			    (struct atio_from_isp *)pkt, ha_locked);
682162306a36Sopenharmony_ci		}
682262306a36Sopenharmony_ci
682362306a36Sopenharmony_ci		for (i = 0; i < cnt; i++) {
682462306a36Sopenharmony_ci			ha->tgt.atio_ring_index++;
682562306a36Sopenharmony_ci			if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) {
682662306a36Sopenharmony_ci				ha->tgt.atio_ring_index = 0;
682762306a36Sopenharmony_ci				ha->tgt.atio_ring_ptr = ha->tgt.atio_ring;
682862306a36Sopenharmony_ci			} else
682962306a36Sopenharmony_ci				ha->tgt.atio_ring_ptr++;
683062306a36Sopenharmony_ci
683162306a36Sopenharmony_ci			pkt->u.raw.signature = cpu_to_le32(ATIO_PROCESSED);
683262306a36Sopenharmony_ci			pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr;
683362306a36Sopenharmony_ci		}
683462306a36Sopenharmony_ci		wmb();
683562306a36Sopenharmony_ci	}
683662306a36Sopenharmony_ci
683762306a36Sopenharmony_ci	/* Adjust ring index */
683862306a36Sopenharmony_ci	wrt_reg_dword(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index);
683962306a36Sopenharmony_ci}
684062306a36Sopenharmony_ci
684162306a36Sopenharmony_civoid
684262306a36Sopenharmony_ciqlt_24xx_config_rings(struct scsi_qla_host *vha)
684362306a36Sopenharmony_ci{
684462306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
684562306a36Sopenharmony_ci	struct qla_msix_entry *msix = &ha->msix_entries[2];
684662306a36Sopenharmony_ci	struct init_cb_24xx *icb = (struct init_cb_24xx *)ha->init_cb;
684762306a36Sopenharmony_ci
684862306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
684962306a36Sopenharmony_ci		return;
685062306a36Sopenharmony_ci
685162306a36Sopenharmony_ci	wrt_reg_dword(ISP_ATIO_Q_IN(vha), 0);
685262306a36Sopenharmony_ci	wrt_reg_dword(ISP_ATIO_Q_OUT(vha), 0);
685362306a36Sopenharmony_ci	rd_reg_dword(ISP_ATIO_Q_OUT(vha));
685462306a36Sopenharmony_ci
685562306a36Sopenharmony_ci	if (ha->flags.msix_enabled) {
685662306a36Sopenharmony_ci		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
685762306a36Sopenharmony_ci			icb->msix_atio = cpu_to_le16(msix->entry);
685862306a36Sopenharmony_ci			icb->firmware_options_2 &= cpu_to_le32(~BIT_26);
685962306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0xf072,
686062306a36Sopenharmony_ci			    "Registering ICB vector 0x%x for atio que.\n",
686162306a36Sopenharmony_ci			    msix->entry);
686262306a36Sopenharmony_ci		}
686362306a36Sopenharmony_ci	} else {
686462306a36Sopenharmony_ci		/* INTx|MSI */
686562306a36Sopenharmony_ci		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
686662306a36Sopenharmony_ci			icb->msix_atio = 0;
686762306a36Sopenharmony_ci			icb->firmware_options_2 |= cpu_to_le32(BIT_26);
686862306a36Sopenharmony_ci			ql_dbg(ql_dbg_init, vha, 0xf072,
686962306a36Sopenharmony_ci			    "%s: Use INTx for ATIOQ.\n", __func__);
687062306a36Sopenharmony_ci		}
687162306a36Sopenharmony_ci	}
687262306a36Sopenharmony_ci}
687362306a36Sopenharmony_ci
687462306a36Sopenharmony_civoid
687562306a36Sopenharmony_ciqlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv)
687662306a36Sopenharmony_ci{
687762306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
687862306a36Sopenharmony_ci	u32 tmp;
687962306a36Sopenharmony_ci
688062306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
688162306a36Sopenharmony_ci		return;
688262306a36Sopenharmony_ci
688362306a36Sopenharmony_ci	if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) {
688462306a36Sopenharmony_ci		if (!ha->tgt.saved_set) {
688562306a36Sopenharmony_ci			/* We save only once */
688662306a36Sopenharmony_ci			ha->tgt.saved_exchange_count = nv->exchange_count;
688762306a36Sopenharmony_ci			ha->tgt.saved_firmware_options_1 =
688862306a36Sopenharmony_ci			    nv->firmware_options_1;
688962306a36Sopenharmony_ci			ha->tgt.saved_firmware_options_2 =
689062306a36Sopenharmony_ci			    nv->firmware_options_2;
689162306a36Sopenharmony_ci			ha->tgt.saved_firmware_options_3 =
689262306a36Sopenharmony_ci			    nv->firmware_options_3;
689362306a36Sopenharmony_ci			ha->tgt.saved_set = 1;
689462306a36Sopenharmony_ci		}
689562306a36Sopenharmony_ci
689662306a36Sopenharmony_ci		if (qla_tgt_mode_enabled(vha))
689762306a36Sopenharmony_ci			nv->exchange_count = cpu_to_le16(0xFFFF);
689862306a36Sopenharmony_ci		else			/* dual */
689962306a36Sopenharmony_ci			nv->exchange_count = cpu_to_le16(vha->ql2xexchoffld);
690062306a36Sopenharmony_ci
690162306a36Sopenharmony_ci		/* Enable target mode */
690262306a36Sopenharmony_ci		nv->firmware_options_1 |= cpu_to_le32(BIT_4);
690362306a36Sopenharmony_ci
690462306a36Sopenharmony_ci		/* Disable ini mode, if requested */
690562306a36Sopenharmony_ci		if (qla_tgt_mode_enabled(vha))
690662306a36Sopenharmony_ci			nv->firmware_options_1 |= cpu_to_le32(BIT_5);
690762306a36Sopenharmony_ci
690862306a36Sopenharmony_ci		/* Disable Full Login after LIP */
690962306a36Sopenharmony_ci		nv->firmware_options_1 &= cpu_to_le32(~BIT_13);
691062306a36Sopenharmony_ci		/* Enable initial LIP */
691162306a36Sopenharmony_ci		nv->firmware_options_1 &= cpu_to_le32(~BIT_9);
691262306a36Sopenharmony_ci		if (ql2xtgt_tape_enable)
691362306a36Sopenharmony_ci			/* Enable FC Tape support */
691462306a36Sopenharmony_ci			nv->firmware_options_2 |= cpu_to_le32(BIT_12);
691562306a36Sopenharmony_ci		else
691662306a36Sopenharmony_ci			/* Disable FC Tape support */
691762306a36Sopenharmony_ci			nv->firmware_options_2 &= cpu_to_le32(~BIT_12);
691862306a36Sopenharmony_ci
691962306a36Sopenharmony_ci		/* Disable Full Login after LIP */
692062306a36Sopenharmony_ci		nv->host_p &= cpu_to_le32(~BIT_10);
692162306a36Sopenharmony_ci
692262306a36Sopenharmony_ci		/*
692362306a36Sopenharmony_ci		 * clear BIT 15 explicitly as we have seen at least
692462306a36Sopenharmony_ci		 * a couple of instances where this was set and this
692562306a36Sopenharmony_ci		 * was causing the firmware to not be initialized.
692662306a36Sopenharmony_ci		 */
692762306a36Sopenharmony_ci		nv->firmware_options_1 &= cpu_to_le32(~BIT_15);
692862306a36Sopenharmony_ci		/* Enable target PRLI control */
692962306a36Sopenharmony_ci		nv->firmware_options_2 |= cpu_to_le32(BIT_14);
693062306a36Sopenharmony_ci
693162306a36Sopenharmony_ci		if (IS_QLA25XX(ha)) {
693262306a36Sopenharmony_ci			/* Change Loop-prefer to Pt-Pt */
693362306a36Sopenharmony_ci			tmp = ~(BIT_4|BIT_5|BIT_6);
693462306a36Sopenharmony_ci			nv->firmware_options_2 &= cpu_to_le32(tmp);
693562306a36Sopenharmony_ci			tmp = P2P << 4;
693662306a36Sopenharmony_ci			nv->firmware_options_2 |= cpu_to_le32(tmp);
693762306a36Sopenharmony_ci		}
693862306a36Sopenharmony_ci	} else {
693962306a36Sopenharmony_ci		if (ha->tgt.saved_set) {
694062306a36Sopenharmony_ci			nv->exchange_count = ha->tgt.saved_exchange_count;
694162306a36Sopenharmony_ci			nv->firmware_options_1 =
694262306a36Sopenharmony_ci			    ha->tgt.saved_firmware_options_1;
694362306a36Sopenharmony_ci			nv->firmware_options_2 =
694462306a36Sopenharmony_ci			    ha->tgt.saved_firmware_options_2;
694562306a36Sopenharmony_ci			nv->firmware_options_3 =
694662306a36Sopenharmony_ci			    ha->tgt.saved_firmware_options_3;
694762306a36Sopenharmony_ci		}
694862306a36Sopenharmony_ci		return;
694962306a36Sopenharmony_ci	}
695062306a36Sopenharmony_ci
695162306a36Sopenharmony_ci	if (ha->base_qpair->enable_class_2) {
695262306a36Sopenharmony_ci		if (vha->flags.init_done)
695362306a36Sopenharmony_ci			fc_host_supported_classes(vha->host) =
695462306a36Sopenharmony_ci				FC_COS_CLASS2 | FC_COS_CLASS3;
695562306a36Sopenharmony_ci
695662306a36Sopenharmony_ci		nv->firmware_options_2 |= cpu_to_le32(BIT_8);
695762306a36Sopenharmony_ci	} else {
695862306a36Sopenharmony_ci		if (vha->flags.init_done)
695962306a36Sopenharmony_ci			fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
696062306a36Sopenharmony_ci
696162306a36Sopenharmony_ci		nv->firmware_options_2 &= ~cpu_to_le32(BIT_8);
696262306a36Sopenharmony_ci	}
696362306a36Sopenharmony_ci}
696462306a36Sopenharmony_ci
696562306a36Sopenharmony_civoid
696662306a36Sopenharmony_ciqlt_24xx_config_nvram_stage2(struct scsi_qla_host *vha,
696762306a36Sopenharmony_ci	struct init_cb_24xx *icb)
696862306a36Sopenharmony_ci{
696962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
697062306a36Sopenharmony_ci
697162306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
697262306a36Sopenharmony_ci		return;
697362306a36Sopenharmony_ci
697462306a36Sopenharmony_ci	if (ha->tgt.node_name_set) {
697562306a36Sopenharmony_ci		memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE);
697662306a36Sopenharmony_ci		icb->firmware_options_1 |= cpu_to_le32(BIT_14);
697762306a36Sopenharmony_ci	}
697862306a36Sopenharmony_ci}
697962306a36Sopenharmony_ci
698062306a36Sopenharmony_civoid
698162306a36Sopenharmony_ciqlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv)
698262306a36Sopenharmony_ci{
698362306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
698462306a36Sopenharmony_ci	u32 tmp;
698562306a36Sopenharmony_ci
698662306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
698762306a36Sopenharmony_ci		return;
698862306a36Sopenharmony_ci
698962306a36Sopenharmony_ci	if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha)) {
699062306a36Sopenharmony_ci		if (!ha->tgt.saved_set) {
699162306a36Sopenharmony_ci			/* We save only once */
699262306a36Sopenharmony_ci			ha->tgt.saved_exchange_count = nv->exchange_count;
699362306a36Sopenharmony_ci			ha->tgt.saved_firmware_options_1 =
699462306a36Sopenharmony_ci			    nv->firmware_options_1;
699562306a36Sopenharmony_ci			ha->tgt.saved_firmware_options_2 =
699662306a36Sopenharmony_ci			    nv->firmware_options_2;
699762306a36Sopenharmony_ci			ha->tgt.saved_firmware_options_3 =
699862306a36Sopenharmony_ci			    nv->firmware_options_3;
699962306a36Sopenharmony_ci			ha->tgt.saved_set = 1;
700062306a36Sopenharmony_ci		}
700162306a36Sopenharmony_ci
700262306a36Sopenharmony_ci		if (qla_tgt_mode_enabled(vha))
700362306a36Sopenharmony_ci			nv->exchange_count = cpu_to_le16(0xFFFF);
700462306a36Sopenharmony_ci		else			/* dual */
700562306a36Sopenharmony_ci			nv->exchange_count = cpu_to_le16(vha->ql2xexchoffld);
700662306a36Sopenharmony_ci
700762306a36Sopenharmony_ci		/* Enable target mode */
700862306a36Sopenharmony_ci		nv->firmware_options_1 |= cpu_to_le32(BIT_4);
700962306a36Sopenharmony_ci
701062306a36Sopenharmony_ci		/* Disable ini mode, if requested */
701162306a36Sopenharmony_ci		if (qla_tgt_mode_enabled(vha))
701262306a36Sopenharmony_ci			nv->firmware_options_1 |= cpu_to_le32(BIT_5);
701362306a36Sopenharmony_ci		/* Disable Full Login after LIP */
701462306a36Sopenharmony_ci		nv->firmware_options_1 &= cpu_to_le32(~BIT_13);
701562306a36Sopenharmony_ci		/* Enable initial LIP */
701662306a36Sopenharmony_ci		nv->firmware_options_1 &= cpu_to_le32(~BIT_9);
701762306a36Sopenharmony_ci		/*
701862306a36Sopenharmony_ci		 * clear BIT 15 explicitly as we have seen at
701962306a36Sopenharmony_ci		 * least a couple of instances where this was set
702062306a36Sopenharmony_ci		 * and this was causing the firmware to not be
702162306a36Sopenharmony_ci		 * initialized.
702262306a36Sopenharmony_ci		 */
702362306a36Sopenharmony_ci		nv->firmware_options_1 &= cpu_to_le32(~BIT_15);
702462306a36Sopenharmony_ci		if (ql2xtgt_tape_enable)
702562306a36Sopenharmony_ci			/* Enable FC tape support */
702662306a36Sopenharmony_ci			nv->firmware_options_2 |= cpu_to_le32(BIT_12);
702762306a36Sopenharmony_ci		else
702862306a36Sopenharmony_ci			/* Disable FC tape support */
702962306a36Sopenharmony_ci			nv->firmware_options_2 &= cpu_to_le32(~BIT_12);
703062306a36Sopenharmony_ci
703162306a36Sopenharmony_ci		/* Disable Full Login after LIP */
703262306a36Sopenharmony_ci		nv->host_p &= cpu_to_le32(~BIT_10);
703362306a36Sopenharmony_ci		/* Enable target PRLI control */
703462306a36Sopenharmony_ci		nv->firmware_options_2 |= cpu_to_le32(BIT_14);
703562306a36Sopenharmony_ci
703662306a36Sopenharmony_ci		/* Change Loop-prefer to Pt-Pt */
703762306a36Sopenharmony_ci		tmp = ~(BIT_4|BIT_5|BIT_6);
703862306a36Sopenharmony_ci		nv->firmware_options_2 &= cpu_to_le32(tmp);
703962306a36Sopenharmony_ci		tmp = P2P << 4;
704062306a36Sopenharmony_ci		nv->firmware_options_2 |= cpu_to_le32(tmp);
704162306a36Sopenharmony_ci	} else {
704262306a36Sopenharmony_ci		if (ha->tgt.saved_set) {
704362306a36Sopenharmony_ci			nv->exchange_count = ha->tgt.saved_exchange_count;
704462306a36Sopenharmony_ci			nv->firmware_options_1 =
704562306a36Sopenharmony_ci			    ha->tgt.saved_firmware_options_1;
704662306a36Sopenharmony_ci			nv->firmware_options_2 =
704762306a36Sopenharmony_ci			    ha->tgt.saved_firmware_options_2;
704862306a36Sopenharmony_ci			nv->firmware_options_3 =
704962306a36Sopenharmony_ci			    ha->tgt.saved_firmware_options_3;
705062306a36Sopenharmony_ci		}
705162306a36Sopenharmony_ci		return;
705262306a36Sopenharmony_ci	}
705362306a36Sopenharmony_ci
705462306a36Sopenharmony_ci	if (ha->base_qpair->enable_class_2) {
705562306a36Sopenharmony_ci		if (vha->flags.init_done)
705662306a36Sopenharmony_ci			fc_host_supported_classes(vha->host) =
705762306a36Sopenharmony_ci				FC_COS_CLASS2 | FC_COS_CLASS3;
705862306a36Sopenharmony_ci
705962306a36Sopenharmony_ci		nv->firmware_options_2 |= cpu_to_le32(BIT_8);
706062306a36Sopenharmony_ci	} else {
706162306a36Sopenharmony_ci		if (vha->flags.init_done)
706262306a36Sopenharmony_ci			fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
706362306a36Sopenharmony_ci
706462306a36Sopenharmony_ci		nv->firmware_options_2 &= ~cpu_to_le32(BIT_8);
706562306a36Sopenharmony_ci	}
706662306a36Sopenharmony_ci}
706762306a36Sopenharmony_ci
706862306a36Sopenharmony_civoid
706962306a36Sopenharmony_ciqlt_81xx_config_nvram_stage2(struct scsi_qla_host *vha,
707062306a36Sopenharmony_ci	struct init_cb_81xx *icb)
707162306a36Sopenharmony_ci{
707262306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
707362306a36Sopenharmony_ci
707462306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
707562306a36Sopenharmony_ci		return;
707662306a36Sopenharmony_ci
707762306a36Sopenharmony_ci	if (ha->tgt.node_name_set) {
707862306a36Sopenharmony_ci		memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE);
707962306a36Sopenharmony_ci		icb->firmware_options_1 |= cpu_to_le32(BIT_14);
708062306a36Sopenharmony_ci	}
708162306a36Sopenharmony_ci}
708262306a36Sopenharmony_ci
708362306a36Sopenharmony_civoid
708462306a36Sopenharmony_ciqlt_83xx_iospace_config(struct qla_hw_data *ha)
708562306a36Sopenharmony_ci{
708662306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
708762306a36Sopenharmony_ci		return;
708862306a36Sopenharmony_ci
708962306a36Sopenharmony_ci	ha->msix_count += 1; /* For ATIO Q */
709062306a36Sopenharmony_ci}
709162306a36Sopenharmony_ci
709262306a36Sopenharmony_ci
709362306a36Sopenharmony_civoid
709462306a36Sopenharmony_ciqlt_modify_vp_config(struct scsi_qla_host *vha,
709562306a36Sopenharmony_ci	struct vp_config_entry_24xx *vpmod)
709662306a36Sopenharmony_ci{
709762306a36Sopenharmony_ci	/* enable target mode.  Bit5 = 1 => disable */
709862306a36Sopenharmony_ci	if (qla_tgt_mode_enabled(vha) || qla_dual_mode_enabled(vha))
709962306a36Sopenharmony_ci		vpmod->options_idx1 &= ~BIT_5;
710062306a36Sopenharmony_ci
710162306a36Sopenharmony_ci	/* Disable ini mode, if requested.  bit4 = 1 => disable */
710262306a36Sopenharmony_ci	if (qla_tgt_mode_enabled(vha))
710362306a36Sopenharmony_ci		vpmod->options_idx1 &= ~BIT_4;
710462306a36Sopenharmony_ci}
710562306a36Sopenharmony_ci
710662306a36Sopenharmony_civoid
710762306a36Sopenharmony_ciqlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha)
710862306a36Sopenharmony_ci{
710962306a36Sopenharmony_ci	mutex_init(&base_vha->vha_tgt.tgt_mutex);
711062306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
711162306a36Sopenharmony_ci		return;
711262306a36Sopenharmony_ci
711362306a36Sopenharmony_ci	if  (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
711462306a36Sopenharmony_ci		ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in;
711562306a36Sopenharmony_ci		ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out;
711662306a36Sopenharmony_ci	} else {
711762306a36Sopenharmony_ci		ISP_ATIO_Q_IN(base_vha) = &ha->iobase->isp24.atio_q_in;
711862306a36Sopenharmony_ci		ISP_ATIO_Q_OUT(base_vha) = &ha->iobase->isp24.atio_q_out;
711962306a36Sopenharmony_ci	}
712062306a36Sopenharmony_ci
712162306a36Sopenharmony_ci	mutex_init(&base_vha->vha_tgt.tgt_host_action_mutex);
712262306a36Sopenharmony_ci
712362306a36Sopenharmony_ci	INIT_LIST_HEAD(&base_vha->unknown_atio_list);
712462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&base_vha->unknown_atio_work,
712562306a36Sopenharmony_ci	    qlt_unknown_atio_work_fn);
712662306a36Sopenharmony_ci
712762306a36Sopenharmony_ci	qlt_clear_mode(base_vha);
712862306a36Sopenharmony_ci
712962306a36Sopenharmony_ci	qla_update_vp_map(base_vha, SET_VP_IDX);
713062306a36Sopenharmony_ci}
713162306a36Sopenharmony_ci
713262306a36Sopenharmony_ciirqreturn_t
713362306a36Sopenharmony_ciqla83xx_msix_atio_q(int irq, void *dev_id)
713462306a36Sopenharmony_ci{
713562306a36Sopenharmony_ci	struct rsp_que *rsp;
713662306a36Sopenharmony_ci	scsi_qla_host_t	*vha;
713762306a36Sopenharmony_ci	struct qla_hw_data *ha;
713862306a36Sopenharmony_ci	unsigned long flags;
713962306a36Sopenharmony_ci
714062306a36Sopenharmony_ci	rsp = (struct rsp_que *) dev_id;
714162306a36Sopenharmony_ci	ha = rsp->hw;
714262306a36Sopenharmony_ci	vha = pci_get_drvdata(ha->pdev);
714362306a36Sopenharmony_ci
714462306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.atio_lock, flags);
714562306a36Sopenharmony_ci
714662306a36Sopenharmony_ci	qlt_24xx_process_atio_queue(vha, 0);
714762306a36Sopenharmony_ci
714862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.atio_lock, flags);
714962306a36Sopenharmony_ci
715062306a36Sopenharmony_ci	return IRQ_HANDLED;
715162306a36Sopenharmony_ci}
715262306a36Sopenharmony_ci
715362306a36Sopenharmony_cistatic void
715462306a36Sopenharmony_ciqlt_handle_abts_recv_work(struct work_struct *work)
715562306a36Sopenharmony_ci{
715662306a36Sopenharmony_ci	struct qla_tgt_sess_op *op = container_of(work,
715762306a36Sopenharmony_ci		struct qla_tgt_sess_op, work);
715862306a36Sopenharmony_ci	scsi_qla_host_t *vha = op->vha;
715962306a36Sopenharmony_ci	struct qla_hw_data *ha = vha->hw;
716062306a36Sopenharmony_ci	unsigned long flags;
716162306a36Sopenharmony_ci
716262306a36Sopenharmony_ci	if (qla2x00_reset_active(vha) ||
716362306a36Sopenharmony_ci	    (op->chip_reset != ha->base_qpair->chip_reset))
716462306a36Sopenharmony_ci		return;
716562306a36Sopenharmony_ci
716662306a36Sopenharmony_ci	spin_lock_irqsave(&ha->tgt.atio_lock, flags);
716762306a36Sopenharmony_ci	qlt_24xx_process_atio_queue(vha, 0);
716862306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->tgt.atio_lock, flags);
716962306a36Sopenharmony_ci
717062306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
717162306a36Sopenharmony_ci	qlt_response_pkt_all_vps(vha, op->rsp, (response_t *)&op->atio);
717262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
717362306a36Sopenharmony_ci
717462306a36Sopenharmony_ci	kfree(op);
717562306a36Sopenharmony_ci}
717662306a36Sopenharmony_ci
717762306a36Sopenharmony_civoid
717862306a36Sopenharmony_ciqlt_handle_abts_recv(struct scsi_qla_host *vha, struct rsp_que *rsp,
717962306a36Sopenharmony_ci    response_t *pkt)
718062306a36Sopenharmony_ci{
718162306a36Sopenharmony_ci	struct qla_tgt_sess_op *op;
718262306a36Sopenharmony_ci
718362306a36Sopenharmony_ci	op = kzalloc(sizeof(*op), GFP_ATOMIC);
718462306a36Sopenharmony_ci
718562306a36Sopenharmony_ci	if (!op) {
718662306a36Sopenharmony_ci		/* do not reach for ATIO queue here.  This is best effort err
718762306a36Sopenharmony_ci		 * recovery at this point.
718862306a36Sopenharmony_ci		 */
718962306a36Sopenharmony_ci		qlt_response_pkt_all_vps(vha, rsp, pkt);
719062306a36Sopenharmony_ci		return;
719162306a36Sopenharmony_ci	}
719262306a36Sopenharmony_ci
719362306a36Sopenharmony_ci	memcpy(&op->atio, pkt, sizeof(*pkt));
719462306a36Sopenharmony_ci	op->vha = vha;
719562306a36Sopenharmony_ci	op->chip_reset = vha->hw->base_qpair->chip_reset;
719662306a36Sopenharmony_ci	op->rsp = rsp;
719762306a36Sopenharmony_ci	INIT_WORK(&op->work, qlt_handle_abts_recv_work);
719862306a36Sopenharmony_ci	queue_work(qla_tgt_wq, &op->work);
719962306a36Sopenharmony_ci	return;
720062306a36Sopenharmony_ci}
720162306a36Sopenharmony_ci
720262306a36Sopenharmony_ciint
720362306a36Sopenharmony_ciqlt_mem_alloc(struct qla_hw_data *ha)
720462306a36Sopenharmony_ci{
720562306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
720662306a36Sopenharmony_ci		return 0;
720762306a36Sopenharmony_ci
720862306a36Sopenharmony_ci	ha->tgt.atio_ring = dma_alloc_coherent(&ha->pdev->dev,
720962306a36Sopenharmony_ci	    (ha->tgt.atio_q_length + 1) * sizeof(struct atio_from_isp),
721062306a36Sopenharmony_ci	    &ha->tgt.atio_dma, GFP_KERNEL);
721162306a36Sopenharmony_ci	if (!ha->tgt.atio_ring) {
721262306a36Sopenharmony_ci		return -ENOMEM;
721362306a36Sopenharmony_ci	}
721462306a36Sopenharmony_ci	return 0;
721562306a36Sopenharmony_ci}
721662306a36Sopenharmony_ci
721762306a36Sopenharmony_civoid
721862306a36Sopenharmony_ciqlt_mem_free(struct qla_hw_data *ha)
721962306a36Sopenharmony_ci{
722062306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
722162306a36Sopenharmony_ci		return;
722262306a36Sopenharmony_ci
722362306a36Sopenharmony_ci	if (ha->tgt.atio_ring) {
722462306a36Sopenharmony_ci		dma_free_coherent(&ha->pdev->dev, (ha->tgt.atio_q_length + 1) *
722562306a36Sopenharmony_ci		    sizeof(struct atio_from_isp), ha->tgt.atio_ring,
722662306a36Sopenharmony_ci		    ha->tgt.atio_dma);
722762306a36Sopenharmony_ci	}
722862306a36Sopenharmony_ci	ha->tgt.atio_ring = NULL;
722962306a36Sopenharmony_ci	ha->tgt.atio_dma = 0;
723062306a36Sopenharmony_ci}
723162306a36Sopenharmony_ci
723262306a36Sopenharmony_cistatic int __init qlt_parse_ini_mode(void)
723362306a36Sopenharmony_ci{
723462306a36Sopenharmony_ci	if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_EXCLUSIVE) == 0)
723562306a36Sopenharmony_ci		ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE;
723662306a36Sopenharmony_ci	else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_DISABLED) == 0)
723762306a36Sopenharmony_ci		ql2x_ini_mode = QLA2XXX_INI_MODE_DISABLED;
723862306a36Sopenharmony_ci	else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_ENABLED) == 0)
723962306a36Sopenharmony_ci		ql2x_ini_mode = QLA2XXX_INI_MODE_ENABLED;
724062306a36Sopenharmony_ci	else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_DUAL) == 0)
724162306a36Sopenharmony_ci		ql2x_ini_mode = QLA2XXX_INI_MODE_DUAL;
724262306a36Sopenharmony_ci	else
724362306a36Sopenharmony_ci		return false;
724462306a36Sopenharmony_ci
724562306a36Sopenharmony_ci	return true;
724662306a36Sopenharmony_ci}
724762306a36Sopenharmony_ci
724862306a36Sopenharmony_ciint __init qlt_init(void)
724962306a36Sopenharmony_ci{
725062306a36Sopenharmony_ci	int ret;
725162306a36Sopenharmony_ci
725262306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct ctio7_to_24xx) != 64);
725362306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct ctio_to_2xxx) != 64);
725462306a36Sopenharmony_ci
725562306a36Sopenharmony_ci	if (!qlt_parse_ini_mode()) {
725662306a36Sopenharmony_ci		ql_log(ql_log_fatal, NULL, 0xe06b,
725762306a36Sopenharmony_ci		    "qlt_parse_ini_mode() failed\n");
725862306a36Sopenharmony_ci		return -EINVAL;
725962306a36Sopenharmony_ci	}
726062306a36Sopenharmony_ci
726162306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
726262306a36Sopenharmony_ci		return 0;
726362306a36Sopenharmony_ci
726462306a36Sopenharmony_ci	qla_tgt_mgmt_cmd_cachep = kmem_cache_create("qla_tgt_mgmt_cmd_cachep",
726562306a36Sopenharmony_ci	    sizeof(struct qla_tgt_mgmt_cmd), __alignof__(struct
726662306a36Sopenharmony_ci	    qla_tgt_mgmt_cmd), 0, NULL);
726762306a36Sopenharmony_ci	if (!qla_tgt_mgmt_cmd_cachep) {
726862306a36Sopenharmony_ci		ql_log(ql_log_fatal, NULL, 0xd04b,
726962306a36Sopenharmony_ci		    "kmem_cache_create for qla_tgt_mgmt_cmd_cachep failed\n");
727062306a36Sopenharmony_ci		return -ENOMEM;
727162306a36Sopenharmony_ci	}
727262306a36Sopenharmony_ci
727362306a36Sopenharmony_ci	qla_tgt_plogi_cachep = kmem_cache_create("qla_tgt_plogi_cachep",
727462306a36Sopenharmony_ci	    sizeof(struct qlt_plogi_ack_t), __alignof__(struct qlt_plogi_ack_t),
727562306a36Sopenharmony_ci	    0, NULL);
727662306a36Sopenharmony_ci
727762306a36Sopenharmony_ci	if (!qla_tgt_plogi_cachep) {
727862306a36Sopenharmony_ci		ql_log(ql_log_fatal, NULL, 0xe06d,
727962306a36Sopenharmony_ci		    "kmem_cache_create for qla_tgt_plogi_cachep failed\n");
728062306a36Sopenharmony_ci		ret = -ENOMEM;
728162306a36Sopenharmony_ci		goto out_mgmt_cmd_cachep;
728262306a36Sopenharmony_ci	}
728362306a36Sopenharmony_ci
728462306a36Sopenharmony_ci	qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab,
728562306a36Sopenharmony_ci	    mempool_free_slab, qla_tgt_mgmt_cmd_cachep);
728662306a36Sopenharmony_ci	if (!qla_tgt_mgmt_cmd_mempool) {
728762306a36Sopenharmony_ci		ql_log(ql_log_fatal, NULL, 0xe06e,
728862306a36Sopenharmony_ci		    "mempool_create for qla_tgt_mgmt_cmd_mempool failed\n");
728962306a36Sopenharmony_ci		ret = -ENOMEM;
729062306a36Sopenharmony_ci		goto out_plogi_cachep;
729162306a36Sopenharmony_ci	}
729262306a36Sopenharmony_ci
729362306a36Sopenharmony_ci	qla_tgt_wq = alloc_workqueue("qla_tgt_wq", 0, 0);
729462306a36Sopenharmony_ci	if (!qla_tgt_wq) {
729562306a36Sopenharmony_ci		ql_log(ql_log_fatal, NULL, 0xe06f,
729662306a36Sopenharmony_ci		    "alloc_workqueue for qla_tgt_wq failed\n");
729762306a36Sopenharmony_ci		ret = -ENOMEM;
729862306a36Sopenharmony_ci		goto out_cmd_mempool;
729962306a36Sopenharmony_ci	}
730062306a36Sopenharmony_ci	/*
730162306a36Sopenharmony_ci	 * Return 1 to signal that initiator-mode is being disabled
730262306a36Sopenharmony_ci	 */
730362306a36Sopenharmony_ci	return (ql2x_ini_mode == QLA2XXX_INI_MODE_DISABLED) ? 1 : 0;
730462306a36Sopenharmony_ci
730562306a36Sopenharmony_ciout_cmd_mempool:
730662306a36Sopenharmony_ci	mempool_destroy(qla_tgt_mgmt_cmd_mempool);
730762306a36Sopenharmony_ciout_plogi_cachep:
730862306a36Sopenharmony_ci	kmem_cache_destroy(qla_tgt_plogi_cachep);
730962306a36Sopenharmony_ciout_mgmt_cmd_cachep:
731062306a36Sopenharmony_ci	kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
731162306a36Sopenharmony_ci	return ret;
731262306a36Sopenharmony_ci}
731362306a36Sopenharmony_ci
731462306a36Sopenharmony_civoid qlt_exit(void)
731562306a36Sopenharmony_ci{
731662306a36Sopenharmony_ci	if (!QLA_TGT_MODE_ENABLED())
731762306a36Sopenharmony_ci		return;
731862306a36Sopenharmony_ci
731962306a36Sopenharmony_ci	destroy_workqueue(qla_tgt_wq);
732062306a36Sopenharmony_ci	mempool_destroy(qla_tgt_mgmt_cmd_mempool);
732162306a36Sopenharmony_ci	kmem_cache_destroy(qla_tgt_plogi_cachep);
732262306a36Sopenharmony_ci	kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep);
732362306a36Sopenharmony_ci}
7324