scsi: target: Make the session shutdown code also wait for commands that are being...
authorBart Van Assche <bart.vanassche@wdc.com>
Fri, 22 Jun 2018 21:52:53 +0000 (14:52 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 2 Jul 2018 20:44:31 +0000 (16:44 -0400)
Target drivers must call target_sess_cmd_list_set_waiting() and
target_wait_for_sess_cmds() before freeing a session. Since freeing a
session is only safe after all commands that are associated with a session
have finished, make target_wait_for_sess_cmds() also wait for commands that
are being aborted. Instead of setting a flag in each pending command from
target_sess_cmd_list_set_waiting() and waiting in
target_wait_for_sess_cmds() on a per-command completion, only set a
per-session flag in the former function and wait on a per-session
completion in the latter function. This change is safe because once a SCSI
initiator system has submitted a command a target system is always allowed
to execute it to completion. See also commit 0f4a943168f3 ("target: Fix
remote-port TMR ABORT + se_cmd fabric stop").

This patch is based on the following two patches:

* Bart Van Assche, target: Simplify session shutdown code, February 19, 2015
  (https://github.com/bvanassche/linux/commit/8df5463d7d7619f2f1b70cfe5172eaef0aa52815).

* Christoph Hellwig, target: Rework session shutdown code, December 7, 2015
  (http://thread.gmane.org/gmane.linux.scsi.target.devel/10695).

Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Reviewed-by: Mike Christie <mchristi@redhat.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Sagi Grimberg <sagig@mellanox.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/target/target_core_tmr.c
drivers/target/target_core_transport.c
include/target/target_core_base.h

index 9c7bc1ca341a6821582b79e0916901346b841515..da8125dd3a4c42008013878692a336d64a46f887 100644 (file)
@@ -142,7 +142,7 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
                        return false;
                }
        }
-       if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
+       if (sess->sess_tearing_down) {
                pr_debug("Attempted to abort io tag: %llu already shutdown,"
                        " skipping\n", se_cmd->tag);
                spin_unlock(&se_cmd->t_state_lock);
@@ -187,7 +187,6 @@ void core_tmr_abort_task(
                if (!__target_check_io_state(se_cmd, se_sess, 0))
                        continue;
 
-               list_del_init(&se_cmd->se_cmd_list);
                spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
 
                cancel_work_sync(&se_cmd->work);
@@ -259,7 +258,7 @@ static void core_tmr_drain_tmr_list(
                        spin_unlock(&sess->sess_cmd_lock);
                        continue;
                }
-               if (sess->sess_tearing_down || cmd->cmd_wait_set) {
+               if (sess->sess_tearing_down) {
                        spin_unlock(&cmd->t_state_lock);
                        spin_unlock(&sess->sess_cmd_lock);
                        continue;
index 595b2f12ca6a79ee125373b6e60c6d80ad93e58c..087dddfc5c35da1645d38c5c7b6392b56e45d734 100644 (file)
@@ -235,8 +235,8 @@ void transport_init_session(struct se_session *se_sess)
        INIT_LIST_HEAD(&se_sess->sess_list);
        INIT_LIST_HEAD(&se_sess->sess_acl_list);
        INIT_LIST_HEAD(&se_sess->sess_cmd_list);
-       INIT_LIST_HEAD(&se_sess->sess_wait_list);
        spin_lock_init(&se_sess->sess_cmd_lock);
+       init_waitqueue_head(&se_sess->cmd_list_wq);
 }
 EXPORT_SYMBOL(transport_init_session);
 
@@ -2728,13 +2728,15 @@ static void target_release_cmd_kref(struct kref *kref)
        if (se_sess) {
                spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
                list_del_init(&se_cmd->se_cmd_list);
+               if (list_empty(&se_sess->sess_cmd_list))
+                       wake_up(&se_sess->cmd_list_wq);
 
                spin_lock(&se_cmd->t_state_lock);
                fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) &&
                              (se_cmd->transport_state & CMD_T_ABORTED);
                spin_unlock(&se_cmd->t_state_lock);
 
-               if (se_cmd->cmd_wait_set || fabric_stop) {
+               if (fabric_stop) {
                        spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
                        target_free_cmd_mem(se_cmd);
                        complete(&se_cmd->cmd_wait_comp);
@@ -2863,78 +2865,41 @@ void target_show_cmd(const char *pfx, struct se_cmd *cmd)
 EXPORT_SYMBOL(target_show_cmd);
 
 /**
- * target_sess_cmd_list_set_waiting - Flag all commands in
- *         sess_cmd_list to complete cmd_wait_comp.  Set
- *         sess_tearing_down so no more commands are queued.
+ * target_sess_cmd_list_set_waiting - Set sess_tearing_down so no new commands are queued.
  * @se_sess:   session to flag
  */
 void target_sess_cmd_list_set_waiting(struct se_session *se_sess)
 {
-       struct se_cmd *se_cmd, *tmp_cmd;
        unsigned long flags;
-       int rc;
 
        spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
-       if (se_sess->sess_tearing_down) {
-               spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
-               return;
-       }
        se_sess->sess_tearing_down = 1;
-       list_splice_init(&se_sess->sess_cmd_list, &se_sess->sess_wait_list);
-
-       list_for_each_entry_safe(se_cmd, tmp_cmd,
-                                &se_sess->sess_wait_list, se_cmd_list) {
-               rc = kref_get_unless_zero(&se_cmd->cmd_kref);
-               if (rc) {
-                       se_cmd->cmd_wait_set = 1;
-                       spin_lock(&se_cmd->t_state_lock);
-                       se_cmd->transport_state |= CMD_T_FABRIC_STOP;
-                       spin_unlock(&se_cmd->t_state_lock);
-               } else
-                       list_del_init(&se_cmd->se_cmd_list);
-       }
-
        spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
 }
 EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
 
 /**
- * target_wait_for_sess_cmds - Wait for outstanding descriptors
+ * target_wait_for_sess_cmds - Wait for outstanding commands
  * @se_sess:    session to wait for active I/O
  */
 void target_wait_for_sess_cmds(struct se_session *se_sess)
 {
-       struct se_cmd *se_cmd, *tmp_cmd;
-       unsigned long flags;
-       bool tas;
-
-       list_for_each_entry_safe(se_cmd, tmp_cmd,
-                               &se_sess->sess_wait_list, se_cmd_list) {
-               pr_debug("Waiting for se_cmd: %p t_state: %d, fabric state:"
-                       " %d\n", se_cmd, se_cmd->t_state,
-                       se_cmd->se_tfo->get_cmd_state(se_cmd));
-
-               spin_lock_irqsave(&se_cmd->t_state_lock, flags);
-               tas = (se_cmd->transport_state & CMD_T_TAS);
-               spin_unlock_irqrestore(&se_cmd->t_state_lock, flags);
-
-               if (!target_put_sess_cmd(se_cmd)) {
-                       if (tas)
-                               target_put_sess_cmd(se_cmd);
-               }
-
-               wait_for_completion(&se_cmd->cmd_wait_comp);
-               pr_debug("After cmd_wait_comp: se_cmd: %p t_state: %d"
-                       " fabric state: %d\n", se_cmd, se_cmd->t_state,
-                       se_cmd->se_tfo->get_cmd_state(se_cmd));
-
-               se_cmd->se_tfo->release_cmd(se_cmd);
-       }
-
-       spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
-       WARN_ON(!list_empty(&se_sess->sess_cmd_list));
-       spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+       struct se_cmd *cmd;
+       int ret;
 
+       WARN_ON_ONCE(!se_sess->sess_tearing_down);
+
+       spin_lock_irq(&se_sess->sess_cmd_lock);
+       do {
+               ret = wait_event_interruptible_lock_irq_timeout(
+                               se_sess->cmd_list_wq,
+                               list_empty(&se_sess->sess_cmd_list),
+                               se_sess->sess_cmd_lock, 180 * HZ);
+               list_for_each_entry(cmd, &se_sess->sess_cmd_list, se_cmd_list)
+                       target_show_cmd("session shutdown: still waiting for ",
+                                       cmd);
+       } while (ret <= 0);
+       spin_unlock_irq(&se_sess->sess_cmd_lock);
 }
 EXPORT_SYMBOL(target_wait_for_sess_cmds);
 
index 448f291125c275718df73376833b1f12d9c81dfb..906c1fc485e425fb5adbf8afdba6f497340ad9d9 100644 (file)
@@ -443,7 +443,6 @@ struct se_cmd {
        u8                      scsi_asc;
        u8                      scsi_ascq;
        u16                     scsi_sense_length;
-       unsigned                cmd_wait_set:1;
        unsigned                unknown_data_length:1;
        bool                    state_active:1;
        u64                     tag; /* SAM command identifier aka task tag */
@@ -606,8 +605,8 @@ struct se_session {
        struct list_head        sess_list;
        struct list_head        sess_acl_list;
        struct list_head        sess_cmd_list;
-       struct list_head        sess_wait_list;
        spinlock_t              sess_cmd_lock;
+       wait_queue_head_t       cmd_list_wq;
        void                    *sess_cmd_map;
        struct sbitmap_queue    sess_tag_pool;
 };