diff options
author | NeilBrown <neilb@suse.de> | 2007-04-04 22:08:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-04-05 00:12:47 -0400 |
commit | 5792a2856a63cdc568e08a7d6f9b2413d9217b3e (patch) | |
tree | 042a09fbfdf75dac5efa3bf61c7d6c6dd4c6ba7f | |
parent | 456a09dce9ca9b0013cabcda918aee851a04471d (diff) |
[PATCH] md: avoid a deadlock when removing a device from an md array via sysfs
A device can be removed from an md array via e.g.
echo remove > /sys/block/md3/md/dev-sde/state
This will try to remove the 'dev-sde' subtree which will deadlock
since
commit e7b0d26a86943370c04d6833c6edba2a72a6e240
With this patch we run the kobject_del via schedule_work so as to
avoid the deadlock.
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/md/md.c | 16 | ||||
-rw-r--r-- | include/linux/raid/md_k.h | 1 |
2 files changed, 16 insertions, 1 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 2a9b6a07e3a2..509171ca7fa8 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -1378,6 +1378,12 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) | |||
1378 | return err; | 1378 | return err; |
1379 | } | 1379 | } |
1380 | 1380 | ||
1381 | static void delayed_delete(struct work_struct *ws) | ||
1382 | { | ||
1383 | mdk_rdev_t *rdev = container_of(ws, mdk_rdev_t, del_work); | ||
1384 | kobject_del(&rdev->kobj); | ||
1385 | } | ||
1386 | |||
1381 | static void unbind_rdev_from_array(mdk_rdev_t * rdev) | 1387 | static void unbind_rdev_from_array(mdk_rdev_t * rdev) |
1382 | { | 1388 | { |
1383 | char b[BDEVNAME_SIZE]; | 1389 | char b[BDEVNAME_SIZE]; |
@@ -1390,7 +1396,12 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) | |||
1390 | printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); | 1396 | printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); |
1391 | rdev->mddev = NULL; | 1397 | rdev->mddev = NULL; |
1392 | sysfs_remove_link(&rdev->kobj, "block"); | 1398 | sysfs_remove_link(&rdev->kobj, "block"); |
1393 | kobject_del(&rdev->kobj); | 1399 | |
1400 | /* We need to delay this, otherwise we can deadlock when | ||
1401 | * writing to 'remove' to "dev/state" | ||
1402 | */ | ||
1403 | INIT_WORK(&rdev->del_work, delayed_delete); | ||
1404 | schedule_work(&rdev->del_work); | ||
1394 | } | 1405 | } |
1395 | 1406 | ||
1396 | /* | 1407 | /* |
@@ -3389,6 +3400,9 @@ static int do_md_stop(mddev_t * mddev, int mode) | |||
3389 | sysfs_remove_link(&mddev->kobj, nm); | 3400 | sysfs_remove_link(&mddev->kobj, nm); |
3390 | } | 3401 | } |
3391 | 3402 | ||
3403 | /* make sure all delayed_delete calls have finished */ | ||
3404 | flush_scheduled_work(); | ||
3405 | |||
3392 | export_array(mddev); | 3406 | export_array(mddev); |
3393 | 3407 | ||
3394 | mddev->array_size = 0; | 3408 | mddev->array_size = 0; |
diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 8245c282168b..de72c49747c8 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h | |||
@@ -104,6 +104,7 @@ struct mdk_rdev_s | |||
104 | * for reporting to userspace and storing | 104 | * for reporting to userspace and storing |
105 | * in superblock. | 105 | * in superblock. |
106 | */ | 106 | */ |
107 | struct work_struct del_work; /* used for delayed sysfs removal */ | ||
107 | }; | 108 | }; |
108 | 109 | ||
109 | struct mddev_s | 110 | struct mddev_s |