浏览代码

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>
NeilBrown 16 年之前
父节点
当前提交
495d357301
共有 2 个文件被更改,包括 14 次插入9 次删除
  1. 13 8
      drivers/md/linear.c
  2. 1 1
      drivers/md/linear.h

+ 13 - 8
drivers/md/linear.c

@@ -223,6 +223,12 @@ static int linear_run (mddev_t *mddev)
 	return 0;
 	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)
 static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
 {
 {
 	/* Adding a drive to a linear array allows the array to grow.
 	/* 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.
 	 * The current one is never freed until the array is stopped.
 	 * This avoids races.
 	 * This avoids races.
 	 */
 	 */
-	linear_conf_t *newconf;
+	linear_conf_t *newconf, *oldconf;
 
 
 	if (rdev->saved_raid_disk != mddev->raid_disks)
 	if (rdev->saved_raid_disk != mddev->raid_disks)
 		return -EINVAL;
 		return -EINVAL;
@@ -245,11 +251,12 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
 	if (!newconf)
 	if (!newconf)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	newconf->prev = mddev->private;
+	oldconf = rcu_dereference(mddev->private);
 	mddev->raid_disks++;
 	mddev->raid_disks++;
 	rcu_assign_pointer(mddev->private, newconf);
 	rcu_assign_pointer(mddev->private, newconf);
 	md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
 	md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
 	set_capacity(mddev->gendisk, mddev->array_sectors);
 	set_capacity(mddev->gendisk, mddev->array_sectors);
+	call_rcu(&oldconf->rcu, free_conf);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -261,14 +268,12 @@ static int linear_stop (mddev_t *mddev)
 	 * We do not require rcu protection here since
 	 * We do not require rcu protection here since
 	 * we hold reconfig_mutex for both linear_add and
 	 * we hold reconfig_mutex for both linear_add and
 	 * linear_stop, so they cannot race.
 	 * 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'*/
 	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;
 	return 0;
 }
 }

+ 1 - 1
drivers/md/linear.h

@@ -10,9 +10,9 @@ typedef struct dev_info dev_info_t;
 
 
 struct linear_private_data
 struct linear_private_data
 {
 {
-	struct linear_private_data *prev;	/* earlier version */
 	sector_t		array_sectors;
 	sector_t		array_sectors;
 	dev_info_t		disks[0];
 	dev_info_t		disks[0];
+	struct rcu_head		rcu;
 };
 };