aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-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