aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/qla2xxx/tcm_qla2xxx.c
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2012-05-18 18:37:53 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2012-06-12 23:12:24 -0400
commitf2d5d9b90b095ab0e8097b2b0793f4a56ed98147 (patch)
treeec35bf589f8dad8ccdc932b4fa24dc0e91f3c33b /drivers/scsi/qla2xxx/tcm_qla2xxx.c
parentaaf68b753313f1e67fd2e8996e32ab2813f441fa (diff)
tcm_qla2xxx: Clear session s_id + loop_id earlier during shutdown
This patch adds a new tcm_qla2xxx_clear_sess_lookup() call to clear session specific s_id + loop_id entries used for se_node_acl pointer lookup ahead of releasing se_session within the process context workqueue callback in tcm_qla2xxx_free_session(). It makes the call in existing tcm_qla2xxx_clear_nacl_from_fcport_map() code invoked from qlt_unreg_sess() in interrupt context w/ hardware_lock held, ahead of the process context callback into qlt_free_session_done() -> tcm_qla2xxx_free_session(). We are doing this to address a race between incoming ATIO or TMR packets using stale se_node_acl pointer once session shutdown has been invoked via qlt_unreg_sess() in qla_target.c LLD code, and when the entire tcm_qla2xxx endpoint has not been forced into shutdown w/ echo 0 > ../$QLA2XXX_PORT/enable Cc: Joern Engel <joern@logfs.org> Cc: Roland Dreier <roland@purestorage.com> Cc: Arun Easi <arun.easi@qlogic.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/scsi/qla2xxx/tcm_qla2xxx.c')
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c48
1 files changed, 31 insertions, 17 deletions
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 3cee5b67d65b..a641c970569a 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -821,6 +821,8 @@ static int tcm_qla2xxx_setup_nacl_from_rport(
821 return 0; 821 return 0;
822} 822}
823 823
824static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *,
825 struct tcm_qla2xxx_nacl *, struct qla_tgt_sess *);
824/* 826/*
825 * Expected to be called with struct qla_hw_data->hardware_lock held 827 * Expected to be called with struct qla_hw_data->hardware_lock held
826 */ 828 */
@@ -842,6 +844,16 @@ static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess)
842 844
843 pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n", 845 pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n",
844 se_nacl, nacl->nport_wwnn, nacl->nport_id); 846 se_nacl, nacl->nport_wwnn, nacl->nport_id);
847 /*
848 * Now clear the se_nacl and session pointers from our HW lport lookup
849 * table mapping for this initiator's fabric S_ID and LOOP_ID entries.
850 *
851 * This is done ahead of callbacks into tcm_qla2xxx_free_session() ->
852 * target_wait_for_sess_cmds() before the session waits for outstanding
853 * I/O to complete, to avoid a race between session shutdown execution
854 * and incoming ATIOs or TMRs picking up a stale se_node_act reference.
855 */
856 tcm_qla2xxx_clear_sess_lookup(lport, nacl, sess);
845} 857}
846 858
847static void tcm_qla2xxx_release_session(struct kref *kref) 859static void tcm_qla2xxx_release_session(struct kref *kref)
@@ -1409,6 +1421,25 @@ static void tcm_qla2xxx_set_sess_by_loop_id(
1409 nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname); 1421 nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname);
1410} 1422}
1411 1423
1424/*
1425 * Should always be called with qla_hw_data->hardware_lock held.
1426 */
1427static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *lport,
1428 struct tcm_qla2xxx_nacl *nacl, struct qla_tgt_sess *sess)
1429{
1430 struct se_session *se_sess = sess->se_sess;
1431 unsigned char be_sid[3];
1432
1433 be_sid[0] = sess->s_id.b.domain;
1434 be_sid[1] = sess->s_id.b.area;
1435 be_sid[2] = sess->s_id.b.al_pa;
1436
1437 tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess,
1438 sess, be_sid);
1439 tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess,
1440 sess, sess->loop_id);
1441}
1442
1412static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) 1443static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
1413{ 1444{
1414 struct qla_tgt *tgt = sess->tgt; 1445 struct qla_tgt *tgt = sess->tgt;
@@ -1417,8 +1448,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
1417 struct se_node_acl *se_nacl; 1448 struct se_node_acl *se_nacl;
1418 struct tcm_qla2xxx_lport *lport; 1449 struct tcm_qla2xxx_lport *lport;
1419 struct tcm_qla2xxx_nacl *nacl; 1450 struct tcm_qla2xxx_nacl *nacl;
1420 unsigned char be_sid[3];
1421 unsigned long flags;
1422 1451
1423 BUG_ON(in_interrupt()); 1452 BUG_ON(in_interrupt());
1424 1453
@@ -1438,21 +1467,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
1438 return; 1467 return;
1439 } 1468 }
1440 target_wait_for_sess_cmds(se_sess, 0); 1469 target_wait_for_sess_cmds(se_sess, 0);
1441 /*
1442 * And now clear the se_nacl and session pointers from our HW lport
1443 * mappings for fabric S_ID and LOOP_ID.
1444 */
1445 memset(&be_sid, 0, 3);
1446 be_sid[0] = sess->s_id.b.domain;
1447 be_sid[1] = sess->s_id.b.area;
1448 be_sid[2] = sess->s_id.b.al_pa;
1449
1450 spin_lock_irqsave(&ha->hardware_lock, flags);
1451 tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess,
1452 sess, be_sid);
1453 tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess,
1454 sess, sess->loop_id);
1455 spin_unlock_irqrestore(&ha->hardware_lock, flags);
1456 1470
1457 transport_deregister_session_configfs(sess->se_sess); 1471 transport_deregister_session_configfs(sess->se_sess);
1458 transport_deregister_session(sess->se_sess); 1472 transport_deregister_session(sess->se_sess);