]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/md/dm-mpath.c
Merge tag 'block-5.6-2020-02-05' of git://git.kernel.dk/linux-block
[linux.git] / drivers / md / dm-mpath.c
index e0c32793c24872a07cd504202447fcd102056369..2bc18c9c3abcfc60e0763976704c878aea7fd13f 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/delay.h>
 #include <scsi/scsi_dh.h>
@@ -29,6 +30,9 @@
 #define DM_MSG_PREFIX "multipath"
 #define DM_PG_INIT_DELAY_MSECS 2000
 #define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
+#define QUEUE_IF_NO_PATH_TIMEOUT_DEFAULT 0
+
+static unsigned long queue_if_no_path_timeout_secs = QUEUE_IF_NO_PATH_TIMEOUT_DEFAULT;
 
 /* Path properties */
 struct pgpath {
@@ -91,6 +95,8 @@ struct multipath {
 
        struct work_struct process_queued_bios;
        struct bio_list queued_bios;
+
+       struct timer_list nopath_timer; /* Timeout for queue_if_no_path */
 };
 
 /*
@@ -108,6 +114,7 @@ static void trigger_event(struct work_struct *work);
 static void activate_or_offline_path(struct pgpath *pgpath);
 static void activate_path_work(struct work_struct *work);
 static void process_queued_bios(struct work_struct *work);
+static void queue_if_no_path_timeout_work(struct timer_list *t);
 
 /*-----------------------------------------------
  * Multipath state flags.
@@ -195,6 +202,8 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
 
                m->ti = ti;
                ti->private = m;
+
+               timer_setup(&m->nopath_timer, queue_if_no_path_timeout_work, 0);
        }
 
        return m;
@@ -717,6 +726,43 @@ static int queue_if_no_path(struct multipath *m, bool queue_if_no_path,
        return 0;
 }
 
+/*
+ * If the queue_if_no_path timeout fires, turn off queue_if_no_path and
+ * process any queued I/O.
+ */
+static void queue_if_no_path_timeout_work(struct timer_list *t)
+{
+       struct multipath *m = from_timer(m, t, nopath_timer);
+       struct mapped_device *md = dm_table_get_md(m->ti->table);
+
+       DMWARN("queue_if_no_path timeout on %s, failing queued IO", dm_device_name(md));
+       queue_if_no_path(m, false, false);
+}
+
+/*
+ * Enable the queue_if_no_path timeout if necessary.
+ * Called with m->lock held.
+ */
+static void enable_nopath_timeout(struct multipath *m)
+{
+       unsigned long queue_if_no_path_timeout =
+               READ_ONCE(queue_if_no_path_timeout_secs) * HZ;
+
+       lockdep_assert_held(&m->lock);
+
+       if (queue_if_no_path_timeout > 0 &&
+           atomic_read(&m->nr_valid_paths) == 0 &&
+           test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) {
+               mod_timer(&m->nopath_timer,
+                         jiffies + queue_if_no_path_timeout);
+       }
+}
+
+static void disable_nopath_timeout(struct multipath *m)
+{
+       del_timer_sync(&m->nopath_timer);
+}
+
 /*
  * An event is triggered whenever a path is taken out of use.
  * Includes path failure and PG bypass.
@@ -1090,6 +1136,7 @@ static int multipath_ctr(struct dm_target *ti, unsigned argc, char **argv)
        struct dm_arg_set as;
        unsigned pg_count = 0;
        unsigned next_pg_num;
+       unsigned long flags;
 
        as.argc = argc;
        as.argv = argv;
@@ -1154,6 +1201,10 @@ static int multipath_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto bad;
        }
 
+       spin_lock_irqsave(&m->lock, flags);
+       enable_nopath_timeout(m);
+       spin_unlock_irqrestore(&m->lock, flags);
+
        ti->num_flush_bios = 1;
        ti->num_discard_bios = 1;
        ti->num_write_same_bios = 1;
@@ -1208,6 +1259,7 @@ static void multipath_dtr(struct dm_target *ti)
 {
        struct multipath *m = ti->private;
 
+       disable_nopath_timeout(m);
        flush_multipath_work(m);
        free_multipath(m);
 }
@@ -1241,6 +1293,8 @@ static int fail_path(struct pgpath *pgpath)
 
        schedule_work(&m->trigger_event);
 
+       enable_nopath_timeout(m);
+
 out:
        spin_unlock_irqrestore(&m->lock, flags);
 
@@ -1291,6 +1345,9 @@ static int reinstate_path(struct pgpath *pgpath)
                process_queued_io_list(m);
        }
 
+       if (pgpath->is_active)
+               disable_nopath_timeout(m);
+
        return r;
 }
 
@@ -1444,7 +1501,7 @@ static void pg_init_done(void *data, int errors)
                break;
        case SCSI_DH_RETRY:
                /* Wait before retrying. */
-               delay_retry = 1;
+               delay_retry = true;
                /* fall through */
        case SCSI_DH_IMM_RETRY:
        case SCSI_DH_RES_TEMP_UNAVAIL:
@@ -1789,6 +1846,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv,
        struct dm_dev *dev;
        struct multipath *m = ti->private;
        action_fn action;
+       unsigned long flags;
 
        mutex_lock(&m->work_mutex);
 
@@ -1800,9 +1858,13 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv,
        if (argc == 1) {
                if (!strcasecmp(argv[0], "queue_if_no_path")) {
                        r = queue_if_no_path(m, true, false);
+                       spin_lock_irqsave(&m->lock, flags);
+                       enable_nopath_timeout(m);
+                       spin_unlock_irqrestore(&m->lock, flags);
                        goto out;
                } else if (!strcasecmp(argv[0], "fail_if_no_path")) {
                        r = queue_if_no_path(m, false, false);
+                       disable_nopath_timeout(m);
                        goto out;
                }
        }
@@ -2065,6 +2127,10 @@ static void __exit dm_multipath_exit(void)
 module_init(dm_multipath_init);
 module_exit(dm_multipath_exit);
 
+module_param_named(queue_if_no_path_timeout_secs,
+                  queue_if_no_path_timeout_secs, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(queue_if_no_path_timeout_secs, "No available paths queue IO timeout in seconds");
+
 MODULE_DESCRIPTION(DM_NAME " multipath target");
 MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
 MODULE_LICENSE("GPL");