Always keep the reference to structure block_device. Reference to block_device is obtained with function bdget_disk (and is released with bdput. bdget_disk was called while the device was being suspended, in dm_suspend() --- however at this point, there could be another devices already suspended. bdget_disk can wait for IO and allocate memory and this could result in waiting for already suspended device, resulting in deadlock. This patch changes the code so that it gets the reference to struct block_device when struct mapped_device is allocated and initialized in (alloc_dev) and drops the reference when it is destroyed in free_dev. Thus, there is no call to bdget_disk, while any device is suspended. Signed-off-by: Mikulas Patocka --- drivers/md/dm.c | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) Index: linux-2.6.29-rc1-devel/drivers/md/dm.c =================================================================== --- linux-2.6.29-rc1-devel.orig/drivers/md/dm.c 2009-01-15 15:37:54.000000000 +0100 +++ linux-2.6.29-rc1-devel/drivers/md/dm.c 2009-01-15 15:37:57.000000000 +0100 @@ -1149,6 +1149,10 @@ static struct mapped_device *alloc_dev(i if (!md->wq) goto bad_thread; + md->bdev = bdget_disk(md->disk, 0); + if (!md->bdev) + goto bad_bdev; + /* Populate the mapping, nobody knows we exist yet */ spin_lock(&_minor_lock); old_md = idr_replace(&_minor_idr, md, minor); @@ -1158,6 +1162,8 @@ static struct mapped_device *alloc_dev(i return md; +bad_bdev: + destroy_workqueue(md->wq); bad_thread: put_disk(md->disk); bad_disk: @@ -1183,10 +1189,8 @@ static void free_dev(struct mapped_devic { int minor = MINOR(disk_devt(md->disk)); - if (md->bdev) { - unlock_fs(md); - bdput(md->bdev); - } + unlock_fs(md); + bdput(md->bdev); destroy_workqueue(md->wq); mempool_destroy(md->tio_pool); mempool_destroy(md->io_pool); @@ -1245,8 +1249,7 @@ static int __bind(struct mapped_device * if (size != get_capacity(md->disk)) memset(&md->geometry, 0, sizeof(md->geometry)); - if (md->bdev) - __set_size(md, size); + __set_size(md, size); if (!size) { dm_table_destroy(t); @@ -1470,11 +1473,6 @@ int dm_swap_table(struct mapped_device * if (!dm_suspended(md)) goto out; - /* without bdev, the device size cannot be changed */ - if (!md->bdev) - if (get_capacity(md->disk) != dm_table_get_size(table)) - goto out; - __unbind(md); r = __bind(md, table); @@ -1554,13 +1552,6 @@ int dm_suspend(struct mapped_device *md, /* bdget() can stall if the pending I/Os are not flushed */ if (!noflush) { - md->bdev = bdget_disk(md->disk, 0); - if (!md->bdev) { - DMWARN("bdget failed in dm_suspend"); - r = -ENOMEM; - goto out; - } - /* * Flush I/O to the device. noflush supersedes do_lockfs, * because lock_fs() needs to flush I/Os. @@ -1610,11 +1601,6 @@ int dm_suspend(struct mapped_device *md, set_bit(DMF_SUSPENDED, &md->flags); out: - if (r && md->bdev) { - bdput(md->bdev); - md->bdev = NULL; - } - dm_table_put(map); out_unlock: @@ -1643,11 +1629,6 @@ int dm_resume(struct mapped_device *md) unlock_fs(md); - if (md->bdev) { - bdput(md->bdev); - md->bdev = NULL; - } - clear_bit(DMF_SUSPENDED, &md->flags); dm_table_unplug_all(map);