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>
This commit is contained in:
parent
8bb495e3f0
commit
6c182cd88d
|
@ -1561,7 +1561,6 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
again:
|
|
||||||
bdev = NULL;
|
bdev = NULL;
|
||||||
mode = 0;
|
mode = 0;
|
||||||
r = 0;
|
r = 0;
|
||||||
|
@ -1579,7 +1578,7 @@ again:
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
|
if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
|
||||||
r = -EAGAIN;
|
r = -ENOTCONN;
|
||||||
else if (!bdev)
|
else if (!bdev)
|
||||||
r = -EIO;
|
r = -EIO;
|
||||||
|
|
||||||
|
@ -1591,11 +1590,8 @@ again:
|
||||||
if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
|
if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT)
|
||||||
r = scsi_verify_blk_ioctl(NULL, cmd);
|
r = scsi_verify_blk_ioctl(NULL, cmd);
|
||||||
|
|
||||||
if (r == -EAGAIN && !fatal_signal_pending(current)) {
|
if (r == -ENOTCONN && !fatal_signal_pending(current))
|
||||||
queue_work(kmultipathd, &m->process_queued_ios);
|
queue_work(kmultipathd, &m->process_queued_ios);
|
||||||
msleep(10);
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
|
|
||||||
return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,10 +386,12 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
struct mapped_device *md = bdev->bd_disk->private_data;
|
struct mapped_device *md = bdev->bd_disk->private_data;
|
||||||
struct dm_table *map = dm_get_live_table(md);
|
struct dm_table *map;
|
||||||
struct dm_target *tgt;
|
struct dm_target *tgt;
|
||||||
int r = -ENOTTY;
|
int r = -ENOTTY;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
map = dm_get_live_table(md);
|
||||||
if (!map || !dm_table_get_size(map))
|
if (!map || !dm_table_get_size(map))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -410,6 +412,11 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
||||||
out:
|
out:
|
||||||
dm_table_put(map);
|
dm_table_put(map);
|
||||||
|
|
||||||
|
if (r == -ENOTCONN) {
|
||||||
|
msleep(10);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue