提交 733db573 编写于 作者: L Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending

Pull SCSI target fixes from Nicholas Bellinger:
 "This series is larger than what I'd normally be conformable with
  sending for a -rc5 PULL request..

  However, the bulk of the series is localized to qla2xxx target
  specific fixes that address a number of real-world correctness issues,
  that have been outstanding on the list for ~6 weeks now.  They where
  submitted + verified + acked by the HW LLD vendor, contributed by a
  major production customer of the code, and are marked for v3.18.y
  stable code.

  That said, I don't see a good reason to wait another month to get
  these fixes into mainline.

  Beyond the qla2xx specific fixes, this series also includes:

   - bugfix for a long standing use-after-free in iscsi-target during
     TPG shutdown + demo-mode sessions.

   - bugfix for a >= v4.0 regression OOPs in iscsi-target during a
     iscsi_start_kthreads() failure.

   - bugfix for a >= v4.0 regression hang in iscsi-target for iser
     explicit session/connection logout.

   - bugfix for a iser-target bug where a early CMA REJECTED status
     during login triggers a NULL pointer dereference OOPs.

   - bugfixes for a handful of v4.2-rc1 specific regressions related to
     the larger set of recent backend configfs attribute changes.

  A big thanks to QLogic + Pure Storage for the qla2xxx target bugfixes"

* git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending: (28 commits)
  Documentation/target: Fix tcm_mod_builder.py build breakage
  iser-target: Fix REJECT CM event use-after-free OOPs
  iscsi-target: Fix iser explicit logout TX kthread leak
  iscsi-target: Fix iscsit_start_kthreads failure OOPs
  iscsi-target: Fix use-after-free during TPG session shutdown
  qla2xxx: terminate exchange when command is aborted by LIO
  qla2xxx: drop cmds/tmrs arrived while session is being deleted
  qla2xxx: disable scsi_transport_fc registration in target mode
  qla2xxx: added sess generations to detect RSCN update races
  qla2xxx: Abort stale cmds on qla_tgt_wq when plogi arrives
  qla2xxx: delay plogi/prli ack until existing sessions are deleted
  qla2xxx: cleanup cmd in qla workqueue before processing TMR
  qla2xxx: kill sessions/log out initiator on RSCN and port down events
  qla2xxx: fix command initialization in target mode.
  qla2xxx: Remove msleep in qlt_send_term_exchange
  qla2xxx: adjust debug flags
  qla2xxx: release request queue reservation.
  qla2xxx: Add flush after updating ATIOQ consumer index.
  qla2xxx: Enable target mode for ISP27XX
  qla2xxx: Fix hardware lock/unlock issue causing kernel panic.
  ...
