diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2011-07-31 16:21:35 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2011-07-31 16:21:35 -0400 |
commit | 05eb0f252b04aa94ace0794f73d56c6a02351d80 (patch) | |
tree | 51c86862b31a5d2361b54658d0b6e14ed03c9afc /drivers/block/loop.c | |
parent | d134b00b9acca3fb054d7c88a5f5d562ecbb42d1 (diff) |
loop: fix deadlock when sysfs and LOOP_CLR_FD race against each other
LOOP_CLR_FD takes lo->lo_ctl_mutex and tries to remove the loop sysfs
files. Sysfs calls show() and waits for lo->lo_ctl_mutex. LOOP_CLR_FD
waits for show() to finish to remove the sysfs file.
cat /sys/class/block/loop0/loop/backing_file
mutex_lock_nested+0x176/0x350
? loop_attr_do_show_backing_file+0x2f/0xd0 [loop]
? loop_attr_do_show_backing_file+0x2f/0xd0 [loop]
loop_attr_do_show_backing_file+0x2f/0xd0 [loop]
dev_attr_show+0x1b/0x60
? sysfs_read_file+0x86/0x1a0
? __get_free_pages+0x12/0x50
sysfs_read_file+0xaf/0x1a0
ioctl(LOOP_CLR_FD):
wait_for_common+0x12c/0x180
? try_to_wake_up+0x2a0/0x2a0
wait_for_completion+0x18/0x20
sysfs_deactivate+0x178/0x180
? sysfs_addrm_finish+0x43/0x70
? sysfs_addrm_start+0x1d/0x20
sysfs_addrm_finish+0x43/0x70
sysfs_hash_and_remove+0x85/0xa0
sysfs_remove_group+0x59/0x100
loop_clr_fd+0x1dc/0x3f0 [loop]
lo_ioctl+0x223/0x7a0 [loop]
Instead of taking the lo_ctl_mutex from sysfs code, take the inner
lo->lo_lock, to protect the access to the backing_file data.
Thanks to Tejun for help debugging and finding a solution.
Cc: Milan Broz <mbroz@redhat.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Cc: stable@kernel.org
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'drivers/block/loop.c')
-rw-r--r-- | drivers/block/loop.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 3defc52f060c..4720c7ade0ae 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c | |||
@@ -743,10 +743,10 @@ static ssize_t loop_attr_backing_file_show(struct loop_device *lo, char *buf) | |||
743 | ssize_t ret; | 743 | ssize_t ret; |
744 | char *p = NULL; | 744 | char *p = NULL; |
745 | 745 | ||
746 | mutex_lock(&lo->lo_ctl_mutex); | 746 | spin_lock_irq(&lo->lo_lock); |
747 | if (lo->lo_backing_file) | 747 | if (lo->lo_backing_file) |
748 | p = d_path(&lo->lo_backing_file->f_path, buf, PAGE_SIZE - 1); | 748 | p = d_path(&lo->lo_backing_file->f_path, buf, PAGE_SIZE - 1); |
749 | mutex_unlock(&lo->lo_ctl_mutex); | 749 | spin_unlock_irq(&lo->lo_lock); |
750 | 750 | ||
751 | if (IS_ERR_OR_NULL(p)) | 751 | if (IS_ERR_OR_NULL(p)) |
752 | ret = PTR_ERR(p); | 752 | ret = PTR_ERR(p); |
@@ -1000,7 +1000,9 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) | |||
1000 | 1000 | ||
1001 | kthread_stop(lo->lo_thread); | 1001 | kthread_stop(lo->lo_thread); |
1002 | 1002 | ||
1003 | spin_lock_irq(&lo->lo_lock); | ||
1003 | lo->lo_backing_file = NULL; | 1004 | lo->lo_backing_file = NULL; |
1005 | spin_unlock_irq(&lo->lo_lock); | ||
1004 | 1006 | ||
1005 | loop_release_xfer(lo); | 1007 | loop_release_xfer(lo); |
1006 | lo->transfer = NULL; | 1008 | lo->transfer = NULL; |