diff options
author | Hannes Reinecke <hare@suse.de> | 2013-07-10 18:41:15 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2013-07-10 18:41:15 -0400 |
commit | 6c182cd88d179cbbd06f4f8a8a19b6977940753f (patch) | |
tree | 27971aeafbdd2410699e77477565157c67d8ffea /drivers/md | |
parent | 8bb495e3f02401ee6f76d1b1d77f3ac9f079e376 (diff) |
dm mpath: fix ioctl deadlock when no paths
When multipath needs to retry an ioctl the reference to the
current live table needs to be dropped. Otherwise a deadlock
occurs when all paths are down:
- dm_blk_ioctl takes a reference to the current table
and spins in multipath_ioctl().
- A new table is being loaded, but upon resume the process
hangs in dm_table_destroy() waiting for references to
drop to zero.
With this patch the reference to the old table is dropped
prior to retry, thereby avoiding the deadlock.
Signed-off-by: Hannes Reinecke <hare@suse.de>
Cc: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-mpath.c | 8 | ||||
-rw-r--r-- | drivers/md/dm.c | 9 |
2 files changed, 10 insertions, 7 deletions
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index bdf26f5bd326..5adede17ddf6 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c | |||
@@ -1561,7 +1561,6 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd, | |||
1561 | unsigned long flags; | 1561 | unsigned long flags; |
1562 | int r; | 1562 | int r; |
1563 | 1563 | ||
1564 | again: | ||
1565 | bdev = NULL; | 1564 | bdev = NULL; |
1566 | mode = 0; | 1565 | mode = 0; |
1567 | r = 0; | 1566 | r = 0; |
@@ -1579,7 +1578,7 @@ again: | |||
1579 | } | 1578 | } |
1580 | 1579 | ||
1581 | if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path)) | 1580 | if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path)) |
1582 | r = -EAGAIN; | 1581 | r = -ENOTCONN; |
1583 | else if (!bdev) | 1582 | else if (!bdev) |
1584 | r = -EIO; | 1583 | r = -EIO; |
1585 | 1584 | ||
@@ -1591,11 +1590,8 @@ again: | |||
1591 | if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) | 1590 | if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) |
1592 | r = scsi_verify_blk_ioctl(NULL, cmd); | 1591 | r = scsi_verify_blk_ioctl(NULL, cmd); |
1593 | 1592 | ||
1594 | if (r == -EAGAIN && !fatal_signal_pending(current)) { | 1593 | if (r == -ENOTCONN && !fatal_signal_pending(current)) |
1595 | queue_work(kmultipathd, &m->process_queued_ios); | 1594 | queue_work(kmultipathd, &m->process_queued_ios); |
1596 | msleep(10); | ||
1597 | goto again; | ||
1598 | } | ||
1599 | 1595 | ||
1600 | return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); | 1596 | return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
1601 | } | 1597 | } |
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index d5370a94b2c1..33f20103d8d5 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -386,10 +386,12 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, | |||
386 | unsigned int cmd, unsigned long arg) | 386 | unsigned int cmd, unsigned long arg) |
387 | { | 387 | { |
388 | struct mapped_device *md = bdev->bd_disk->private_data; | 388 | struct mapped_device *md = bdev->bd_disk->private_data; |
389 | struct dm_table *map = dm_get_live_table(md); | 389 | struct dm_table *map; |
390 | struct dm_target *tgt; | 390 | struct dm_target *tgt; |
391 | int r = -ENOTTY; | 391 | int r = -ENOTTY; |
392 | 392 | ||
393 | retry: | ||
394 | map = dm_get_live_table(md); | ||
393 | if (!map || !dm_table_get_size(map)) | 395 | if (!map || !dm_table_get_size(map)) |
394 | goto out; | 396 | goto out; |
395 | 397 | ||
@@ -410,6 +412,11 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, | |||
410 | out: | 412 | out: |
411 | dm_table_put(map); | 413 | dm_table_put(map); |
412 | 414 | ||
415 | if (r == -ENOTCONN) { | ||
416 | msleep(10); | ||
417 | goto retry; | ||
418 | } | ||
419 | |||
413 | return r; | 420 | return r; |
414 | } | 421 | } |
415 | 422 | ||