diff options
Diffstat (limited to 'drivers/md/dm-mpath.c')
-rw-r--r-- | drivers/md/dm-mpath.c | 47 |
1 files changed, 30 insertions, 17 deletions
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 754f38f8a692..638dae048b4f 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/workqueue.h> +#include <linux/delay.h> #include <scsi/scsi_dh.h> #include <linux/atomic.h> @@ -61,11 +62,11 @@ struct multipath { struct list_head list; struct dm_target *ti; - spinlock_t lock; - const char *hw_handler_name; char *hw_handler_params; + spinlock_t lock; + unsigned nr_priority_groups; struct list_head priority_groups; @@ -81,16 +82,17 @@ struct multipath { struct priority_group *next_pg; /* Switch to this PG if set */ unsigned repeat_count; /* I/Os left before calling PS again */ - unsigned queue_io; /* Must we queue all I/O? */ - unsigned queue_if_no_path; /* Queue I/O if last path fails? */ - unsigned saved_queue_if_no_path;/* Saved state during suspension */ + unsigned queue_io:1; /* Must we queue all I/O? */ + unsigned queue_if_no_path:1; /* Queue I/O if last path fails? */ + unsigned saved_queue_if_no_path:1; /* Saved state during suspension */ + unsigned pg_init_retries; /* Number of times to retry pg_init */ unsigned pg_init_count; /* Number of times pg_init called */ unsigned pg_init_delay_msecs; /* Number of msecs before pg_init retry */ + unsigned queue_size; struct work_struct process_queued_ios; struct list_head queued_ios; - unsigned queue_size; struct work_struct trigger_event; @@ -328,14 +330,18 @@ static void __choose_pgpath(struct multipath *m, size_t nr_bytes) /* * Loop through priority groups until we find a valid path. * First time we skip PGs marked 'bypassed'. - * Second time we only try the ones we skipped. + * Second time we only try the ones we skipped, but set + * pg_init_delay_retry so we do not hammer controllers. */ do { list_for_each_entry(pg, &m->priority_groups, list) { if (pg->bypassed == bypassed) continue; - if (!__choose_path_in_pg(m, pg, nr_bytes)) + if (!__choose_path_in_pg(m, pg, nr_bytes)) { + if (!bypassed) + m->pg_init_delay_retry = 1; return; + } } } while (bypassed--); @@ -481,9 +487,6 @@ static void process_queued_ios(struct work_struct *work) spin_lock_irqsave(&m->lock, flags); - if (!m->queue_size) - goto out; - if (!m->current_pgpath) __choose_pgpath(m, 0); @@ -496,7 +499,6 @@ static void process_queued_ios(struct work_struct *work) if (m->pg_init_required && !m->pg_init_in_progress && pgpath) __pg_init_all_paths(m); -out: spin_unlock_irqrestore(&m->lock, flags); if (!must_queue) dispatch_queued_ios(m); @@ -1517,11 +1519,16 @@ out: static int multipath_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) { - struct multipath *m = (struct multipath *) ti->private; - struct block_device *bdev = NULL; - fmode_t mode = 0; + struct multipath *m = ti->private; + struct block_device *bdev; + fmode_t mode; unsigned long flags; - int r = 0; + int r; + +again: + bdev = NULL; + mode = 0; + r = 0; spin_lock_irqsave(&m->lock, flags); @@ -1546,6 +1553,12 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd, if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) r = scsi_verify_blk_ioctl(NULL, cmd); + if (r == -EAGAIN && !fatal_signal_pending(current)) { + queue_work(kmultipathd, &m->process_queued_ios); + msleep(10); + goto again; + } + return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); } @@ -1643,7 +1656,7 @@ out: *---------------------------------------------------------------*/ static struct target_type multipath_target = { .name = "multipath", - .version = {1, 3, 0}, + .version = {1, 4, 0}, .module = THIS_MODULE, .ctr = multipath_ctr, .dtr = multipath_dtr, |