Skip to content

Commit

Permalink
dm mpath: fix ioctl deadlock when no paths
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Hannes Reinecke authored and Alasdair G Kergon committed Jul 10, 2013
1 parent 8bb495e commit 6c182cd
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 7 deletions.
8 changes: 2 additions & 6 deletions drivers/md/dm-mpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -1561,7 +1561,6 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
unsigned long flags;
int r;

again:
bdev = NULL;
mode = 0;
r = 0;
Expand All @@ -1579,7 +1578,7 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
}

if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
r = -EAGAIN;
r = -ENOTCONN;
else if (!bdev)
r = -EIO;

Expand All @@ -1591,11 +1590,8 @@ 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)) {
if (r == -ENOTCONN && !fatal_signal_pending(current))
queue_work(kmultipathd, &m->process_queued_ios);
msleep(10);
goto again;
}

return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
}
Expand Down
9 changes: 8 additions & 1 deletion drivers/md/dm.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,12 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
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;
int r = -ENOTTY;

retry:
map = dm_get_live_table(md);
if (!map || !dm_table_get_size(map))
goto out;

Expand All @@ -410,6 +412,11 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
out:
dm_table_put(map);

if (r == -ENOTCONN) {
msleep(10);
goto retry;
}

return r;
}

Expand Down

0 comments on commit 6c182cd

Please sign in to comment.