tcm_qla2xxx: Clear session s_id + loop_id earlier during shutdown
authorNicholas Bellinger <nab@linux-iscsi.org>
Fri, 18 May 2012 22:37:53 +0000 (15:37 -0700)
committerNicholas Bellinger <nab@linux-iscsi.org>
Wed, 13 Jun 2012 03:12:24 +0000 (20:12 -0700)
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>
drivers/scsi/qla2xxx/tcm_qla2xxx.c

index 3cee5b67d65b335a16e54a82d912ef2cbc101314..a641c970569a71266c0eb39393677a25830cf13a 100644 (file)
@@ -821,6 +821,8 @@ static int tcm_qla2xxx_setup_nacl_from_rport(
        return 0;
 }
 
+static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *,
+                       struct tcm_qla2xxx_nacl *, struct qla_tgt_sess *);
 /*
  * Expected to be called with struct qla_hw_data->hardware_lock held
  */
@@ -842,6 +844,16 @@ static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess)
 
        pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n",
            se_nacl, nacl->nport_wwnn, nacl->nport_id);
+       /*
+        * Now clear the se_nacl and session pointers from our HW lport lookup
+        * table mapping for this initiator's fabric S_ID and LOOP_ID entries.
+        *
+        * This is done ahead of callbacks into tcm_qla2xxx_free_session() ->
+        * target_wait_for_sess_cmds() before the session waits for outstanding
+        * I/O to complete, to avoid a race between session shutdown execution
+        * and incoming ATIOs or TMRs picking up a stale se_node_act reference.
+        */
+       tcm_qla2xxx_clear_sess_lookup(lport, nacl, sess);
 }
 
 static void tcm_qla2xxx_release_session(struct kref *kref)
@@ -1409,6 +1421,25 @@ static void tcm_qla2xxx_set_sess_by_loop_id(
            nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname);
 }
 
+/*
+ * Should always be called with qla_hw_data->hardware_lock held.
+ */
+static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *lport,
+               struct tcm_qla2xxx_nacl *nacl, struct qla_tgt_sess *sess)
+{
+       struct se_session *se_sess = sess->se_sess;
+       unsigned char be_sid[3];
+
+       be_sid[0] = sess->s_id.b.domain;
+       be_sid[1] = sess->s_id.b.area;
+       be_sid[2] = sess->s_id.b.al_pa;
+
+       tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess,
+                               sess, be_sid);
+       tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess,
+                               sess, sess->loop_id);
+}
+
 static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
 {
        struct qla_tgt *tgt = sess->tgt;
@@ -1417,8 +1448,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
        struct se_node_acl *se_nacl;
        struct tcm_qla2xxx_lport *lport;
        struct tcm_qla2xxx_nacl *nacl;
-       unsigned char be_sid[3];
-       unsigned long flags;
 
        BUG_ON(in_interrupt());
 
@@ -1438,21 +1467,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
                return;
        }
        target_wait_for_sess_cmds(se_sess, 0);
-       /*
-        * And now clear the se_nacl and session pointers from our HW lport
-        * mappings for fabric S_ID and LOOP_ID.
-        */
-       memset(&be_sid, 0, 3);
-       be_sid[0] = sess->s_id.b.domain;
-       be_sid[1] = sess->s_id.b.area;
-       be_sid[2] = sess->s_id.b.al_pa;
-
-       spin_lock_irqsave(&ha->hardware_lock, flags);
-       tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess,
-                       sess, be_sid);
-       tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess,
-                       sess, sess->loop_id);
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
        transport_deregister_session_configfs(sess->se_sess);
        transport_deregister_session(sess->se_sess);