qla2xxx: kill sessions/log out initiator on RSCN and port down events

To fix some issues talking to ESX, this patch modifies the qla2xxx driver
so that it never logs into remote ports.  This has the side effect of
getting rid of the "rports" entirely, which means we never log out of
initiators and never tear down sessions when an initiator goes away.

This is mostly OK, except that we can run into trouble if we have
initiator A assigned FC address X:Y:Z by the fabric talking to us, and
then initiator A goes away.  Some time (could be a long time) later,
initiator B comes along and also gets FC address X:Y:Z (which is
available again, because initiator A is gone).  If initiator B starts
talking to us, then we'll still have the session for initiator A, and
since we look up incoming IO based on the FC address X:Y:Z, initiator B
will end up using ACLs for initiator A.

Fix this by:

 1. Handling RSCN events somewhat differently; instead of completely
    skipping the processing of fcports, we look through the list, and if
    an fcport disappears, we tell the target code the tear down the
    session and tell the HBA FW to release the N_Port handle.

 2. Handling "port down" events by flushing all of our sessions.  The
    firmware was already releasing the N_Port handle but we want the
    target code to drop all the sessions too.

Cc: <stable@vger.kernel.org> # v3.18+
Signed-off-by: Roland Dreier <roland@purestorage.com>
Signed-off-by: Alexei Potashnik <alexei@purestorage.com>
Acked-by: Quinn Tran <quinn.tran@qlogic.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
This commit is contained in:
Roland Dreier 2015-07-14 16:00:42 -04:00 committed by Nicholas Bellinger
parent 9fce12540c
commit b2032fd567
3 changed files with 117 additions and 31 deletions

View File

@ -68,7 +68,7 @@
* | | | 0xd101-0xd1fe |
* | | | 0xd214-0xd2fe |
* | Target Mode | 0xe079 | |
* | Target Mode Management | 0xf072 | 0xf002 |
* | Target Mode Management | 0xf080 | 0xf002 |
* | | | 0xf046-0xf049 |
* | Target Mode Task Management | 0x1000b | |
* ----------------------------------------------------------------------

View File

@ -3464,20 +3464,43 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
continue;
if (fcport->scan_state == QLA_FCPORT_SCAN &&
atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_FCP2_DEVICE) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
ha->isp_ops->fabric_logout(vha,
fcport->loop_id,
fcport->d_id.b.domain,
fcport->d_id.b.area,
fcport->d_id.b.al_pa);
qla2x00_clear_loop_id(fcport);
if (fcport->scan_state == QLA_FCPORT_SCAN) {
if (qla_ini_mode_enabled(base_vha) &&
atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_FCP2_DEVICE) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
ha->isp_ops->fabric_logout(vha,
fcport->loop_id,
fcport->d_id.b.domain,
fcport->d_id.b.area,
fcport->d_id.b.al_pa);
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);
}
}
}
@ -3498,6 +3521,28 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
(fcport->flags & FCF_LOGIN_NEEDED) == 0)
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) {
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(
@ -3524,16 +3569,38 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
break;
/* Find a new loop ID to use. */
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(base_vha, fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
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. */
fcport->loop_id = next_loopid;
rval = qla2x00_find_new_loop_id(base_vha,
fcport);
if (rval != QLA_SUCCESS) {
/* Ran out of IDs to use */
break;
}
/* Login and update database */
qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
/* Login and update database */
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);
}
@ -3729,11 +3796,12 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
fcport->fp_speed = new_fcport->fp_speed;
/*
* If address the same and state FCS_ONLINE, nothing
* changed.
* If address the same and state FCS_ONLINE
* (or in target mode), nothing changed.
*/
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;
}
@ -3753,6 +3821,22 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
* Log it out if still logged in and mark it for
* 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->flags |= FCF_LOGIN_NEEDED;
if (fcport->loop_id != FC_NO_LOOP_ID &&
@ -3772,6 +3856,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
if (found)
continue;
/* 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);
/* Allocate a new replacement fcport. */

View File

@ -113,6 +113,7 @@ static void qlt_abort_cmd_on_host_reset(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);
static void qlt_disable_vha(struct scsi_qla_host *vha);
static void qlt_clear_tgt_db(struct qla_tgt *tgt);
/*
* Global Variables
*/
@ -431,10 +432,10 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd)
loop_id = le16_to_cpu(n->u.isp24.nport_handle);
if (loop_id == 0xFFFF) {
#if 0 /* FIXME: Re-enable Global event handling.. */
/* Global event */
atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count);
qlt_clear_tgt_db(ha->tgt.qla_tgt);
atomic_inc(&vha->vha_tgt.qla_tgt->tgt_global_resets_count);
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)) {
sess = list_entry(ha->tgt.qla_tgt->sess_list.next,
typeof(*sess), sess_list_entry);
@ -788,7 +789,7 @@ void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport)
if (!vha->hw->tgt.tgt_ops)
return;
if (!tgt || (fcport->port_type != FCT_INITIATOR))
if (!tgt)
return;
if (tgt->tgt_stop) {