diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-05-18 18:37:53 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-06-12 23:12:24 -0400 |
commit | f2d5d9b90b095ab0e8097b2b0793f4a56ed98147 (patch) | |
tree | ec35bf589f8dad8ccdc932b4fa24dc0e91f3c33b /drivers/scsi/qla2xxx/tcm_qla2xxx.c | |
parent | aaf68b753313f1e67fd2e8996e32ab2813f441fa (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.c | 48 |
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 | ||
824 | static 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 | ||
847 | static void tcm_qla2xxx_release_session(struct kref *kref) | 859 | static 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 | */ | ||
1427 | static 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 | |||
1412 | static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) | 1443 | static 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); |