Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Hannes Reinecke and committed by
Alasdair G Kergon
6c182cd8 8bb495e3

+10 -7
+2 -6
drivers/md/dm-mpath.c
··· 1561 1561 unsigned long flags; 1562 1562 int r; 1563 1563 1564 - again: 1565 1564 bdev = NULL; 1566 1565 mode = 0; 1567 1566 r = 0; ··· 1578 1579 } 1579 1580 1580 1581 if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path)) 1581 - r = -EAGAIN; 1582 + r = -ENOTCONN; 1582 1583 else if (!bdev) 1583 1584 r = -EIO; 1584 1585 ··· 1590 1591 if (!r && ti->len != i_size_read(bdev->bd_inode) >> SECTOR_SHIFT) 1591 1592 r = scsi_verify_blk_ioctl(NULL, cmd); 1592 1593 1593 - if (r == -EAGAIN && !fatal_signal_pending(current)) { 1594 + if (r == -ENOTCONN && !fatal_signal_pending(current)) 1594 1595 queue_work(kmultipathd, &m->process_queued_ios); 1595 - msleep(10); 1596 - goto again; 1597 - } 1598 1596 1599 1597 return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg); 1600 1598 }
+8 -1
drivers/md/dm.c
··· 386 386 unsigned int cmd, unsigned long arg) 387 387 { 388 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 390 struct dm_target *tgt; 391 391 int r = -ENOTTY; 392 392 393 + retry: 394 + map = dm_get_live_table(md); 393 395 if (!map || !dm_table_get_size(map)) 394 396 goto out; 395 397 ··· 411 409 412 410 out: 413 411 dm_table_put(map); 412 + 413 + if (r == -ENOTCONN) { 414 + msleep(10); 415 + goto retry; 416 + } 414 417 415 418 return r; 416 419 }