summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2009-06-18 08:49:42 +1000
committerNeilBrown <neilb@suse.de>2009-06-18 08:49:42 +1000
commit495d357301e1de01fabe30ce9a555301fb4675c3 (patch)
treecff9506eec5e4008004a913acf8f58d85eae6de6
parentaf11c397fd8835c70ec0bb777104e4ab98b2d660 (diff)
md/linear: use call_rcu to free obsolete 'conf' structures.
Current, when we update the 'conf' structure, when adding a drive to a linear array, we keep the old version around until the array is finally stopped, as it is not safe to free it immediately. Now that we have rcu protection on all accesses to 'conf', we can use call_rcu to free it more promptly. Signed-off-by: NeilBrown <neilb@suse.de>
-rw-r--r--drivers/md/linear.c21
-rw-r--r--drivers/md/linear.h2
2 files changed, 14 insertions, 9 deletions
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 93f2b1d1839..15c8b7b25a9 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -223,6 +223,12 @@ static int linear_run (mddev_t *mddev)
return 0;
}
+static void free_conf(struct rcu_head *head)
+{
+ linear_conf_t *conf = container_of(head, linear_conf_t, rcu);
+ kfree(conf);
+}
+
static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
{
/* Adding a drive to a linear array allows the array to grow.
@@ -233,7 +239,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
* The current one is never freed until the array is stopped.
* This avoids races.
*/
- linear_conf_t *newconf;
+ linear_conf_t *newconf, *oldconf;
if (rdev->saved_raid_disk != mddev->raid_disks)
return -EINVAL;
@@ -245,11 +251,12 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
if (!newconf)
return -ENOMEM;
- newconf->prev = mddev->private;
+ oldconf = rcu_dereference(mddev->private);
mddev->raid_disks++;
rcu_assign_pointer(mddev->private, newconf);
md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors);
+ call_rcu(&oldconf->rcu, free_conf);
return 0;
}
@@ -261,14 +268,12 @@ static int linear_stop (mddev_t *mddev)
* We do not require rcu protection here since
* we hold reconfig_mutex for both linear_add and
* linear_stop, so they cannot race.
+ * We should make sure any old 'conf's are properly
+ * freed though.
*/
-
+ rcu_barrier();
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
- do {
- linear_conf_t *t = conf->prev;
- kfree(conf);
- conf = t;
- } while (conf);
+ kfree(conf);
return 0;
}
diff --git a/drivers/md/linear.h b/drivers/md/linear.h
index 599e5c1bbb0..0ce29b61605 100644
--- a/drivers/md/linear.h
+++ b/drivers/md/linear.h
@@ -10,9 +10,9 @@ typedef struct dev_info dev_info_t;
struct linear_private_data
{
- struct linear_private_data *prev; /* earlier version */
sector_t array_sectors;
dev_info_t disks[0];
+ struct rcu_head rcu;
};