aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2009-06-17 18:49:42 -0400
committerNeilBrown <neilb@suse.de>2009-06-17 18:49:42 -0400
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 93f2b1d18398..15c8b7b25a9b 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -223,6 +223,12 @@ static int linear_run (mddev_t *mddev)
223 return 0; 223 return 0;
224} 224}
225 225
226static void free_conf(struct rcu_head *head)
227{
228 linear_conf_t *conf = container_of(head, linear_conf_t, rcu);
229 kfree(conf);
230}
231
226static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) 232static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
227{ 233{
228 /* Adding a drive to a linear array allows the array to grow. 234 /* 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)
233 * The current one is never freed until the array is stopped. 239 * The current one is never freed until the array is stopped.
234 * This avoids races. 240 * This avoids races.
235 */ 241 */
236 linear_conf_t *newconf; 242 linear_conf_t *newconf, *oldconf;
237 243
238 if (rdev->saved_raid_disk != mddev->raid_disks) 244 if (rdev->saved_raid_disk != mddev->raid_disks)
239 return -EINVAL; 245 return -EINVAL;
@@ -245,11 +251,12 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
245 if (!newconf) 251 if (!newconf)
246 return -ENOMEM; 252 return -ENOMEM;
247 253
248 newconf->prev = mddev->private; 254 oldconf = rcu_dereference(mddev->private);
249 mddev->raid_disks++; 255 mddev->raid_disks++;
250 rcu_assign_pointer(mddev->private, newconf); 256 rcu_assign_pointer(mddev->private, newconf);
251 md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 257 md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
252 set_capacity(mddev->gendisk, mddev->array_sectors); 258 set_capacity(mddev->gendisk, mddev->array_sectors);
259 call_rcu(&oldconf->rcu, free_conf);
253 return 0; 260 return 0;
254} 261}
255 262
@@ -261,14 +268,12 @@ static int linear_stop (mddev_t *mddev)
261 * We do not require rcu protection here since 268 * We do not require rcu protection here since
262 * we hold reconfig_mutex for both linear_add and 269 * we hold reconfig_mutex for both linear_add and
263 * linear_stop, so they cannot race. 270 * linear_stop, so they cannot race.
271 * We should make sure any old 'conf's are properly
272 * freed though.
264 */ 273 */
265 274 rcu_barrier();
266 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ 275 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
267 do { 276 kfree(conf);
268 linear_conf_t *t = conf->prev;
269 kfree(conf);
270 conf = t;
271 } while (conf);
272 277
273 return 0; 278 return 0;
274} 279}
diff --git a/drivers/md/linear.h b/drivers/md/linear.h
index 599e5c1bbb01..0ce29b61605a 100644
--- a/drivers/md/linear.h
+++ b/drivers/md/linear.h
@@ -10,9 +10,9 @@ typedef struct dev_info dev_info_t;
10 10
11struct linear_private_data 11struct linear_private_data
12{ 12{
13 struct linear_private_data *prev; /* earlier version */
14 sector_t array_sectors; 13 sector_t array_sectors;
15 dev_info_t disks[0]; 14 dev_info_t disks[0];
15 struct rcu_head rcu;
16}; 16};
17 17
18 18