...@@ -199,7 +199,8 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): ...@@ -199,7 +199,8 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name):
buf += "#include <linux/string.h>\n" buf += "#include <linux/string.h>\n"
buf += "#include <linux/configfs.h>\n" buf += "#include <linux/configfs.h>\n"
buf += "#include <linux/ctype.h>\n" buf += "#include <linux/ctype.h>\n"
buf += "#include <asm/unaligned.h>\n\n" buf += "#include <asm/unaligned.h>\n"
buf += "#include <scsi/scsi_proto.h>\n\n"
buf += "#include <target/target_core_base.h>\n" buf += "#include <target/target_core_base.h>\n"
buf += "#include <target/target_core_fabric.h>\n" buf += "#include <target/target_core_fabric.h>\n"
buf += "#include <target/target_core_fabric_configfs.h>\n" buf += "#include <target/target_core_fabric_configfs.h>\n"
...@@ -230,8 +231,14 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): ...@@ -230,8 +231,14 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name):
buf += " }\n" buf += " }\n"
buf += " tpg->" + fabric_mod_port + " = " + fabric_mod_port + ";\n" buf += " tpg->" + fabric_mod_port + " = " + fabric_mod_port + ";\n"
buf += " tpg->" + fabric_mod_port + "_tpgt = tpgt;\n\n" buf += " tpg->" + fabric_mod_port + "_tpgt = tpgt;\n\n"
buf += " ret = core_tpg_register(&" + fabric_mod_name + "_ops, wwn,\n"
buf += " &tpg->se_tpg, SCSI_PROTOCOL_SAS);\n" if proto_ident == "FC":
buf += " ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP);\n"
elif proto_ident == "SAS":
buf += " ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_SAS);\n"
elif proto_ident == "iSCSI":
buf += " ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_ISCSI);\n"
buf += " if (ret < 0) {\n" buf += " if (ret < 0) {\n"
buf += " kfree(tpg);\n" buf += " kfree(tpg);\n"
buf += " return NULL;\n" buf += " return NULL;\n"
...@@ -292,7 +299,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): ...@@ -292,7 +299,7 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name):
buf += "static const struct target_core_fabric_ops " + fabric_mod_name + "_ops = {\n" buf += "static const struct target_core_fabric_ops " + fabric_mod_name + "_ops = {\n"
buf += " .module = THIS_MODULE,\n" buf += " .module = THIS_MODULE,\n"
buf += " .name = " + fabric_mod_name + ",\n" buf += " .name = \"" + fabric_mod_name + "\",\n"
buf += " .get_fabric_name = " + fabric_mod_name + "_get_fabric_name,\n" buf += " .get_fabric_name = " + fabric_mod_name + "_get_fabric_name,\n"
buf += " .tpg_get_wwn = " + fabric_mod_name + "_get_fabric_wwn,\n" buf += " .tpg_get_wwn = " + fabric_mod_name + "_get_fabric_wwn,\n"
buf += " .tpg_get_tag = " + fabric_mod_name + "_get_tag,\n" buf += " .tpg_get_tag = " + fabric_mod_name + "_get_tag,\n"
...@@ -322,17 +329,17 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name): ...@@ -322,17 +329,17 @@ def tcm_mod_build_configfs(proto_ident, fabric_mod_dir_var, fabric_mod_name):
buf += " .fabric_make_tpg = " + fabric_mod_name + "_make_tpg,\n" buf += " .fabric_make_tpg = " + fabric_mod_name + "_make_tpg,\n"
buf += " .fabric_drop_tpg = " + fabric_mod_name + "_drop_tpg,\n" buf += " .fabric_drop_tpg = " + fabric_mod_name + "_drop_tpg,\n"
buf += "\n" buf += "\n"
buf += " .tfc_wwn_attrs = " + fabric_mod_name + "_wwn_attrs;\n" buf += " .tfc_wwn_attrs = " + fabric_mod_name + "_wwn_attrs,\n"
buf += "};\n\n" buf += "};\n\n"
buf += "static int __init " + fabric_mod_name + "_init(void)\n" buf += "static int __init " + fabric_mod_name + "_init(void)\n"
buf += "{\n" buf += "{\n"
buf += " return target_register_template(" + fabric_mod_name + "_ops);\n" buf += " return target_register_template(&" + fabric_mod_name + "_ops);\n"
buf += "};\n\n" buf += "};\n\n"
buf += "static void __exit " + fabric_mod_name + "_exit(void)\n" buf += "static void __exit " + fabric_mod_name + "_exit(void)\n"
buf += "{\n" buf += "{\n"
buf += " target_unregister_template(" + fabric_mod_name + "_ops);\n" buf += " target_unregister_template(&" + fabric_mod_name + "_ops);\n"
buf += "};\n\n" buf += "};\n\n"
buf += "MODULE_DESCRIPTION(\"" + fabric_mod_name.upper() + " series fabric driver\");\n" buf += "MODULE_DESCRIPTION(\"" + fabric_mod_name.upper() + " series fabric driver\");\n"
......
...@@ -775,6 +775,17 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) ...@@ -775,6 +775,17 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
ret = isert_rdma_post_recvl(isert_conn); ret = isert_rdma_post_recvl(isert_conn);
if (ret) if (ret)
goto out_conn_dev; goto out_conn_dev;
/*
* Obtain the second reference now before isert_rdma_accept() to
* ensure that any initiator generated REJECT CM event that occurs
* asynchronously won't drop the last reference until the error path
* in iscsi_target_login_sess_out() does it's ->iscsit_free_conn() ->
* isert_free_conn() -> isert_put_conn() -> kref_put().
*/
if (!kref_get_unless_zero(&isert_conn->kref)) {
isert_warn("conn %p connect_release is running\n", isert_conn);
goto out_conn_dev;
}
ret = isert_rdma_accept(isert_conn); ret = isert_rdma_accept(isert_conn);
if (ret) if (ret)
...@@ -836,11 +847,6 @@ isert_connected_handler(struct rdma_cm_id *cma_id) ...@@ -836,11 +847,6 @@ isert_connected_handler(struct rdma_cm_id *cma_id)
isert_info("conn %p\n", isert_conn); isert_info("conn %p\n", isert_conn);
if (!kref_get_unless_zero(&isert_conn->kref)) {
isert_warn("conn %p connect_release is running\n", isert_conn);
return;
}
mutex_lock(&isert_conn->mutex); mutex_lock(&isert_conn->mutex);
if (isert_conn->state != ISER_CONN_FULL_FEATURE) if (isert_conn->state != ISER_CONN_FULL_FEATURE)
isert_conn->state = ISER_CONN_UP; isert_conn->state = ISER_CONN_UP;
......
...@@ -738,7 +738,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, ...@@ -738,7 +738,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
ql_log(ql_log_info, vha, 0x706f, ql_log(ql_log_info, vha, 0x706f,
"Issuing MPI reset.\n"); "Issuing MPI reset.\n");
if (IS_QLA83XX(ha)) { if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) {
uint32_t idc_control; uint32_t idc_control;
qla83xx_idc_lock(vha, 0); qla83xx_idc_lock(vha, 0);
......
...@@ -67,10 +67,10 @@ ...@@ -67,10 +67,10 @@
* | | | 0xd031-0xd0ff | * | | | 0xd031-0xd0ff |
* | | | 0xd101-0xd1fe | * | | | 0xd101-0xd1fe |
* | | | 0xd214-0xd2fe | * | | | 0xd214-0xd2fe |
* | Target Mode | 0xe079 | | * | Target Mode | 0xe080 | |
* | Target Mode Management | 0xf072 | 0xf002 | * | Target Mode Management | 0xf096 | 0xf002 |
* | | | 0xf046-0xf049 | * | | | 0xf046-0xf049 |
* | Target Mode Task Management | 0x1000b | | * | Target Mode Task Management | 0x1000d | |
* ---------------------------------------------------------------------- * ----------------------------------------------------------------------
*/ */
......
...@@ -274,6 +274,7 @@ ...@@ -274,6 +274,7 @@
#define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/ #define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/
struct req_que; struct req_que;
struct qla_tgt_sess;
/* /*
* (sd.h is not exported, hence local inclusion) * (sd.h is not exported, hence local inclusion)
...@@ -2026,6 +2027,7 @@ typedef struct fc_port { ...@@ -2026,6 +2027,7 @@ typedef struct fc_port {
uint16_t port_id; uint16_t port_id;
unsigned long retry_delay_timestamp; unsigned long retry_delay_timestamp;
struct qla_tgt_sess *tgt_session;
} fc_port_t; } fc_port_t;
#include "qla_mr.h" #include "qla_mr.h"
...@@ -3154,13 +3156,13 @@ struct qla_hw_data { ...@@ -3154,13 +3156,13 @@ struct qla_hw_data {
/* Bit 21 of fw_attributes decides the MCTP capabilities */ /* Bit 21 of fw_attributes decides the MCTP capabilities */
#define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \ #define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \
((ha)->fw_attributes_ext[0] & BIT_0)) ((ha)->fw_attributes_ext[0] & BIT_0))
#define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha)) #define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha))
#define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha)) #define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha))
#define IS_PI_DIFB_DIX0_CAPABLE(ha) (0) #define IS_PI_DIFB_DIX0_CAPABLE(ha) (0)
#define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha)) #define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha))
#define IS_PI_SPLIT_DET_CAPABLE(ha) (IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \ #define IS_PI_SPLIT_DET_CAPABLE(ha) (IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \
(((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22)) (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22))
#define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha)) #define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha))
#define IS_TGT_MODE_CAPABLE(ha) (ha->tgt.atio_q_length) #define IS_TGT_MODE_CAPABLE(ha) (ha->tgt.atio_q_length)
#define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha)) #define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha))
#define IS_DPORT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_DPORT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha))
...@@ -3579,6 +3581,16 @@ typedef struct scsi_qla_host { ...@@ -3579,6 +3581,16 @@ typedef struct scsi_qla_host {
uint16_t fcoe_fcf_idx; uint16_t fcoe_fcf_idx;
uint8_t fcoe_vn_port_mac[6]; uint8_t fcoe_vn_port_mac[6];
/* list of commands waiting on workqueue */
struct list_head qla_cmd_list;
struct list_head qla_sess_op_cmd_list;
spinlock_t cmd_list_lock;
/* Counter to detect races between ELS and RSCN events */
atomic_t generation_tick;
/* Time when global fcport update has been scheduled */
int total_fcport_update_gen;
uint32_t vp_abort_cnt; uint32_t vp_abort_cnt;
struct fc_vport *fc_vport; /* holds fc_vport * for each vport */ struct fc_vport *fc_vport; /* holds fc_vport * for each vport */
......
...@@ -115,6 +115,8 @@ qla2x00_async_iocb_timeout(void *data) ...@@ -115,6 +115,8 @@ qla2x00_async_iocb_timeout(void *data)
QLA_LOGIO_LOGIN_RETRIED : 0; QLA_LOGIO_LOGIN_RETRIED : 0;
qla2x00_post_async_login_done_work(fcport->vha, fcport, qla2x00_post_async_login_done_work(fcport->vha, fcport,
lio->u.logio.data); lio->u.logio.data);
} else if (sp->type == SRB_LOGOUT_CMD) {
qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT);
} }
} }
...@@ -497,7 +499,10 @@ void ...@@ -497,7 +499,10 @@ void
qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport,
uint16_t *data) uint16_t *data)
{ {
/* Don't re-login in target mode */
if (!fcport->tgt_session)
qla2x00_mark_device_lost(vha, fcport, 1, 0); qla2x00_mark_device_lost(vha, fcport, 1, 0);
qlt_logo_completion_handler(fcport, data[0]);
return; return;
} }
...@@ -1538,7 +1543,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) ...@@ -1538,7 +1543,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
mem_size = (ha->fw_memory_size - 0x11000 + 1) * mem_size = (ha->fw_memory_size - 0x11000 + 1) *
sizeof(uint16_t); sizeof(uint16_t);
} else if (IS_FWI2_CAPABLE(ha)) { } else if (IS_FWI2_CAPABLE(ha)) {
if (IS_QLA83XX(ha)) if (IS_QLA83XX(ha) || IS_QLA27XX(ha))
fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem); fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem);
else if (IS_QLA81XX(ha)) else if (IS_QLA81XX(ha))
fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem); fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem);
...@@ -1550,7 +1555,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) ...@@ -1550,7 +1555,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
mem_size = (ha->fw_memory_size - 0x100000 + 1) * mem_size = (ha->fw_memory_size - 0x100000 + 1) *
sizeof(uint32_t); sizeof(uint32_t);
if (ha->mqenable) { if (ha->mqenable) {
if (!IS_QLA83XX(ha)) if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha))
mq_size = sizeof(struct qla2xxx_mq_chain); mq_size = sizeof(struct qla2xxx_mq_chain);
/* /*
* Allocate maximum buffer size for all queues. * Allocate maximum buffer size for all queues.
...@@ -2922,21 +2927,14 @@ qla2x00_rport_del(void *data) ...@@ -2922,21 +2927,14 @@ qla2x00_rport_del(void *data)
{ {
fc_port_t *fcport = data; fc_port_t *fcport = data;
struct fc_rport *rport; struct fc_rport *rport;
scsi_qla_host_t *vha = fcport->vha;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(fcport->vha->host->host_lock, flags); spin_lock_irqsave(fcport->vha->host->host_lock, flags);
rport = fcport->drport ? fcport->drport: fcport->rport; rport = fcport->drport ? fcport->drport: fcport->rport;
fcport->drport = NULL; fcport->drport = NULL;
spin_unlock_irqrestore(fcport->vha->host->host_lock, flags); spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
if (rport) { if (rport)
fc_remote_port_delete(rport); fc_remote_port_delete(rport);
/*
* Release the target mode FC NEXUS in qla_target.c code
* if target mod is enabled.
*/
qlt_fc_port_deleted(vha, fcport);
}
} }
/** /**
...@@ -3303,6 +3301,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -3303,6 +3301,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
* Create target mode FC NEXUS in qla_target.c if target mode is * Create target mode FC NEXUS in qla_target.c if target mode is
* enabled.. * enabled..
*/ */
qlt_fc_port_added(vha, fcport); qlt_fc_port_added(vha, fcport);
spin_lock_irqsave(fcport->vha->host->host_lock, flags); spin_lock_irqsave(fcport->vha->host->host_lock, flags);
...@@ -3341,8 +3340,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -3341,8 +3340,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
if (IS_QLAFX00(vha->hw)) { if (IS_QLAFX00(vha->hw)) {
qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_set_fcport_state(fcport, FCS_ONLINE);
qla2x00_reg_remote_port(vha, fcport); goto reg_port;
return;
} }
fcport->login_retry = 0; fcport->login_retry = 0;
fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
...@@ -3350,7 +3348,16 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) ...@@ -3350,7 +3348,16 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_set_fcport_state(fcport, FCS_ONLINE);
qla2x00_iidma_fcport(vha, fcport); qla2x00_iidma_fcport(vha, fcport);
qla24xx_update_fcport_fcp_prio(vha, fcport); qla24xx_update_fcport_fcp_prio(vha, fcport);
reg_port:
if (qla_ini_mode_enabled(vha))
qla2x00_reg_remote_port(vha, fcport); qla2x00_reg_remote_port(vha, fcport);
else {
/*
* Create target mode FC NEXUS in qla_target.c
*/
qlt_fc_port_added(vha, fcport);
}
} }
/* /*
...@@ -3375,6 +3382,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3375,6 +3382,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
LIST_HEAD(new_fcports); LIST_HEAD(new_fcports);
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
int discovery_gen;
/* If FL port exists, then SNS is present */ /* If FL port exists, then SNS is present */
if (IS_FWI2_CAPABLE(ha)) if (IS_FWI2_CAPABLE(ha))
...@@ -3445,6 +3453,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3445,6 +3453,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
fcport->scan_state = QLA_FCPORT_SCAN; fcport->scan_state = QLA_FCPORT_SCAN;
} }
/* Mark the time right before querying FW for connected ports.
* This process is long, asynchronous and by the time it's done,
* collected information might not be accurate anymore. E.g.
* disconnected port might have re-connected and a brand new
* session has been created. In this case session's generation
* will be newer than discovery_gen. */
qlt_do_generation_tick(vha, &discovery_gen);
rval = qla2x00_find_all_fabric_devs(vha, &new_fcports); rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
if (rval != QLA_SUCCESS) if (rval != QLA_SUCCESS)
break; break;
...@@ -3460,7 +3476,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3460,7 +3476,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
continue; continue;
if (fcport->scan_state == QLA_FCPORT_SCAN && if (fcport->scan_state == QLA_FCPORT_SCAN) {
if (qla_ini_mode_enabled(base_vha) &&
atomic_read(&fcport->state) == FCS_ONLINE) { atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(vha, fcport, qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0); ql2xplogiabsentdevice, 0);
...@@ -3475,6 +3492,29 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3475,6 +3492,29 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
fcport->d_id.b.al_pa); fcport->d_id.b.al_pa);
qla2x00_clear_loop_id(fcport); qla2x00_clear_loop_id(fcport);
} }
} else if (!qla_ini_mode_enabled(base_vha)) {
/*
* In target mode, explicitly kill
* sessions and log out of devices
* that are gone, so that we don't
* end up with an initiator using the
* wrong ACL (if the fabric recycles
* an FC address and we have a stale
* session around) and so that we don't
* report initiators that are no longer
* on the fabric.
*/
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf077,
"port gone, logging out/killing session: "
"%8phC state 0x%x flags 0x%x fc4_type 0x%x "
"scan_state %d\n",
fcport->port_name,
atomic_read(&fcport->state),
fcport->flags, fcport->fc4_type,
fcport->scan_state);
qlt_fc_port_deleted(vha, fcport,
discovery_gen);
}
} }
} }
...@@ -3494,6 +3534,28 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3494,6 +3534,28 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
(fcport->flags & FCF_LOGIN_NEEDED) == 0) (fcport->flags & FCF_LOGIN_NEEDED) == 0)
continue; continue;
/*
* If we're not an initiator, skip looking for devices
* and logging in. There's no reason for us to do it,
* and it seems to actively cause problems in target
* mode if we race with the initiator logging into us
* (we might get the "port ID used" status back from
* our login command and log out the initiator, which
* seems to cause havoc).
*/
if (!qla_ini_mode_enabled(base_vha)) {
if (fcport->scan_state == QLA_FCPORT_FOUND) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf078,
"port %8phC state 0x%x flags 0x%x fc4_type 0x%x "
"scan_state %d (initiator mode disabled; skipping "
"login)\n", fcport->port_name,
atomic_read(&fcport->state),
fcport->flags, fcport->fc4_type,
fcport->scan_state);
}
continue;
}
if (fcport->loop_id == FC_NO_LOOP_ID) { if (fcport->loop_id == FC_NO_LOOP_ID) {
fcport->loop_id = next_loopid; fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id( rval = qla2x00_find_new_loop_id(
...@@ -3520,16 +3582,38 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) ...@@ -3520,16 +3582,38 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break; break;
/*
* If we're not an initiator, skip looking for devices
* and logging in. There's no reason for us to do it,
* and it seems to actively cause problems in target
* mode if we race with the initiator logging into us
* (we might get the "port ID used" status back from
* our login command and log out the initiator, which
* seems to cause havoc).
*/
if (qla_ini_mode_enabled(base_vha)) {
/* Find a new loop ID to use. */ /* Find a new loop ID to use. */
fcport->loop_id = next_loopid; fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(base_vha, fcport); rval = qla2x00_find_new_loop_id(base_vha,
fcport);
if (rval != QLA_SUCCESS) { if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */ /* Ran out of IDs to use */
break; break;
} }
/* Login and update database */ /* Login and update database */
qla2x00_fabric_dev_login(vha, fcport, &next_loopid); qla2x00_fabric_dev_login(vha, fcport,
&next_loopid);
} else {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf079,
"new port %8phC state 0x%x flags 0x%x fc4_type "
"0x%x scan_state %d (initiator mode disabled; "
"skipping login)\n",
fcport->port_name,
atomic_read(&fcport->state),
fcport->flags, fcport->fc4_type,
fcport->scan_state);
}
list_move_tail(&fcport->list, &vha->vp_fcports); list_move_tail(&fcport->list, &vha->vp_fcports);
} }
...@@ -3725,11 +3809,12 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3725,11 +3809,12 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
fcport->fp_speed = new_fcport->fp_speed; fcport->fp_speed = new_fcport->fp_speed;
/* /*
* If address the same and state FCS_ONLINE, nothing * If address the same and state FCS_ONLINE
* changed. * (or in target mode), nothing changed.
*/ */
if (fcport->d_id.b24 == new_fcport->d_id.b24 && if (fcport->d_id.b24 == new_fcport->d_id.b24 &&
atomic_read(&fcport->state) == FCS_ONLINE) { (atomic_read(&fcport->state) == FCS_ONLINE ||
!qla_ini_mode_enabled(base_vha))) {
break; break;
} }
...@@ -3749,6 +3834,22 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3749,6 +3834,22 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
* Log it out if still logged in and mark it for * Log it out if still logged in and mark it for
* relogin later. * relogin later.
*/ */
if (!qla_ini_mode_enabled(base_vha)) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf080,
"port changed FC ID, %8phC"
" old %x:%x:%x (loop_id 0x%04x)-> new %x:%x:%x\n",
fcport->port_name,
fcport->d_id.b.domain,
fcport->d_id.b.area,
fcport->d_id.b.al_pa,
fcport->loop_id,
new_fcport->d_id.b.domain,
new_fcport->d_id.b.area,
new_fcport->d_id.b.al_pa);
fcport->d_id.b24 = new_fcport->d_id.b24;
break;
}
fcport->d_id.b24 = new_fcport->d_id.b24; fcport->d_id.b24 = new_fcport->d_id.b24;
fcport->flags |= FCF_LOGIN_NEEDED; fcport->flags |= FCF_LOGIN_NEEDED;
if (fcport->loop_id != FC_NO_LOOP_ID && if (fcport->loop_id != FC_NO_LOOP_ID &&
...@@ -3768,6 +3869,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ...@@ -3768,6 +3869,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
if (found) if (found)
continue; continue;
/* If device was not in our fcports list, then add it. */ /* If device was not in our fcports list, then add it. */
new_fcport->scan_state = QLA_FCPORT_FOUND;
list_add_tail(&new_fcport->list, new_fcports); list_add_tail(&new_fcport->list, new_fcports);
/* Allocate a new replacement fcport. */ /* Allocate a new replacement fcport. */
...@@ -4188,6 +4290,14 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) ...@@ -4188,6 +4290,14 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha)
atomic_read(&fcport->state) != FCS_UNCONFIGURED) { atomic_read(&fcport->state) != FCS_UNCONFIGURED) {
spin_unlock_irqrestore(&ha->vport_slock, flags); spin_unlock_irqrestore(&ha->vport_slock, flags);
qla2x00_rport_del(fcport); qla2x00_rport_del(fcport);
/*
* Release the target mode FC NEXUS in
* qla_target.c, if target mod is enabled.
*/
qlt_fc_port_deleted(vha, fcport,
base_vha->total_fcport_update_gen);
spin_lock_irqsave(&ha->vport_slock, flags); spin_lock_irqsave(&ha->vport_slock, flags);
} }
} }
......
...@@ -1943,6 +1943,9 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) ...@@ -1943,6 +1943,9 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
logio->control_flags = logio->control_flags =
cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
if (!sp->fcport->tgt_session ||
!sp->fcport->tgt_session->keep_nport_handle)
logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT);
logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[0] = sp->fcport->d_id.b.al_pa;
logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[1] = sp->fcport->d_id.b.area;
......
...@@ -2415,7 +2415,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, ...@@ -2415,7 +2415,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
*orig_iocb_cnt = mcp->mb[10]; *orig_iocb_cnt = mcp->mb[10];
if (vha->hw->flags.npiv_supported && max_npiv_vports) if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11]; *max_npiv_vports = mcp->mb[11];
if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) && max_fcfs) if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw) ||
IS_QLA27XX(vha->hw)) && max_fcfs)
*max_fcfs = mcp->mb[12]; *max_fcfs = mcp->mb[12];
} }
...@@ -3898,7 +3899,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) ...@@ -3898,7 +3899,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
if (!(rsp->options & BIT_0)) { if (!(rsp->options & BIT_0)) {
WRT_REG_DWORD(rsp->rsp_q_out, 0); WRT_REG_DWORD(rsp->rsp_q_out, 0);
if (!IS_QLA83XX(ha)) if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha))
WRT_REG_DWORD(rsp->rsp_q_in, 0); WRT_REG_DWORD(rsp->rsp_q_in, 0);
} }
...@@ -5345,7 +5346,7 @@ qla83xx_restart_nic_firmware(scsi_qla_host_t *vha) ...@@ -5345,7 +5346,7 @@ qla83xx_restart_nic_firmware(scsi_qla_host_t *vha)
mbx_cmd_t *mcp = &mc; mbx_cmd_t *mcp = &mc;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
if (!IS_QLA83XX(ha)) if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha))
return QLA_FUNCTION_FAILED; return QLA_FUNCTION_FAILED;
ql_dbg(ql_dbg_mbx, vha, 0x1143, "Entered %s.\n", __func__); ql_dbg(ql_dbg_mbx, vha, 0x1143, "Entered %s.\n", __func__);
......
...@@ -2504,6 +2504,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -2504,6 +2504,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->mbx_count = MAILBOX_REGISTER_COUNT; ha->mbx_count = MAILBOX_REGISTER_COUNT;
req_length = REQUEST_ENTRY_CNT_24XX; req_length = REQUEST_ENTRY_CNT_24XX;
rsp_length = RESPONSE_ENTRY_CNT_2300; rsp_length = RESPONSE_ENTRY_CNT_2300;
ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
ha->init_cb_size = sizeof(struct mid_init_cb_81xx); ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
ha->gid_list_info_size = 8; ha->gid_list_info_size = 8;
...@@ -3229,11 +3230,15 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, ...@@ -3229,11 +3230,15 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
spin_lock_irqsave(vha->host->host_lock, flags); spin_lock_irqsave(vha->host->host_lock, flags);
fcport->drport = rport; fcport->drport = rport;
spin_unlock_irqrestore(vha->host->host_lock, flags); spin_unlock_irqrestore(vha->host->host_lock, flags);
qlt_do_generation_tick(vha, &base_vha->total_fcport_update_gen);
set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
qla2xxx_wake_dpc(base_vha); qla2xxx_wake_dpc(base_vha);
} else { } else {
int now;
if (rport)
fc_remote_port_delete(rport); fc_remote_port_delete(rport);
qlt_fc_port_deleted(vha, fcport); qlt_do_generation_tick(vha, &now);
qlt_fc_port_deleted(vha, fcport, now);
} }
} }
...@@ -3763,8 +3768,11 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, ...@@ -3763,8 +3768,11 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
INIT_LIST_HEAD(&vha->vp_fcports); INIT_LIST_HEAD(&vha->vp_fcports);
INIT_LIST_HEAD(&vha->work_list); INIT_LIST_HEAD(&vha->work_list);
INIT_LIST_HEAD(&vha->list); INIT_LIST_HEAD(&vha->list);
INIT_LIST_HEAD(&vha->qla_cmd_list);
INIT_LIST_HEAD(&vha->qla_sess_op_cmd_list);
spin_lock_init(&vha->work_lock); spin_lock_init(&vha->work_lock);
spin_lock_init(&vha->cmd_list_lock);
sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no); sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
ql_dbg(ql_dbg_init, vha, 0x0041, ql_dbg(ql_dbg_init, vha, 0x0041,
......
...@@ -1697,7 +1697,7 @@ qla83xx_select_led_port(struct qla_hw_data *ha) ...@@ -1697,7 +1697,7 @@ qla83xx_select_led_port(struct qla_hw_data *ha)
{ {
uint32_t led_select_value = 0; uint32_t led_select_value = 0;
if (!IS_QLA83XX(ha)) if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha))
goto out; goto out;
if (ha->port_no == 0) if (ha->port_no == 0)
......
...@@ -113,6 +113,11 @@ static void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, ...@@ -113,6 +113,11 @@ static void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha,
static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha,
struct atio_from_isp *atio, uint16_t status, int qfull); struct atio_from_isp *atio, uint16_t status, int qfull);
static void qlt_disable_vha(struct scsi_qla_host *vha); static void qlt_disable_vha(struct scsi_qla_host *vha);
static void qlt_clear_tgt_db(struct qla_tgt *tgt);
static void qlt_send_notify_ack(struct scsi_qla_host *vha,
struct imm_ntfy_from_isp *ntfy,
uint32_t add_flags, uint16_t resp_code, int resp_code_valid,
uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan);
/* /*
* Global Variables * Global Variables
*/ */
...@@ -122,6 +127,16 @@ static struct workqueue_struct *qla_tgt_wq; ...@@ -122,6 +127,16 @@ static struct workqueue_struct *qla_tgt_wq;
static DEFINE_MUTEX(qla_tgt_mutex); static DEFINE_MUTEX(qla_tgt_mutex);
static LIST_HEAD(qla_tgt_glist); static LIST_HEAD(qla_tgt_glist);
/* This API intentionally takes dest as a parameter, rather than returning
* int value to avoid caller forgetting to issue wmb() after the store */
void qlt_do_generation_tick(struct scsi_qla_host *vha, int *dest)
{
scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev);
*dest = atomic_inc_return(&base_vha->generation_tick);
/* memory barrier */
wmb();
}
/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ /* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
static struct qla_tgt_sess *qlt_find_sess_by_port_name( static struct qla_tgt_sess *qlt_find_sess_by_port_name(
struct qla_tgt *tgt, struct qla_tgt *tgt,
...@@ -381,14 +396,73 @@ static void qlt_free_session_done(struct work_struct *work) ...@@ -381,14 +396,73 @@ static void qlt_free_session_done(struct work_struct *work)
struct qla_tgt *tgt = sess->tgt; struct qla_tgt *tgt = sess->tgt;
struct scsi_qla_host *vha = sess->vha; struct scsi_qla_host *vha = sess->vha;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
unsigned long flags;
bool logout_started = false;
fc_port_t fcport;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084,
"%s: se_sess %p / sess %p from port %8phC loop_id %#04x"
" s_id %02x:%02x:%02x logout %d keep %d plogi %d\n",
__func__, sess->se_sess, sess, sess->port_name, sess->loop_id,
sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
sess->logout_on_delete, sess->keep_nport_handle,
sess->plogi_ack_needed);
BUG_ON(!tgt); BUG_ON(!tgt);
if (sess->logout_on_delete) {
int rc;
memset(&fcport, 0, sizeof(fcport));
fcport.loop_id = sess->loop_id;
fcport.d_id = sess->s_id;
memcpy(fcport.port_name, sess->port_name, WWN_SIZE);
fcport.vha = vha;
fcport.tgt_session = sess;
rc = qla2x00_post_async_logout_work(vha, &fcport, NULL);
if (rc != QLA_SUCCESS)
ql_log(ql_log_warn, vha, 0xf085,
"Schedule logo failed sess %p rc %d\n",
sess, rc);
else
logout_started = true;
}
/* /*
* Release the target session for FC Nexus from fabric module code. * Release the target session for FC Nexus from fabric module code.
*/ */
if (sess->se_sess != NULL) if (sess->se_sess != NULL)
ha->tgt.tgt_ops->free_session(sess); ha->tgt.tgt_ops->free_session(sess);
if (logout_started) {
bool traced = false;
while (!ACCESS_ONCE(sess->logout_completed)) {
if (!traced) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf086,
"%s: waiting for sess %p logout\n",
__func__, sess);
traced = true;
}
msleep(100);
}
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf087,
"%s: sess %p logout completed\n",
__func__, sess);
}
spin_lock_irqsave(&ha->hardware_lock, flags);
if (sess->plogi_ack_needed)
qlt_send_notify_ack(vha, &sess->tm_iocb,
0, 0, 0, 0, 0, 0);
list_del(&sess->sess_list_entry);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001,
"Unregistration of sess %p finished\n", sess); "Unregistration of sess %p finished\n", sess);
...@@ -409,9 +483,9 @@ void qlt_unreg_sess(struct qla_tgt_sess *sess) ...@@ -409,9 +483,9 @@ void qlt_unreg_sess(struct qla_tgt_sess *sess)
vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess);
list_del(&sess->sess_list_entry); if (!list_empty(&sess->del_list_entry))
if (sess->deleted) list_del_init(&sess->del_list_entry);
list_del(&sess->del_list_entry); sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
INIT_WORK(&sess->free_work, qlt_free_session_done); INIT_WORK(&sess->free_work, qlt_free_session_done);
schedule_work(&sess->free_work); schedule_work(&sess->free_work);
...@@ -431,10 +505,10 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) ...@@ -431,10 +505,10 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
loop_id = le16_to_cpu(n->u.isp24.nport_handle); loop_id = le16_to_cpu(n->u.isp24.nport_handle);
if (loop_id == 0xFFFF) { if (loop_id == 0xFFFF) {
#if 0 /* FIXME: Re-enable Global event handling.. */
/* Global event */ /* Global event */
atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count); atomic_inc(&vha->vha_tgt.qla_tgt->tgt_global_resets_count);
qlt_clear_tgt_db(ha->tgt.qla_tgt); qlt_clear_tgt_db(vha->vha_tgt.qla_tgt);
#if 0 /* FIXME: do we need to choose a session here? */
if (!list_empty(&ha->tgt.qla_tgt->sess_list)) { if (!list_empty(&ha->tgt.qla_tgt->sess_list)) {
sess = list_entry(ha->tgt.qla_tgt->sess_list.next, sess = list_entry(ha->tgt.qla_tgt->sess_list.next,
typeof(*sess), sess_list_entry); typeof(*sess), sess_list_entry);
...@@ -489,27 +563,38 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, ...@@ -489,27 +563,38 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess,
struct qla_tgt *tgt = sess->tgt; struct qla_tgt *tgt = sess->tgt;
uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5; uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5;
if (sess->deleted) if (sess->deleted) {
/* Upgrade to unconditional deletion in case it was temporary */
if (immediate && sess->deleted == QLA_SESS_DELETION_PENDING)
list_del(&sess->del_list_entry);
else
return; return;
}
ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, ql_dbg(ql_dbg_tgt, sess->vha, 0xe001,
"Scheduling sess %p for deletion\n", sess); "Scheduling sess %p for deletion\n", sess);
list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
sess->deleted = 1;
if (immediate) if (immediate) {
dev_loss_tmo = 0; dev_loss_tmo = 0;
sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
list_add(&sess->del_list_entry, &tgt->del_sess_list);
} else {
sess->deleted = QLA_SESS_DELETION_PENDING;
list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
}
sess->expires = jiffies + dev_loss_tmo * HZ; sess->expires = jiffies + dev_loss_tmo * HZ;
ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, ql_dbg(ql_dbg_tgt, sess->vha, 0xe048,
"qla_target(%d): session for port %8phC (loop ID %d) scheduled for " "qla_target(%d): session for port %8phC (loop ID %d s_id %02x:%02x:%02x)"
"deletion in %u secs (expires: %lu) immed: %d\n", " scheduled for deletion in %u secs (expires: %lu) immed: %d, logout: %d, gen: %#x\n",
sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo, sess->vha->vp_idx, sess->port_name, sess->loop_id,
sess->expires, immediate); sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
dev_loss_tmo, sess->expires, immediate, sess->logout_on_delete,
sess->generation);
if (immediate) if (immediate)
schedule_delayed_work(&tgt->sess_del_work, 0); mod_delayed_work(system_wq, &tgt->sess_del_work, 0);
else else
schedule_delayed_work(&tgt->sess_del_work, schedule_delayed_work(&tgt->sess_del_work,
sess->expires - jiffies); sess->expires - jiffies);
...@@ -578,9 +663,9 @@ static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id, ...@@ -578,9 +663,9 @@ static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id,
/* ha->hardware_lock supposed to be held on entry */ /* ha->hardware_lock supposed to be held on entry */
static void qlt_undelete_sess(struct qla_tgt_sess *sess) static void qlt_undelete_sess(struct qla_tgt_sess *sess)
{ {
BUG_ON(!sess->deleted); BUG_ON(sess->deleted != QLA_SESS_DELETION_PENDING);
list_del(&sess->del_list_entry); list_del_init(&sess->del_list_entry);
sess->deleted = 0; sess->deleted = 0;
} }
...@@ -599,7 +684,9 @@ static void qlt_del_sess_work_fn(struct delayed_work *work) ...@@ -599,7 +684,9 @@ static void qlt_del_sess_work_fn(struct delayed_work *work)
del_list_entry); del_list_entry);
elapsed = jiffies; elapsed = jiffies;
if (time_after_eq(elapsed, sess->expires)) { if (time_after_eq(elapsed, sess->expires)) {
qlt_undelete_sess(sess); /* No turning back */
list_del_init(&sess->del_list_entry);
sess->deleted = QLA_SESS_DELETION_IN_PROGRESS;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004,
"Timeout: sess %p about to be deleted\n", "Timeout: sess %p about to be deleted\n",
...@@ -643,6 +730,13 @@ static struct qla_tgt_sess *qlt_create_sess( ...@@ -643,6 +730,13 @@ static struct qla_tgt_sess *qlt_create_sess(
fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->d_id.b.al_pa, fcport->d_id.b.area,
fcport->loop_id); fcport->loop_id);
/* Cannot undelete at this point */
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
spin_unlock_irqrestore(&ha->hardware_lock,
flags);
return NULL;
}
if (sess->deleted) if (sess->deleted)
qlt_undelete_sess(sess); qlt_undelete_sess(sess);
...@@ -652,6 +746,9 @@ static struct qla_tgt_sess *qlt_create_sess( ...@@ -652,6 +746,9 @@ static struct qla_tgt_sess *qlt_create_sess(
if (sess->local && !local) if (sess->local && !local)
sess->local = 0; sess->local = 0;
qlt_do_generation_tick(vha, &sess->generation);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
return sess; return sess;
...@@ -673,6 +770,14 @@ static struct qla_tgt_sess *qlt_create_sess( ...@@ -673,6 +770,14 @@ static struct qla_tgt_sess *qlt_create_sess(
sess->s_id = fcport->d_id; sess->s_id = fcport->d_id;
sess->loop_id = fcport->loop_id; sess->loop_id = fcport->loop_id;
sess->local = local; sess->local = local;
INIT_LIST_HEAD(&sess->del_list_entry);
/* Under normal circumstances we want to logout from firmware when
* session eventually ends and release corresponding nport handle.
* In the exception cases (e.g. when new PLOGI is waiting) corresponding
* code will adjust these flags as necessary. */
sess->logout_on_delete = 1;
sess->keep_nport_handle = 0;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006,
"Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n",
...@@ -705,6 +810,7 @@ static struct qla_tgt_sess *qlt_create_sess( ...@@ -705,6 +810,7 @@ static struct qla_tgt_sess *qlt_create_sess(
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
list_add_tail(&sess->sess_list_entry, &vha->vha_tgt.qla_tgt->sess_list); list_add_tail(&sess->sess_list_entry, &vha->vha_tgt.qla_tgt->sess_list);
vha->vha_tgt.qla_tgt->sess_count++; vha->vha_tgt.qla_tgt->sess_count++;
qlt_do_generation_tick(vha, &sess->generation);
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b,
...@@ -718,7 +824,7 @@ static struct qla_tgt_sess *qlt_create_sess( ...@@ -718,7 +824,7 @@ static struct qla_tgt_sess *qlt_create_sess(
} }
/* /*
* Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port() * Called from qla2x00_reg_remote_port()
*/ */
void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
{ {
...@@ -750,6 +856,10 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) ...@@ -750,6 +856,10 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
mutex_unlock(&vha->vha_tgt.tgt_mutex); mutex_unlock(&vha->vha_tgt.tgt_mutex);
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
} else if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
/* Point of no return */
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return;
} else { } else {
kref_get(&sess->se_sess->sess_kref); kref_get(&sess->se_sess->sess_kref);
...@@ -780,27 +890,36 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) ...@@ -780,27 +890,36 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport)
spin_unlock_irqrestore(&ha->hardware_lock, flags); spin_unlock_irqrestore(&ha->hardware_lock, flags);
} }
void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport) /*
* max_gen - specifies maximum session generation
* at which this deletion requestion is still valid
*/
void
qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen)
{ {
struct qla_hw_data *ha = vha->hw;
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
struct qla_tgt_sess *sess; struct qla_tgt_sess *sess;
unsigned long flags;
if (!vha->hw->tgt.tgt_ops) if (!vha->hw->tgt.tgt_ops)
return; return;
if (!tgt || (fcport->port_type != FCT_INITIATOR)) if (!tgt)
return; return;
spin_lock_irqsave(&ha->hardware_lock, flags);
if (tgt->tgt_stop) { if (tgt->tgt_stop) {
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return; return;
} }
sess = qlt_find_sess_by_port_name(tgt, fcport->port_name); sess = qlt_find_sess_by_port_name(tgt, fcport->port_name);
if (!sess) { if (!sess) {
spin_unlock_irqrestore(&ha->hardware_lock, flags); return;
}
if (max_gen - sess->generation < 0) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf092,
"Ignoring stale deletion request for se_sess %p / sess %p"
" for port %8phC, req_gen %d, sess_gen %d\n",
sess->se_sess, sess, sess->port_name, max_gen,
sess->generation);
return; return;
} }
...@@ -808,7 +927,6 @@ void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport) ...@@ -808,7 +927,6 @@ void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
sess->local = 1; sess->local = 1;
qlt_schedule_sess_for_deletion(sess, false); qlt_schedule_sess_for_deletion(sess, false);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
} }
static inline int test_tgt_sess_count(struct qla_tgt *tgt) static inline int test_tgt_sess_count(struct qla_tgt *tgt)
...@@ -1175,6 +1293,70 @@ static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha, ...@@ -1175,6 +1293,70 @@ static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha,
FCP_TMF_CMPL, true); FCP_TMF_CMPL, true);
} }
static int abort_cmd_for_tag(struct scsi_qla_host *vha, uint32_t tag)
{
struct qla_tgt_sess_op *op;
struct qla_tgt_cmd *cmd;
spin_lock(&vha->cmd_list_lock);
list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
if (tag == op->atio.u.isp24.exchange_addr) {
op->aborted = true;
spin_unlock(&vha->cmd_list_lock);
return 1;
}
}
list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
if (tag == cmd->atio.u.isp24.exchange_addr) {
cmd->state = QLA_TGT_STATE_ABORTED;
spin_unlock(&vha->cmd_list_lock);
return 1;
}
}
spin_unlock(&vha->cmd_list_lock);
return 0;
}
/* drop cmds for the given lun
* XXX only looks for cmds on the port through which lun reset was recieved
* XXX does not go through the list of other port (which may have cmds
* for the same lun)
*/
static void abort_cmds_for_lun(struct scsi_qla_host *vha,
uint32_t lun, uint8_t *s_id)
{
struct qla_tgt_sess_op *op;
struct qla_tgt_cmd *cmd;
uint32_t key;
key = sid_to_key(s_id);
spin_lock(&vha->cmd_list_lock);
list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
uint32_t op_key;
uint32_t op_lun;
op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
op_lun = scsilun_to_int(
(struct scsi_lun *)&op->atio.u.isp24.fcp_cmnd.lun);
if (op_key == key && op_lun == lun)
op->aborted = true;
}
list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
uint32_t cmd_key;
uint32_t cmd_lun;
cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id);
cmd_lun = scsilun_to_int(
(struct scsi_lun *)&cmd->atio.u.isp24.fcp_cmnd.lun);
if (cmd_key == key && cmd_lun == lun)
cmd->state = QLA_TGT_STATE_ABORTED;
}
spin_unlock(&vha->cmd_list_lock);
}
/* ha->hardware_lock supposed to be held on entry */ /* ha->hardware_lock supposed to be held on entry */
static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess) struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess)
...@@ -1199,8 +1381,19 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, ...@@ -1199,8 +1381,19 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha,
} }
spin_unlock(&se_sess->sess_cmd_lock); spin_unlock(&se_sess->sess_cmd_lock);
if (!found_lun) /* cmd not in LIO lists, look in qla list */
if (!found_lun) {
if (abort_cmd_for_tag(vha, abts->exchange_addr_to_abort)) {
/* send TASK_ABORT response immediately */
qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_CMPL, false);
return 0;
} else {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf081,
"unable to find cmd in driver or LIO for tag 0x%x\n",
abts->exchange_addr_to_abort);
return -ENOENT; return -ENOENT;
}
}
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f,
"qla_target(%d): task abort (tag=%d)\n", "qla_target(%d): task abort (tag=%d)\n",
...@@ -1284,6 +1477,11 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, ...@@ -1284,6 +1477,11 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha,
return; return;
} }
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false);
return;
}
rc = __qlt_24xx_handle_abts(vha, abts, sess); rc = __qlt_24xx_handle_abts(vha, abts, sess);
if (rc != 0) { if (rc != 0) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054,
...@@ -1726,20 +1924,6 @@ static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd, ...@@ -1726,20 +1924,6 @@ static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd,
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct se_cmd *se_cmd = &cmd->se_cmd; struct se_cmd *se_cmd = &cmd->se_cmd;
if (unlikely(cmd->aborted)) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014,
"qla_target(%d): terminating exchange for aborted cmd=%p (se_cmd=%p, tag=%lld)",
vha->vp_idx, cmd, se_cmd, se_cmd->tag);
cmd->state = QLA_TGT_STATE_ABORTED;
cmd->cmd_flags |= BIT_6;
qlt_send_term_exchange(vha, cmd, &cmd->atio, 0);
/* !! At this point cmd could be already freed !! */
return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED;
}
prm->cmd = cmd; prm->cmd = cmd;
prm->tgt = tgt; prm->tgt = tgt;
prm->rq_result = scsi_status; prm->rq_result = scsi_status;
...@@ -2301,6 +2485,19 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, ...@@ -2301,6 +2485,19 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
unsigned long flags = 0; unsigned long flags = 0;
int res; int res;
spin_lock_irqsave(&ha->hardware_lock, flags);
if (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
cmd->state = QLA_TGT_STATE_PROCESSED;
if (cmd->sess->logout_completed)
/* no need to terminate. FW already freed exchange. */
qlt_abort_cmd_on_host_reset(cmd->vha, cmd);
else
qlt_send_term_exchange(vha, cmd, &cmd->atio, 1);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return 0;
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
memset(&prm, 0, sizeof(prm)); memset(&prm, 0, sizeof(prm));
qlt_check_srr_debug(cmd, &xmit_type); qlt_check_srr_debug(cmd, &xmit_type);
...@@ -2313,9 +2510,6 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, ...@@ -2313,9 +2510,6 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status,
&full_req_cnt); &full_req_cnt);
if (unlikely(res != 0)) { if (unlikely(res != 0)) {
if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED)
return 0;
return res; return res;
} }
...@@ -2345,9 +2539,10 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, ...@@ -2345,9 +2539,10 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
res = qlt_build_ctio_crc2_pkt(&prm, vha); res = qlt_build_ctio_crc2_pkt(&prm, vha);
else else
res = qlt_24xx_build_ctio_pkt(&prm, vha); res = qlt_24xx_build_ctio_pkt(&prm, vha);
if (unlikely(res != 0)) if (unlikely(res != 0)) {
vha->req->cnt += full_req_cnt;
goto out_unmap_unlock; goto out_unmap_unlock;
}
pkt = (struct ctio7_to_24xx *)prm.pkt; pkt = (struct ctio7_to_24xx *)prm.pkt;
...@@ -2461,7 +2656,8 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) ...@@ -2461,7 +2656,8 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
spin_lock_irqsave(&ha->hardware_lock, flags); spin_lock_irqsave(&ha->hardware_lock, flags);
if (qla2x00_reset_active(vha) || cmd->reset_count != ha->chip_reset) { if (qla2x00_reset_active(vha) || (cmd->reset_count != ha->chip_reset) ||
(cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS)) {
/* /*
* Either a chip reset is active or this request was from * Either a chip reset is active or this request was from
* previous life, just abort the processing. * previous life, just abort the processing.
...@@ -2485,8 +2681,11 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) ...@@ -2485,8 +2681,11 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
else else
res = qlt_24xx_build_ctio_pkt(&prm, vha); res = qlt_24xx_build_ctio_pkt(&prm, vha);
if (unlikely(res != 0)) if (unlikely(res != 0)) {
vha->req->cnt += prm.req_cnt;
goto out_unlock_free_unmap; goto out_unlock_free_unmap;
}
pkt = (struct ctio7_to_24xx *)prm.pkt; pkt = (struct ctio7_to_24xx *)prm.pkt;
pkt->u.status0.flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT | pkt->u.status0.flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT |
CTIO7_FLAGS_STATUS_MODE_0); CTIO7_FLAGS_STATUS_MODE_0);
...@@ -2649,6 +2848,89 @@ qlt_handle_dif_error(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd, ...@@ -2649,6 +2848,89 @@ qlt_handle_dif_error(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd,
} }
/* If hardware_lock held on entry, might drop it, then reaquire */
/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
static int __qlt_send_term_imm_notif(struct scsi_qla_host *vha,
struct imm_ntfy_from_isp *ntfy)
{
struct nack_to_isp *nack;
struct qla_hw_data *ha = vha->hw;
request_t *pkt;
int ret = 0;
ql_dbg(ql_dbg_tgt_tmr, vha, 0xe01c,
"Sending TERM ELS CTIO (ha=%p)\n", ha);
pkt = (request_t *)qla2x00_alloc_iocbs_ready(vha, NULL);
if (pkt == NULL) {
ql_dbg(ql_dbg_tgt, vha, 0xe080,
"qla_target(%d): %s failed: unable to allocate "
"request packet\n", vha->vp_idx, __func__);
return -ENOMEM;
}
pkt->entry_type = NOTIFY_ACK_TYPE;
pkt->entry_count = 1;
pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK;
nack = (struct nack_to_isp *)pkt;
nack->ox_id = ntfy->ox_id;
nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle;
if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) {
nack->u.isp24.flags = ntfy->u.isp24.flags &
__constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB);
}
/* terminate */
nack->u.isp24.flags |=
__constant_cpu_to_le16(NOTIFY_ACK_FLAGS_TERMINATE);
nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id;
nack->u.isp24.status = ntfy->u.isp24.status;
nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode;
nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle;
nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address;
nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs;
nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui;
nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
qla2x00_start_iocbs(vha, vha->req);
return ret;
}
static void qlt_send_term_imm_notif(struct scsi_qla_host *vha,
struct imm_ntfy_from_isp *imm, int ha_locked)
{
unsigned long flags = 0;
int rc;
if (qlt_issue_marker(vha, ha_locked) < 0)
return;
if (ha_locked) {
rc = __qlt_send_term_imm_notif(vha, imm);
#if 0 /* Todo */
if (rc == -ENOMEM)
qlt_alloc_qfull_cmd(vha, imm, 0, 0);
#endif
goto done;
}
spin_lock_irqsave(&vha->hw->hardware_lock, flags);
rc = __qlt_send_term_imm_notif(vha, imm);
#if 0 /* Todo */
if (rc == -ENOMEM)
qlt_alloc_qfull_cmd(vha, imm, 0, 0);
#endif
done:
if (!ha_locked)
spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
}
/* If hardware_lock held on entry, might drop it, then reaquire */ /* If hardware_lock held on entry, might drop it, then reaquire */
/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */ /* This function sends the appropriate CTIO to ISP 2xxx or 24xx */
static int __qlt_send_term_exchange(struct scsi_qla_host *vha, static int __qlt_send_term_exchange(struct scsi_qla_host *vha,
...@@ -2715,7 +2997,7 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, ...@@ -2715,7 +2997,7 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha,
static void qlt_send_term_exchange(struct scsi_qla_host *vha, static void qlt_send_term_exchange(struct scsi_qla_host *vha,
struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked) struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked)
{ {
unsigned long flags; unsigned long flags = 0;
int rc; int rc;
if (qlt_issue_marker(vha, ha_locked) < 0) if (qlt_issue_marker(vha, ha_locked) < 0)
...@@ -2731,17 +3013,18 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha, ...@@ -2731,17 +3013,18 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha,
rc = __qlt_send_term_exchange(vha, cmd, atio); rc = __qlt_send_term_exchange(vha, cmd, atio);
if (rc == -ENOMEM) if (rc == -ENOMEM)
qlt_alloc_qfull_cmd(vha, atio, 0, 0); qlt_alloc_qfull_cmd(vha, atio, 0, 0);
spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
done: done:
if (cmd && ((cmd->state != QLA_TGT_STATE_ABORTED) || if (cmd && ((cmd->state != QLA_TGT_STATE_ABORTED) ||
!cmd->cmd_sent_to_fw)) { !cmd->cmd_sent_to_fw)) {
if (!ha_locked && !in_interrupt()) if (cmd->sg_mapped)
msleep(250); /* just in case */
qlt_unmap_sg(vha, cmd); qlt_unmap_sg(vha, cmd);
vha->hw->tgt.tgt_ops->free_cmd(cmd); vha->hw->tgt.tgt_ops->free_cmd(cmd);
} }
if (!ha_locked)
spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
return; return;
} }
...@@ -2792,6 +3075,24 @@ static void qlt_chk_exch_leak_thresh_hold(struct scsi_qla_host *vha) ...@@ -2792,6 +3075,24 @@ static void qlt_chk_exch_leak_thresh_hold(struct scsi_qla_host *vha)
} }
void qlt_abort_cmd(struct qla_tgt_cmd *cmd)
{
struct qla_tgt *tgt = cmd->tgt;
struct scsi_qla_host *vha = tgt->vha;
struct se_cmd *se_cmd = &cmd->se_cmd;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014,
"qla_target(%d): terminating exchange for aborted cmd=%p "
"(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, &cmd->se_cmd,
se_cmd->tag);
cmd->state = QLA_TGT_STATE_ABORTED;
cmd->cmd_flags |= BIT_6;
qlt_send_term_exchange(vha, cmd, &cmd->atio, 0);
}
EXPORT_SYMBOL(qlt_abort_cmd);
void qlt_free_cmd(struct qla_tgt_cmd *cmd) void qlt_free_cmd(struct qla_tgt_cmd *cmd)
{ {
struct qla_tgt_sess *sess = cmd->sess; struct qla_tgt_sess *sess = cmd->sess;
...@@ -3015,7 +3316,7 @@ qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) ...@@ -3015,7 +3316,7 @@ qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd)
dump_stack(); dump_stack();
} }
cmd->cmd_flags |= BIT_12; cmd->cmd_flags |= BIT_17;
ha->tgt.tgt_ops->free_cmd(cmd); ha->tgt.tgt_ops->free_cmd(cmd);
} }
...@@ -3177,7 +3478,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, ...@@ -3177,7 +3478,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
skip_term: skip_term:
if (cmd->state == QLA_TGT_STATE_PROCESSED) { if (cmd->state == QLA_TGT_STATE_PROCESSED) {
; cmd->cmd_flags |= BIT_12;
} else if (cmd->state == QLA_TGT_STATE_NEED_DATA) { } else if (cmd->state == QLA_TGT_STATE_NEED_DATA) {
int rx_status = 0; int rx_status = 0;
...@@ -3191,9 +3492,11 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, ...@@ -3191,9 +3492,11 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
ha->tgt.tgt_ops->handle_data(cmd); ha->tgt.tgt_ops->handle_data(cmd);
return; return;
} else if (cmd->state == QLA_TGT_STATE_ABORTED) { } else if (cmd->state == QLA_TGT_STATE_ABORTED) {
cmd->cmd_flags |= BIT_18;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e,
"Aborted command %p (tag %lld) finished\n", cmd, se_cmd->tag); "Aborted command %p (tag %lld) finished\n", cmd, se_cmd->tag);
} else { } else {
cmd->cmd_flags |= BIT_19;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c,
"qla_target(%d): A command in state (%d) should " "qla_target(%d): A command in state (%d) should "
"not return a CTIO complete\n", vha->vp_idx, cmd->state); "not return a CTIO complete\n", vha->vp_idx, cmd->state);
...@@ -3205,7 +3508,6 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, ...@@ -3205,7 +3508,6 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
dump_stack(); dump_stack();
} }
ha->tgt.tgt_ops->free_cmd(cmd); ha->tgt.tgt_ops->free_cmd(cmd);
} }
...@@ -3263,6 +3565,13 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) ...@@ -3263,6 +3565,13 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd)
if (tgt->tgt_stop) if (tgt->tgt_stop)
goto out_term; goto out_term;
if (cmd->state == QLA_TGT_STATE_ABORTED) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf082,
"cmd with tag %u is aborted\n",
cmd->atio.u.isp24.exchange_addr);
goto out_term;
}
cdb = &atio->u.isp24.fcp_cmnd.cdb[0]; cdb = &atio->u.isp24.fcp_cmnd.cdb[0];
cmd->se_cmd.tag = atio->u.isp24.exchange_addr; cmd->se_cmd.tag = atio->u.isp24.exchange_addr;
cmd->unpacked_lun = scsilun_to_int( cmd->unpacked_lun = scsilun_to_int(
...@@ -3316,6 +3625,12 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) ...@@ -3316,6 +3625,12 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd)
static void qlt_do_work(struct work_struct *work) static void qlt_do_work(struct work_struct *work)
{ {
struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
scsi_qla_host_t *vha = cmd->vha;
unsigned long flags;
spin_lock_irqsave(&vha->cmd_list_lock, flags);
list_del(&cmd->cmd_list);
spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
__qlt_do_work(cmd); __qlt_do_work(cmd);
} }
...@@ -3345,6 +3660,11 @@ static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, ...@@ -3345,6 +3660,11 @@ static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha,
cmd->loop_id = sess->loop_id; cmd->loop_id = sess->loop_id;
cmd->conf_compl_supported = sess->conf_compl_supported; cmd->conf_compl_supported = sess->conf_compl_supported;
cmd->cmd_flags = 0;
cmd->jiffies_at_alloc = get_jiffies_64();
cmd->reset_count = vha->hw->chip_reset;
return cmd; return cmd;
} }
...@@ -3362,6 +3682,17 @@ static void qlt_create_sess_from_atio(struct work_struct *work) ...@@ -3362,6 +3682,17 @@ static void qlt_create_sess_from_atio(struct work_struct *work)
unsigned long flags; unsigned long flags;
uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id; uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id;
spin_lock_irqsave(&vha->cmd_list_lock, flags);
list_del(&op->cmd_list);
spin_unlock_irqrestore(&vha->cmd_list_lock, flags);
if (op->aborted) {
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf083,
"sess_op with tag %u is aborted\n",
op->atio.u.isp24.exchange_addr);
goto out_term;
}
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022,
"qla_target(%d): Unable to find wwn login" "qla_target(%d): Unable to find wwn login"
" (s_id %x:%x:%x), trying to create it manually\n", " (s_id %x:%x:%x), trying to create it manually\n",
...@@ -3434,10 +3765,25 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, ...@@ -3434,10 +3765,25 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
memcpy(&op->atio, atio, sizeof(*atio)); memcpy(&op->atio, atio, sizeof(*atio));
op->vha = vha; op->vha = vha;
spin_lock(&vha->cmd_list_lock);
list_add_tail(&op->cmd_list, &vha->qla_sess_op_cmd_list);
spin_unlock(&vha->cmd_list_lock);
INIT_WORK(&op->work, qlt_create_sess_from_atio); INIT_WORK(&op->work, qlt_create_sess_from_atio);
queue_work(qla_tgt_wq, &op->work); queue_work(qla_tgt_wq, &op->work);
return 0; return 0;
} }
/* Another WWN used to have our s_id. Our PLOGI scheduled its
* session deletion, but it's still in sess_del_work wq */
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
ql_dbg(ql_dbg_io, vha, 0x3061,
"New command while old session %p is being deleted\n",
sess);
return -EFAULT;
}
/* /*
* Do kref_get() before returning + dropping qla_hw_data->hardware_lock. * Do kref_get() before returning + dropping qla_hw_data->hardware_lock.
*/ */
...@@ -3451,13 +3797,13 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, ...@@ -3451,13 +3797,13 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha,
return -ENOMEM; return -ENOMEM;
} }
cmd->cmd_flags = 0;
cmd->jiffies_at_alloc = get_jiffies_64();
cmd->reset_count = vha->hw->chip_reset;
cmd->cmd_in_wq = 1; cmd->cmd_in_wq = 1;
cmd->cmd_flags |= BIT_0; cmd->cmd_flags |= BIT_0;
spin_lock(&vha->cmd_list_lock);
list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list);
spin_unlock(&vha->cmd_list_lock);
INIT_WORK(&cmd->work, qlt_do_work); INIT_WORK(&cmd->work, qlt_do_work);
queue_work(qla_tgt_wq, &cmd->work); queue_work(qla_tgt_wq, &cmd->work);
return 0; return 0;
...@@ -3471,6 +3817,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, ...@@ -3471,6 +3817,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
struct scsi_qla_host *vha = sess->vha; struct scsi_qla_host *vha = sess->vha;
struct qla_hw_data *ha = vha->hw; struct qla_hw_data *ha = vha->hw;
struct qla_tgt_mgmt_cmd *mcmd; struct qla_tgt_mgmt_cmd *mcmd;
struct atio_from_isp *a = (struct atio_from_isp *)iocb;
int res; int res;
uint8_t tmr_func; uint8_t tmr_func;
...@@ -3511,6 +3858,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, ...@@ -3511,6 +3858,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun,
ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002, ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002,
"qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx); "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx);
tmr_func = TMR_LUN_RESET; tmr_func = TMR_LUN_RESET;
abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id);
break; break;
case QLA_TGT_CLEAR_TS: case QLA_TGT_CLEAR_TS:
...@@ -3599,6 +3947,9 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) ...@@ -3599,6 +3947,9 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb)
sizeof(struct atio_from_isp)); sizeof(struct atio_from_isp));
} }
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS)
return -EFAULT;
return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
} }
...@@ -3664,22 +4015,280 @@ static int qlt_abort_task(struct scsi_qla_host *vha, ...@@ -3664,22 +4015,280 @@ static int qlt_abort_task(struct scsi_qla_host *vha,
return __qlt_abort_task(vha, iocb, sess); return __qlt_abort_task(vha, iocb, sess);
} }
void qlt_logo_completion_handler(fc_port_t *fcport, int rc)
{
if (fcport->tgt_session) {
if (rc != MBS_COMMAND_COMPLETE) {
ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093,
"%s: se_sess %p / sess %p from"
" port %8phC loop_id %#04x s_id %02x:%02x:%02x"
" LOGO failed: %#x\n",
__func__,
fcport->tgt_session->se_sess,
fcport->tgt_session,
fcport->port_name, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, rc);
}
fcport->tgt_session->logout_completed = 1;
}
}
static void qlt_swap_imm_ntfy_iocb(struct imm_ntfy_from_isp *a,
struct imm_ntfy_from_isp *b)
{
struct imm_ntfy_from_isp tmp;
memcpy(&tmp, a, sizeof(struct imm_ntfy_from_isp));
memcpy(a, b, sizeof(struct imm_ntfy_from_isp));
memcpy(b, &tmp, sizeof(struct imm_ntfy_from_isp));
}
/*
* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list)
*
* Schedules sessions with matching port_id/loop_id but different wwn for
* deletion. Returns existing session with matching wwn if present.
* Null otherwise.
*/
static struct qla_tgt_sess *
qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn,
port_id_t port_id, uint16_t loop_id)
{
struct qla_tgt_sess *sess = NULL, *other_sess;
uint64_t other_wwn;
list_for_each_entry(other_sess, &tgt->sess_list, sess_list_entry) {
other_wwn = wwn_to_u64(other_sess->port_name);
if (wwn == other_wwn) {
WARN_ON(sess);
sess = other_sess;
continue;
}
/* find other sess with nport_id collision */
if (port_id.b24 == other_sess->s_id.b24) {
if (loop_id != other_sess->loop_id) {
ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000c,
"Invalidating sess %p loop_id %d wwn %llx.\n",
other_sess, other_sess->loop_id, other_wwn);
/*
* logout_on_delete is set by default, but another
* session that has the same s_id/loop_id combo
* might have cleared it when requested this session
* deletion, so don't touch it
*/
qlt_schedule_sess_for_deletion(other_sess, true);
} else {
/*
* Another wwn used to have our s_id/loop_id
* combo - kill the session, but don't log out
*/
sess->logout_on_delete = 0;
qlt_schedule_sess_for_deletion(other_sess,
true);
}
continue;
}
/* find other sess with nport handle collision */
if (loop_id == other_sess->loop_id) {
ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000d,
"Invalidating sess %p loop_id %d wwn %llx.\n",
other_sess, other_sess->loop_id, other_wwn);
/* Same loop_id but different s_id
* Ok to kill and logout */
qlt_schedule_sess_for_deletion(other_sess, true);
}
}
return sess;
}
/* Abort any commands for this s_id waiting on qla_tgt_wq workqueue */
static int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id)
{
struct qla_tgt_sess_op *op;
struct qla_tgt_cmd *cmd;
uint32_t key;
int count = 0;
key = (((u32)s_id->b.domain << 16) |
((u32)s_id->b.area << 8) |
((u32)s_id->b.al_pa));
spin_lock(&vha->cmd_list_lock);
list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
if (op_key == key) {
op->aborted = true;
count++;
}
}
list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) {
uint32_t cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id);
if (cmd_key == key) {
cmd->state = QLA_TGT_STATE_ABORTED;
count++;
}
}
spin_unlock(&vha->cmd_list_lock);
return count;
}
/* /*
* ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
*/ */
static int qlt_24xx_handle_els(struct scsi_qla_host *vha, static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
struct imm_ntfy_from_isp *iocb) struct imm_ntfy_from_isp *iocb)
{ {
struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
struct qla_hw_data *ha = vha->hw;
struct qla_tgt_sess *sess = NULL;
uint64_t wwn;
port_id_t port_id;
uint16_t loop_id;
uint16_t wd3_lo;
int res = 0; int res = 0;
wwn = wwn_to_u64(iocb->u.isp24.port_name);
port_id.b.domain = iocb->u.isp24.port_id[2];
port_id.b.area = iocb->u.isp24.port_id[1];
port_id.b.al_pa = iocb->u.isp24.port_id[0];
port_id.b.rsvd_1 = 0;
loop_id = le16_to_cpu(iocb->u.isp24.nport_handle);
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026,
"qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n", "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n",
vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode); vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode);
/* res = 1 means ack at the end of thread
* res = 0 means ack async/later.
*/
switch (iocb->u.isp24.status_subcode) { switch (iocb->u.isp24.status_subcode) {
case ELS_PLOGI: case ELS_PLOGI:
case ELS_FLOGI:
/* Mark all stale commands in qla_tgt_wq for deletion */
abort_cmds_for_s_id(vha, &port_id);
if (wwn)
sess = qlt_find_sess_invalidate_other(tgt, wwn,
port_id, loop_id);
if (!sess || IS_SW_RESV_ADDR(sess->s_id)) {
res = 1;
break;
}
if (sess->plogi_ack_needed) {
/*
* Initiator sent another PLOGI before last PLOGI could
* finish. Swap plogi iocbs and terminate old one
* without acking, new one will get acked when session
* deletion completes.
*/
ql_log(ql_log_warn, sess->vha, 0xf094,
"sess %p received double plogi.\n", sess);
qlt_swap_imm_ntfy_iocb(iocb, &sess->tm_iocb);
qlt_send_term_imm_notif(vha, iocb, 1);
res = 0;
break;
}
res = 0;
/*
* Save immediate Notif IOCB for Ack when sess is done
* and being deleted.
*/
memcpy(&sess->tm_iocb, iocb, sizeof(sess->tm_iocb));
sess->plogi_ack_needed = 1;
/*
* Under normal circumstances we want to release nport handle
* during LOGO process to avoid nport handle leaks inside FW.
* The exception is when LOGO is done while another PLOGI with
* the same nport handle is waiting as might be the case here.
* Note: there is always a possibily of a race where session
* deletion has already started for other reasons (e.g. ACL
* removal) and now PLOGI arrives:
* 1. if PLOGI arrived in FW after nport handle has been freed,
* FW must have assigned this PLOGI a new/same handle and we
* can proceed ACK'ing it as usual when session deletion
* completes.
* 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT
* bit reached it, the handle has now been released. We'll
* get an error when we ACK this PLOGI. Nothing will be sent
* back to initiator. Initiator should eventually retry
* PLOGI and situation will correct itself.
*/
sess->keep_nport_handle = ((sess->loop_id == loop_id) &&
(sess->s_id.b24 == port_id.b24));
qlt_schedule_sess_for_deletion(sess, true);
break;
case ELS_PRLI: case ELS_PRLI:
wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo);
if (wwn)
sess = qlt_find_sess_invalidate_other(tgt, wwn, port_id,
loop_id);
if (sess != NULL) {
if (sess->deleted) {
/*
* Impatient initiator sent PRLI before last
* PLOGI could finish. Will force him to re-try,
* while last one finishes.
*/
ql_log(ql_log_warn, sess->vha, 0xf095,
"sess %p PRLI received, before plogi ack.\n",
sess);
qlt_send_term_imm_notif(vha, iocb, 1);
res = 0;
break;
}
/*
* This shouldn't happen under normal circumstances,
* since we have deleted the old session during PLOGI
*/
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf096,
"PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n",
sess->loop_id, sess, iocb->u.isp24.nport_handle);
sess->local = 0;
sess->loop_id = loop_id;
sess->s_id = port_id;
if (wd3_lo & BIT_7)
sess->conf_compl_supported = 1;
}
res = 1; /* send notify ack */
/* Make session global (not used in fabric mode) */
if (ha->current_topology != ISP_CFG_F) {
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
} else {
/* todo: else - create sess here. */
res = 1; /* send notify ack */
}
break;
case ELS_LOGO: case ELS_LOGO:
case ELS_PRLO: case ELS_PRLO:
res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS);
...@@ -3697,6 +4306,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, ...@@ -3697,6 +4306,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
break; break;
} }
case ELS_FLOGI: /* should never happen */
default: default:
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061,
"qla_target(%d): Unsupported ELS command %x " "qla_target(%d): Unsupported ELS command %x "
...@@ -5012,6 +5622,11 @@ static void qlt_abort_work(struct qla_tgt *tgt, ...@@ -5012,6 +5622,11 @@ static void qlt_abort_work(struct qla_tgt *tgt,
if (!sess) if (!sess)
goto out_term; goto out_term;
} else { } else {
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
sess = NULL;
goto out_term;
}
kref_get(&sess->se_sess->sess_kref); kref_get(&sess->se_sess->sess_kref);
} }
...@@ -5066,6 +5681,11 @@ static void qlt_tmr_work(struct qla_tgt *tgt, ...@@ -5066,6 +5681,11 @@ static void qlt_tmr_work(struct qla_tgt *tgt,
if (!sess) if (!sess)
goto out_term; goto out_term;
} else { } else {
if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) {
sess = NULL;
goto out_term;
}
kref_get(&sess->se_sess->sess_kref); kref_get(&sess->se_sess->sess_kref);
} }
...@@ -5552,6 +6172,7 @@ qlt_24xx_process_atio_queue(struct scsi_qla_host *vha) ...@@ -5552,6 +6172,7 @@ qlt_24xx_process_atio_queue(struct scsi_qla_host *vha)
/* Adjust ring index */ /* Adjust ring index */
WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index); WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index);
RD_REG_DWORD_RELAXED(ISP_ATIO_Q_OUT(vha));
} }
void void
...@@ -5793,7 +6414,7 @@ qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha) ...@@ -5793,7 +6414,7 @@ qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha)
if (!QLA_TGT_MODE_ENABLED()) if (!QLA_TGT_MODE_ENABLED())
return; return;
if (ha->mqenable || IS_QLA83XX(ha)) { if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) {
ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in; ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in;
ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out; ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out;
} else { } else {
......
...@@ -167,7 +167,24 @@ struct imm_ntfy_from_isp { ...@@ -167,7 +167,24 @@ struct imm_ntfy_from_isp {
uint32_t srr_rel_offs; uint32_t srr_rel_offs;
uint16_t srr_ui; uint16_t srr_ui;
uint16_t srr_ox_id; uint16_t srr_ox_id;
uint8_t reserved_4[19]; union {
struct {
uint8_t node_name[8];
} plogi; /* PLOGI/ADISC/PDISC */
struct {
/* PRLI word 3 bit 0-15 */
uint16_t wd3_lo;
uint8_t resv0[6];
} prli;
struct {
uint8_t port_id[3];
uint8_t resv1;
uint16_t nport_handle;
uint16_t resv2;
} req_els;
} u;
uint8_t port_name[8];
uint8_t resv3[3];
uint8_t vp_index; uint8_t vp_index;
uint32_t reserved_5; uint32_t reserved_5;
uint8_t port_id[3]; uint8_t port_id[3];
...@@ -234,6 +251,7 @@ struct nack_to_isp { ...@@ -234,6 +251,7 @@ struct nack_to_isp {
uint8_t reserved[2]; uint8_t reserved[2];
uint16_t ox_id; uint16_t ox_id;
} __packed; } __packed;
#define NOTIFY_ACK_FLAGS_TERMINATE BIT_3
#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0 #define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0
#define NOTIFY_ACK_SRR_FLAGS_REJECT 1 #define NOTIFY_ACK_SRR_FLAGS_REJECT 1
...@@ -790,13 +808,6 @@ int qla2x00_wait_for_hba_online(struct scsi_qla_host *); ...@@ -790,13 +808,6 @@ int qla2x00_wait_for_hba_online(struct scsi_qla_host *);
#define FC_TM_REJECT 4 #define FC_TM_REJECT 4
#define FC_TM_FAILED 5 #define FC_TM_FAILED 5
/*
* Error code of qlt_pre_xmit_response() meaning that cmd's exchange was
* terminated, so no more actions is needed and success should be returned
* to target.
*/
#define QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED 0x1717
#if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G) #if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G)
#define pci_dma_lo32(a) (a & 0xffffffff) #define pci_dma_lo32(a) (a & 0xffffffff)
#define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff) #define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff)
...@@ -874,6 +885,15 @@ struct qla_tgt_sess_op { ...@@ -874,6 +885,15 @@ struct qla_tgt_sess_op {
struct scsi_qla_host *vha; struct scsi_qla_host *vha;
struct atio_from_isp atio; struct atio_from_isp atio;
struct work_struct work; struct work_struct work;
struct list_head cmd_list;
bool aborted;
};
enum qla_sess_deletion {
QLA_SESS_DELETION_NONE = 0,
QLA_SESS_DELETION_PENDING = 1, /* hopefully we can get rid of
* this one */
QLA_SESS_DELETION_IN_PROGRESS = 2,
}; };
/* /*
...@@ -884,8 +904,15 @@ struct qla_tgt_sess { ...@@ -884,8 +904,15 @@ struct qla_tgt_sess {
port_id_t s_id; port_id_t s_id;
unsigned int conf_compl_supported:1; unsigned int conf_compl_supported:1;
unsigned int deleted:1; unsigned int deleted:2;
unsigned int local:1; unsigned int local:1;
unsigned int logout_on_delete:1;
unsigned int plogi_ack_needed:1;
unsigned int keep_nport_handle:1;
unsigned char logout_completed;
int generation;
struct se_session *se_sess; struct se_session *se_sess;
struct scsi_qla_host *vha; struct scsi_qla_host *vha;
...@@ -897,6 +924,10 @@ struct qla_tgt_sess { ...@@ -897,6 +924,10 @@ struct qla_tgt_sess {
uint8_t port_name[WWN_SIZE]; uint8_t port_name[WWN_SIZE];
struct work_struct free_work; struct work_struct free_work;
union {
struct imm_ntfy_from_isp tm_iocb;
};
}; };
struct qla_tgt_cmd { struct qla_tgt_cmd {
...@@ -912,7 +943,6 @@ struct qla_tgt_cmd { ...@@ -912,7 +943,6 @@ struct qla_tgt_cmd {
unsigned int conf_compl_supported:1; unsigned int conf_compl_supported:1;
unsigned int sg_mapped:1; unsigned int sg_mapped:1;
unsigned int free_sg:1; unsigned int free_sg:1;
unsigned int aborted:1; /* Needed in case of SRR */
unsigned int write_data_transferred:1; unsigned int write_data_transferred:1;
unsigned int ctx_dsd_alloced:1; unsigned int ctx_dsd_alloced:1;
unsigned int q_full:1; unsigned int q_full:1;
...@@ -961,6 +991,9 @@ struct qla_tgt_cmd { ...@@ -961,6 +991,9 @@ struct qla_tgt_cmd {
* BIT_14 - Back end data received/sent. * BIT_14 - Back end data received/sent.
* BIT_15 - SRR prepare ctio * BIT_15 - SRR prepare ctio
* BIT_16 - complete free * BIT_16 - complete free
* BIT_17 - flush - qlt_abort_cmd_on_host_reset
* BIT_18 - completion w/abort status
* BIT_19 - completion w/unknown status
*/ */
uint32_t cmd_flags; uint32_t cmd_flags;
}; };
...@@ -1026,6 +1059,10 @@ struct qla_tgt_srr_ctio { ...@@ -1026,6 +1059,10 @@ struct qla_tgt_srr_ctio {
struct qla_tgt_cmd *cmd; struct qla_tgt_cmd *cmd;
}; };
/* Check for Switch reserved address */
#define IS_SW_RESV_ADDR(_s_id) \
((_s_id.b.domain == 0xff) && (_s_id.b.area == 0xfc))
#define QLA_TGT_XMIT_DATA 1 #define QLA_TGT_XMIT_DATA 1
#define QLA_TGT_XMIT_STATUS 2 #define QLA_TGT_XMIT_STATUS 2
#define QLA_TGT_XMIT_ALL (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA) #define QLA_TGT_XMIT_ALL (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA)
...@@ -1043,7 +1080,7 @@ extern int qlt_lport_register(void *, u64, u64, u64, ...@@ -1043,7 +1080,7 @@ extern int qlt_lport_register(void *, u64, u64, u64,
extern void qlt_lport_deregister(struct scsi_qla_host *); extern void qlt_lport_deregister(struct scsi_qla_host *);
extern void qlt_unreg_sess(struct qla_tgt_sess *); extern void qlt_unreg_sess(struct qla_tgt_sess *);
extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *);
extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *); extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *, int);
extern int __init qlt_init(void); extern int __init qlt_init(void);
extern void qlt_exit(void); extern void qlt_exit(void);
extern void qlt_update_vp_map(struct scsi_qla_host *, int); extern void qlt_update_vp_map(struct scsi_qla_host *, int);
...@@ -1073,12 +1110,23 @@ static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) ...@@ -1073,12 +1110,23 @@ static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha)
ha->host->active_mode |= MODE_INITIATOR; ha->host->active_mode |= MODE_INITIATOR;
} }
static inline uint32_t sid_to_key(const uint8_t *s_id)
{
uint32_t key;
key = (((unsigned long)s_id[0] << 16) |
((unsigned long)s_id[1] << 8) |
(unsigned long)s_id[2]);
return key;
}
/* /*
* Exported symbols from qla_target.c LLD logic used by qla2xxx code.. * Exported symbols from qla_target.c LLD logic used by qla2xxx code..
*/ */
extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *);
extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *);
extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t);
extern void qlt_abort_cmd(struct qla_tgt_cmd *);
extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *);
extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *);
extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); extern void qlt_free_cmd(struct qla_tgt_cmd *cmd);
...@@ -1109,5 +1157,7 @@ extern void qlt_stop_phase2(struct qla_tgt *); ...@@ -1109,5 +1157,7 @@ extern void qlt_stop_phase2(struct qla_tgt *);
extern irqreturn_t qla83xx_msix_atio_q(int, void *); extern irqreturn_t qla83xx_msix_atio_q(int, void *);
extern void qlt_83xx_iospace_config(struct qla_hw_data *); extern void qlt_83xx_iospace_config(struct qla_hw_data *);
extern int qlt_free_qfull_cmds(struct scsi_qla_host *); extern int qlt_free_qfull_cmds(struct scsi_qla_host *);
extern void qlt_logo_completion_handler(fc_port_t *, int);
extern void qlt_do_generation_tick(struct scsi_qla_host *, int *);
#endif /* __QLA_TARGET_H */ #endif /* __QLA_TARGET_H */
...@@ -374,7 +374,7 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd) ...@@ -374,7 +374,7 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd)
{ {
struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd *cmd = container_of(se_cmd,
struct qla_tgt_cmd, se_cmd); struct qla_tgt_cmd, se_cmd);
cmd->cmd_flags |= BIT_3;
cmd->bufflen = se_cmd->data_length; cmd->bufflen = se_cmd->data_length;
cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); cmd->dma_data_direction = target_reverse_dma_direction(se_cmd);
...@@ -405,7 +405,7 @@ static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd) ...@@ -405,7 +405,7 @@ static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd)
se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) { se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) {
spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
wait_for_completion_timeout(&se_cmd->t_transport_stop_comp, wait_for_completion_timeout(&se_cmd->t_transport_stop_comp,
3000); 3 * HZ);
return 0; return 0;
} }
spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
...@@ -541,12 +541,10 @@ static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) ...@@ -541,12 +541,10 @@ static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd)
cmd->cmd_flags |= BIT_4; cmd->cmd_flags |= BIT_4;
cmd->bufflen = se_cmd->data_length; cmd->bufflen = se_cmd->data_length;
cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); cmd->dma_data_direction = target_reverse_dma_direction(se_cmd);
cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
cmd->sg_cnt = se_cmd->t_data_nents; cmd->sg_cnt = se_cmd->t_data_nents;
cmd->sg = se_cmd->t_data_sg; cmd->sg = se_cmd->t_data_sg;
cmd->offset = 0; cmd->offset = 0;
cmd->cmd_flags |= BIT_3;
cmd->prot_sg_cnt = se_cmd->t_prot_nents; cmd->prot_sg_cnt = se_cmd->t_prot_nents;
cmd->prot_sg = se_cmd->t_prot_sg; cmd->prot_sg = se_cmd->t_prot_sg;
...@@ -571,7 +569,6 @@ static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd) ...@@ -571,7 +569,6 @@ static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd)
cmd->sg_cnt = 0; cmd->sg_cnt = 0;
cmd->offset = 0; cmd->offset = 0;
cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); cmd->dma_data_direction = target_reverse_dma_direction(se_cmd);
cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED);
if (cmd->cmd_flags & BIT_5) { if (cmd->cmd_flags & BIT_5) {
pr_crit("Bit_5 already set for cmd = %p.\n", cmd); pr_crit("Bit_5 already set for cmd = %p.\n", cmd);
dump_stack(); dump_stack();
...@@ -636,14 +633,7 @@ static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) ...@@ -636,14 +633,7 @@ static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd)
{ {
struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd *cmd = container_of(se_cmd,
struct qla_tgt_cmd, se_cmd); struct qla_tgt_cmd, se_cmd);
struct scsi_qla_host *vha = cmd->vha; qlt_abort_cmd(cmd);
struct qla_hw_data *ha = vha->hw;
if (!cmd->sg_mapped)
return;
pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
cmd->sg_mapped = 0;
} }
static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *, static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *,
...@@ -1149,9 +1139,7 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id( ...@@ -1149,9 +1139,7 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id(
return NULL; return NULL;
} }
key = (((unsigned long)s_id[0] << 16) | key = sid_to_key(s_id);
((unsigned long)s_id[1] << 8) |
(unsigned long)s_id[2]);
pr_debug("find_sess_by_s_id: 0x%06x\n", key); pr_debug("find_sess_by_s_id: 0x%06x\n", key);
se_nacl = btree_lookup32(&lport->lport_fcport_map, key); se_nacl = btree_lookup32(&lport->lport_fcport_map, key);
...@@ -1186,9 +1174,7 @@ static void tcm_qla2xxx_set_sess_by_s_id( ...@@ -1186,9 +1174,7 @@ static void tcm_qla2xxx_set_sess_by_s_id(
void *slot; void *slot;
int rc; int rc;
key = (((unsigned long)s_id[0] << 16) | key = sid_to_key(s_id);
((unsigned long)s_id[1] << 8) |
(unsigned long)s_id[2]);
pr_debug("set_sess_by_s_id: %06x\n", key); pr_debug("set_sess_by_s_id: %06x\n", key);
slot = btree_lookup32(&lport->lport_fcport_map, key); slot = btree_lookup32(&lport->lport_fcport_map, key);
...@@ -1544,6 +1530,10 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, ...@@ -1544,6 +1530,10 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id,
} }
sess->conf_compl_supported = conf_compl_supported; sess->conf_compl_supported = conf_compl_supported;
/* Reset logout parameters to default */
sess->logout_on_delete = 1;
sess->keep_nport_handle = 0;
} }
/* /*
......
...@@ -3998,6 +3998,12 @@ int iscsi_target_tx_thread(void *arg) ...@@ -3998,6 +3998,12 @@ int iscsi_target_tx_thread(void *arg)
} }
transport_err: transport_err:
/*
* Avoid the normal connection failure code-path if this connection
* is still within LOGIN mode, and iscsi_np process context is
* responsible for cleaning up the early connection failure.
*/
if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
iscsit_take_action_for_connection_exit(conn); iscsit_take_action_for_connection_exit(conn);
out: out:
return 0; return 0;
...@@ -4082,7 +4088,7 @@ static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf) ...@@ -4082,7 +4088,7 @@ static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf)
int iscsi_target_rx_thread(void *arg) int iscsi_target_rx_thread(void *arg)
{ {
int ret; int ret, rc;
u8 buffer[ISCSI_HDR_LEN], opcode; u8 buffer[ISCSI_HDR_LEN], opcode;
u32 checksum = 0, digest = 0; u32 checksum = 0, digest = 0;
struct iscsi_conn *conn = arg; struct iscsi_conn *conn = arg;
...@@ -4092,10 +4098,16 @@ int iscsi_target_rx_thread(void *arg) ...@@ -4092,10 +4098,16 @@ int iscsi_target_rx_thread(void *arg)
* connection recovery / failure event can be triggered externally. * connection recovery / failure event can be triggered externally.
*/ */
allow_signal(SIGINT); allow_signal(SIGINT);
/*
* Wait for iscsi_post_login_handler() to complete before allowing
* incoming iscsi/tcp socket I/O, and/or failing the connection.
*/
rc = wait_for_completion_interruptible(&conn->rx_login_comp);
if (rc < 0)
return 0;
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) { if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
struct completion comp; struct completion comp;
int rc;
init_completion(&comp); init_completion(&comp);
rc = wait_for_completion_interruptible(&comp); rc = wait_for_completion_interruptible(&comp);
...@@ -4532,7 +4544,18 @@ static void iscsit_logout_post_handler_closesession( ...@@ -4532,7 +4544,18 @@ static void iscsit_logout_post_handler_closesession(
struct iscsi_conn *conn) struct iscsi_conn *conn)
{ {
struct iscsi_session *sess = conn->sess; struct iscsi_session *sess = conn->sess;
int sleep = cmpxchg(&conn->tx_thread_active, true, false); int sleep = 1;
/*
* Traditional iscsi/tcp will invoke this logic from TX thread
* context during session logout, so clear tx_thread_active and
* sleep if iscsit_close_connection() has not already occured.
*
* Since iser-target invokes this logic from it's own workqueue,
* always sleep waiting for RX/TX thread shutdown to complete
* within iscsit_close_connection().
*/
if (conn->conn_transport->transport_type == ISCSI_TCP)
sleep = cmpxchg(&conn->tx_thread_active, true, false);
atomic_set(&conn->conn_logout_remove, 0); atomic_set(&conn->conn_logout_remove, 0);
complete(&conn->conn_logout_comp); complete(&conn->conn_logout_comp);
...@@ -4546,7 +4569,10 @@ static void iscsit_logout_post_handler_closesession( ...@@ -4546,7 +4569,10 @@ static void iscsit_logout_post_handler_closesession(
static void iscsit_logout_post_handler_samecid( static void iscsit_logout_post_handler_samecid(
struct iscsi_conn *conn) struct iscsi_conn *conn)
{ {
int sleep = cmpxchg(&conn->tx_thread_active, true, false); int sleep = 1;
if (conn->conn_transport->transport_type == ISCSI_TCP)
sleep = cmpxchg(&conn->tx_thread_active, true, false);
atomic_set(&conn->conn_logout_remove, 0); atomic_set(&conn->conn_logout_remove, 0);
complete(&conn->conn_logout_comp); complete(&conn->conn_logout_comp);
...@@ -4765,6 +4791,7 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) ...@@ -4765,6 +4791,7 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
struct iscsi_session *sess; struct iscsi_session *sess;
struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
struct se_session *se_sess, *se_sess_tmp; struct se_session *se_sess, *se_sess_tmp;
LIST_HEAD(free_list);
int session_count = 0; int session_count = 0;
spin_lock_bh(&se_tpg->session_lock); spin_lock_bh(&se_tpg->session_lock);
...@@ -4786,14 +4813,17 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) ...@@ -4786,14 +4813,17 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
} }
atomic_set(&sess->session_reinstatement, 1); atomic_set(&sess->session_reinstatement, 1);
spin_unlock(&sess->conn_lock); spin_unlock(&sess->conn_lock);
list_move_tail(&se_sess->sess_list, &free_list);
}
spin_unlock_bh(&se_tpg->session_lock); spin_unlock_bh(&se_tpg->session_lock);
iscsit_free_session(sess); list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
spin_lock_bh(&se_tpg->session_lock); sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
iscsit_free_session(sess);
session_count++; session_count++;
} }
spin_unlock_bh(&se_tpg->session_lock);
pr_debug("Released %d iSCSI Session(s) from Target Portal" pr_debug("Released %d iSCSI Session(s) from Target Portal"
" Group: %hu\n", session_count, tpg->tpgt); " Group: %hu\n", session_count, tpg->tpgt);
......
...@@ -82,6 +82,7 @@ static struct iscsi_login *iscsi_login_init_conn(struct iscsi_conn *conn) ...@@ -82,6 +82,7 @@ static struct iscsi_login *iscsi_login_init_conn(struct iscsi_conn *conn)
init_completion(&conn->conn_logout_comp); init_completion(&conn->conn_logout_comp);
init_completion(&conn->rx_half_close_comp); init_completion(&conn->rx_half_close_comp);
init_completion(&conn->tx_half_close_comp); init_completion(&conn->tx_half_close_comp);
init_completion(&conn->rx_login_comp);
spin_lock_init(&conn->cmd_lock); spin_lock_init(&conn->cmd_lock);
spin_lock_init(&conn->conn_usage_lock); spin_lock_init(&conn->conn_usage_lock);
spin_lock_init(&conn->immed_queue_lock); spin_lock_init(&conn->immed_queue_lock);
...@@ -644,7 +645,7 @@ static void iscsi_post_login_start_timers(struct iscsi_conn *conn) ...@@ -644,7 +645,7 @@ static void iscsi_post_login_start_timers(struct iscsi_conn *conn)
iscsit_start_nopin_timer(conn); iscsit_start_nopin_timer(conn);
} }
static int iscsit_start_kthreads(struct iscsi_conn *conn) int iscsit_start_kthreads(struct iscsi_conn *conn)
{ {
int ret = 0; int ret = 0;
...@@ -679,6 +680,7 @@ static int iscsit_start_kthreads(struct iscsi_conn *conn) ...@@ -679,6 +680,7 @@ static int iscsit_start_kthreads(struct iscsi_conn *conn)
return 0; return 0;
out_tx: out_tx:
send_sig(SIGINT, conn->tx_thread, 1);
kthread_stop(conn->tx_thread); kthread_stop(conn->tx_thread);
conn->tx_thread_active = false; conn->tx_thread_active = false;
out_bitmap: out_bitmap:
...@@ -689,7 +691,7 @@ static int iscsit_start_kthreads(struct iscsi_conn *conn) ...@@ -689,7 +691,7 @@ static int iscsit_start_kthreads(struct iscsi_conn *conn)
return ret; return ret;
} }
int iscsi_post_login_handler( void iscsi_post_login_handler(
struct iscsi_np *np, struct iscsi_np *np,
struct iscsi_conn *conn, struct iscsi_conn *conn,
u8 zero_tsih) u8 zero_tsih)
...@@ -699,7 +701,6 @@ int iscsi_post_login_handler( ...@@ -699,7 +701,6 @@ int iscsi_post_login_handler(
struct se_session *se_sess = sess->se_sess; struct se_session *se_sess = sess->se_sess;
struct iscsi_portal_group *tpg = sess->tpg; struct iscsi_portal_group *tpg = sess->tpg;
struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
int rc;
iscsit_inc_conn_usage_count(conn); iscsit_inc_conn_usage_count(conn);
...@@ -739,10 +740,6 @@ int iscsi_post_login_handler( ...@@ -739,10 +740,6 @@ int iscsi_post_login_handler(
sess->sess_ops->InitiatorName); sess->sess_ops->InitiatorName);
spin_unlock_bh(&sess->conn_lock); spin_unlock_bh(&sess->conn_lock);
rc = iscsit_start_kthreads(conn);
if (rc)
return rc;
iscsi_post_login_start_timers(conn); iscsi_post_login_start_timers(conn);
/* /*
* Determine CPU mask to ensure connection's RX and TX kthreads * Determine CPU mask to ensure connection's RX and TX kthreads
...@@ -751,15 +748,20 @@ int iscsi_post_login_handler( ...@@ -751,15 +748,20 @@ int iscsi_post_login_handler(
iscsit_thread_get_cpumask(conn); iscsit_thread_get_cpumask(conn);
conn->conn_rx_reset_cpumask = 1; conn->conn_rx_reset_cpumask = 1;
conn->conn_tx_reset_cpumask = 1; conn->conn_tx_reset_cpumask = 1;
/*
* Wakeup the sleeping iscsi_target_rx_thread() now that
* iscsi_conn is in TARG_CONN_STATE_LOGGED_IN state.
*/
complete(&conn->rx_login_comp);
iscsit_dec_conn_usage_count(conn); iscsit_dec_conn_usage_count(conn);
if (stop_timer) { if (stop_timer) {
spin_lock_bh(&se_tpg->session_lock); spin_lock_bh(&se_tpg->session_lock);
iscsit_stop_time2retain_timer(sess); iscsit_stop_time2retain_timer(sess);
spin_unlock_bh(&se_tpg->session_lock); spin_unlock_bh(&se_tpg->session_lock);
} }
iscsit_dec_session_usage_count(sess); iscsit_dec_session_usage_count(sess);
return 0; return;
} }
iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 1); iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 1);
...@@ -800,10 +802,6 @@ int iscsi_post_login_handler( ...@@ -800,10 +802,6 @@ int iscsi_post_login_handler(
" iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt); " iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt);
spin_unlock_bh(&se_tpg->session_lock); spin_unlock_bh(&se_tpg->session_lock);
rc = iscsit_start_kthreads(conn);
if (rc)
return rc;
iscsi_post_login_start_timers(conn); iscsi_post_login_start_timers(conn);
/* /*
* Determine CPU mask to ensure connection's RX and TX kthreads * Determine CPU mask to ensure connection's RX and TX kthreads
...@@ -812,10 +810,12 @@ int iscsi_post_login_handler( ...@@ -812,10 +810,12 @@ int iscsi_post_login_handler(
iscsit_thread_get_cpumask(conn); iscsit_thread_get_cpumask(conn);
conn->conn_rx_reset_cpumask = 1; conn->conn_rx_reset_cpumask = 1;
conn->conn_tx_reset_cpumask = 1; conn->conn_tx_reset_cpumask = 1;
/*
* Wakeup the sleeping iscsi_target_rx_thread() now that
* iscsi_conn is in TARG_CONN_STATE_LOGGED_IN state.
*/
complete(&conn->rx_login_comp);
iscsit_dec_conn_usage_count(conn); iscsit_dec_conn_usage_count(conn);
return 0;
} }
static void iscsi_handle_login_thread_timeout(unsigned long data) static void iscsi_handle_login_thread_timeout(unsigned long data)
...@@ -1380,23 +1380,12 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) ...@@ -1380,23 +1380,12 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
if (ret < 0) if (ret < 0)
goto new_sess_out; goto new_sess_out;
if (!conn->sess) {
pr_err("struct iscsi_conn session pointer is NULL!\n");
goto new_sess_out;
}
iscsi_stop_login_thread_timer(np); iscsi_stop_login_thread_timer(np);
if (signal_pending(current))
goto new_sess_out;
if (ret == 1) { if (ret == 1) {
tpg_np = conn->tpg_np; tpg_np = conn->tpg_np;
ret = iscsi_post_login_handler(np, conn, zero_tsih); iscsi_post_login_handler(np, conn, zero_tsih);
if (ret < 0)
goto new_sess_out;
iscsit_deaccess_np(np, tpg, tpg_np); iscsit_deaccess_np(np, tpg, tpg_np);
} }
......
...@@ -12,7 +12,8 @@ extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *); ...@@ -12,7 +12,8 @@ extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *);
extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *); extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *);
extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32); extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32);
extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *); extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *);
extern int iscsi_post_login_handler(struct iscsi_np *, struct iscsi_conn *, u8); extern int iscsit_start_kthreads(struct iscsi_conn *);
extern void iscsi_post_login_handler(struct iscsi_np *, struct iscsi_conn *, u8);
extern void iscsi_target_login_sess_out(struct iscsi_conn *, struct iscsi_np *, extern void iscsi_target_login_sess_out(struct iscsi_conn *, struct iscsi_np *,
bool, bool); bool, bool);
extern int iscsi_target_login_thread(void *); extern int iscsi_target_login_thread(void *);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
******************************************************************************/ ******************************************************************************/
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/kthread.h>
#include <scsi/iscsi_proto.h> #include <scsi/iscsi_proto.h>
#include <target/target_core_base.h> #include <target/target_core_base.h>
#include <target/target_core_fabric.h> #include <target/target_core_fabric.h>
...@@ -361,10 +362,24 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log ...@@ -361,10 +362,24 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
ntohl(login_rsp->statsn), login->rsp_length); ntohl(login_rsp->statsn), login->rsp_length);
padding = ((-login->rsp_length) & 3); padding = ((-login->rsp_length) & 3);
/*
* Before sending the last login response containing the transition
* bit for full-feature-phase, go ahead and start up TX/RX threads
* now to avoid potential resource allocation failures after the
* final login response has been sent.
*/
if (login->login_complete) {
int rc = iscsit_start_kthreads(conn);
if (rc) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
return -1;
}
}
if (conn->conn_transport->iscsit_put_login_tx(conn, login, if (conn->conn_transport->iscsit_put_login_tx(conn, login,
login->rsp_length + padding) < 0) login->rsp_length + padding) < 0)
return -1; goto err;
login->rsp_length = 0; login->rsp_length = 0;
mutex_lock(&sess->cmdsn_mutex); mutex_lock(&sess->cmdsn_mutex);
...@@ -373,6 +388,23 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log ...@@ -373,6 +388,23 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
mutex_unlock(&sess->cmdsn_mutex); mutex_unlock(&sess->cmdsn_mutex);
return 0; return 0;
err:
if (login->login_complete) {
if (conn->rx_thread && conn->rx_thread_active) {
send_sig(SIGINT, conn->rx_thread, 1);
kthread_stop(conn->rx_thread);
}
if (conn->tx_thread && conn->tx_thread_active) {
send_sig(SIGINT, conn->tx_thread, 1);
kthread_stop(conn->tx_thread);
}
spin_lock(&iscsit_global->ts_bitmap_lock);
bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id,
get_order(1));
spin_unlock(&iscsit_global->ts_bitmap_lock);
}
return -1;
} }
static void iscsi_target_sk_data_ready(struct sock *sk) static void iscsi_target_sk_data_ready(struct sock *sk)
......
...@@ -747,7 +747,7 @@ static ssize_t store_pi_prot_type(struct se_dev_attrib *da, ...@@ -747,7 +747,7 @@ static ssize_t store_pi_prot_type(struct se_dev_attrib *da,
if (!dev->transport->init_prot || !dev->transport->free_prot) { if (!dev->transport->init_prot || !dev->transport->free_prot) {
/* 0 is only allowed value for non-supporting backends */ /* 0 is only allowed value for non-supporting backends */
if (flag == 0) if (flag == 0)
return 0; return count;
pr_err("DIF protection not supported by backend: %s\n", pr_err("DIF protection not supported by backend: %s\n",
dev->transport->name); dev->transport->name);
...@@ -1590,9 +1590,9 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( ...@@ -1590,9 +1590,9 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
u8 type = 0; u8 type = 0;
if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH) if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
return 0; return count;
if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
return 0; return count;
if (dev->export_count) { if (dev->export_count) {
pr_debug("Unable to process APTPL metadata while" pr_debug("Unable to process APTPL metadata while"
...@@ -1658,22 +1658,32 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( ...@@ -1658,22 +1658,32 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
* PR APTPL Metadata for Reservation * PR APTPL Metadata for Reservation
*/ */
case Opt_res_holder: case Opt_res_holder:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
res_holder = arg; res_holder = arg;
break; break;
case Opt_res_type: case Opt_res_type:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
type = (u8)arg; type = (u8)arg;
break; break;
case Opt_res_scope: case Opt_res_scope:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
break; break;
case Opt_res_all_tg_pt: case Opt_res_all_tg_pt:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
all_tg_pt = (int)arg; all_tg_pt = (int)arg;
break; break;
case Opt_mapped_lun: case Opt_mapped_lun:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
mapped_lun = (u64)arg; mapped_lun = (u64)arg;
break; break;
/* /*
...@@ -1701,14 +1711,20 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( ...@@ -1701,14 +1711,20 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata(
} }
break; break;
case Opt_tpgt: case Opt_tpgt:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
tpgt = (u16)arg; tpgt = (u16)arg;
break; break;
case Opt_port_rtpi: case Opt_port_rtpi:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
break; break;
case Opt_target_lun: case Opt_target_lun:
match_int(args, &arg); ret = match_int(args, &arg);
if (ret)
goto out;
target_lun = (u64)arg; target_lun = (u64)arg;
break; break;
default: default:
...@@ -1985,7 +2001,7 @@ static ssize_t target_core_store_alua_lu_gp( ...@@ -1985,7 +2001,7 @@ static ssize_t target_core_store_alua_lu_gp(
lu_gp_mem = dev->dev_alua_lu_gp_mem; lu_gp_mem = dev->dev_alua_lu_gp_mem;
if (!lu_gp_mem) if (!lu_gp_mem)
return 0; return count;
if (count > LU_GROUP_NAME_BUF) { if (count > LU_GROUP_NAME_BUF) {
pr_err("ALUA LU Group Alias too large!\n"); pr_err("ALUA LU Group Alias too large!\n");
......
...@@ -1474,7 +1474,7 @@ core_scsi3_decode_spec_i_port( ...@@ -1474,7 +1474,7 @@ core_scsi3_decode_spec_i_port(
LIST_HEAD(tid_dest_list); LIST_HEAD(tid_dest_list);
struct pr_transport_id_holder *tidh_new, *tidh, *tidh_tmp; struct pr_transport_id_holder *tidh_new, *tidh, *tidh_tmp;
unsigned char *buf, *ptr, proto_ident; unsigned char *buf, *ptr, proto_ident;
const unsigned char *i_str; const unsigned char *i_str = NULL;
char *iport_ptr = NULL, i_buf[PR_REG_ISID_ID_LEN]; char *iport_ptr = NULL, i_buf[PR_REG_ISID_ID_LEN];
sense_reason_t ret; sense_reason_t ret;
u32 tpdl, tid_len = 0; u32 tpdl, tid_len = 0;
......
...@@ -333,6 +333,7 @@ static int rd_configure_device(struct se_device *dev) ...@@ -333,6 +333,7 @@ static int rd_configure_device(struct se_device *dev)
dev->dev_attrib.hw_block_size = RD_BLOCKSIZE; dev->dev_attrib.hw_block_size = RD_BLOCKSIZE;
dev->dev_attrib.hw_max_sectors = UINT_MAX; dev->dev_attrib.hw_max_sectors = UINT_MAX;
dev->dev_attrib.hw_queue_depth = RD_MAX_DEVICE_QUEUE_DEPTH; dev->dev_attrib.hw_queue_depth = RD_MAX_DEVICE_QUEUE_DEPTH;
dev->dev_attrib.is_nonrot = 1;
rd_dev->rd_dev_id = rd_host->rd_host_dev_id_count++; rd_dev->rd_dev_id = rd_host->rd_host_dev_id_count++;
......
...@@ -458,6 +458,13 @@ spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) ...@@ -458,6 +458,13 @@ spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
buf[4] = 0x4; buf[4] = 0x4;
} }
/* logical unit supports type 1 and type 3 protection */
if ((dev->transport->get_device_type(dev) == TYPE_DISK) &&
(sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) &&
(dev->dev_attrib.pi_prot_type || cmd->se_sess->sess_prot_type)) {
buf[4] |= (0x3 << 3);
}
/* Set HEADSUP, ORDSUP, SIMPSUP */ /* Set HEADSUP, ORDSUP, SIMPSUP */
buf[5] = 0x07; buf[5] = 0x07;
......
...@@ -595,6 +595,7 @@ struct iscsi_conn { ...@@ -595,6 +595,7 @@ struct iscsi_conn {
int bitmap_id; int bitmap_id;
int rx_thread_active; int rx_thread_active;
struct task_struct *rx_thread; struct task_struct *rx_thread;
struct completion rx_login_comp;
int tx_thread_active; int tx_thread_active;
struct task_struct *tx_thread; struct task_struct *tx_thread;
/* list_head for session connection list */ /* list_head for session connection list */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册