diff options
Diffstat (limited to 'drivers/md/linear.c')
-rw-r--r-- | drivers/md/linear.c | 21 |
1 files changed, 13 insertions, 8 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 | ||
226 | static 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 | |||
226 | static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) | 232 | static 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 | } |