diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/md/md.c | 16 |
1 files changed, 15 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; |