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