aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-12-07 23:49:46 -0500
committerNeilBrown <neilb@suse.de>2011-12-07 23:49:46 -0500
commitaf8a24347f966ab9fae6b0f127d1fbc9d6932d3a (patch)
tree903afdae95dc11444707410d784ad857dfb887c9 /drivers
parent1d23f178d56ae1349b4fc5108ac8f4f8cdc92afc (diff)
md: take a reference to mddev during sysfs access.
When we are accessing an mddev via sysfs we know that the mddev cannot disappear because it has an embedded kobj which is refcounted by sysfs. And we also take the mddev_lock. However this is not enough. The final mddev_put could have been called and the mddev_delayed_delete is waiting for sysfs to let go so it can destroy the kobj and mddev. In this state there are a lot of changes that should not be attempted. To to guard against this we: - initialise mddev->all_mddevs in on last put so the state can be easily detected. - in md_attr_show and md_attr_store, check ->all_mddevs under all_mddevs_lock and mddev_get the mddev if it still appears to be active. This means that if we get to sysfs as the mddev is being deleted we will get -EBUSY. rdev_attr_store and rdev_attr_show are similar but already have sufficient protection. They check that rdev->mddev still points to mddev after taking mddev_lock. As this is cleared before delayed removal which can only be requested under the mddev_lock, this ensure the rdev and mddev are still alive. Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/md/md.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 93b0da13350..f97c3b2f2f8 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -570,7 +570,7 @@ static void mddev_put(struct mddev *mddev)
570 mddev->ctime == 0 && !mddev->hold_active) { 570 mddev->ctime == 0 && !mddev->hold_active) {
571 /* Array is not configured at all, and not held active, 571 /* Array is not configured at all, and not held active,
572 * so destroy it */ 572 * so destroy it */
573 list_del(&mddev->all_mddevs); 573 list_del_init(&mddev->all_mddevs);
574 bs = mddev->bio_set; 574 bs = mddev->bio_set;
575 mddev->bio_set = NULL; 575 mddev->bio_set = NULL;
576 if (mddev->gendisk) { 576 if (mddev->gendisk) {
@@ -4489,11 +4489,20 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
4489 4489
4490 if (!entry->show) 4490 if (!entry->show)
4491 return -EIO; 4491 return -EIO;
4492 spin_lock(&all_mddevs_lock);
4493 if (list_empty(&mddev->all_mddevs)) {
4494 spin_unlock(&all_mddevs_lock);
4495 return -EBUSY;
4496 }
4497 mddev_get(mddev);
4498 spin_unlock(&all_mddevs_lock);
4499
4492 rv = mddev_lock(mddev); 4500 rv = mddev_lock(mddev);
4493 if (!rv) { 4501 if (!rv) {
4494 rv = entry->show(mddev, page); 4502 rv = entry->show(mddev, page);
4495 mddev_unlock(mddev); 4503 mddev_unlock(mddev);
4496 } 4504 }
4505 mddev_put(mddev);
4497 return rv; 4506 return rv;
4498} 4507}
4499 4508
@@ -4509,11 +4518,19 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
4509 return -EIO; 4518 return -EIO;
4510 if (!capable(CAP_SYS_ADMIN)) 4519 if (!capable(CAP_SYS_ADMIN))
4511 return -EACCES; 4520 return -EACCES;
4521 spin_lock(&all_mddevs_lock);
4522 if (list_empty(&mddev->all_mddevs)) {
4523 spin_unlock(&all_mddevs_lock);
4524 return -EBUSY;
4525 }
4526 mddev_get(mddev);
4527 spin_unlock(&all_mddevs_lock);
4512 rv = mddev_lock(mddev); 4528 rv = mddev_lock(mddev);
4513 if (!rv) { 4529 if (!rv) {
4514 rv = entry->store(mddev, page, length); 4530 rv = entry->store(mddev, page, length);
4515 mddev_unlock(mddev); 4531 mddev_unlock(mddev);
4516 } 4532 }
4533 mddev_put(mddev);
4517 return rv; 4534 return rv;
4518} 4535}
4519 4536