diff options
| author | SandeepKsinha <sandeepksinha@gmail.com> | 2009-06-17 18:49:35 -0400 |
|---|---|---|
| committer | NeilBrown <neilb@suse.de> | 2009-06-17 18:49:35 -0400 |
| commit | af11c397fd8835c70ec0bb777104e4ab98b2d660 (patch) | |
| tree | 59371fb1f955083f890dd6434e4c2f0ab9b7a97a | |
| parent | 0894cc3066aaa3e75a99383c0d25feebf9b688ac (diff) | |
md linear: Protecting mddev with rcu locks to avoid races
Due to the lack of memory ordering guarantees, we may have races around
mddev->conf.
In particular, the correct contents of the structure we get from
dereferencing ->private might not be visible to this CPU yet, and
they might not be correct w.r.t mddev->raid_disks.
This patch addresses the problem using rcu protection to avoid
such race conditions.
Signed-off-by: SandeepKsinha <sandeepksinha@gmail.com>
Signed-off-by: NeilBrown <neilb@suse.de>
| -rw-r--r-- | drivers/md/linear.c | 47 |
1 files changed, 38 insertions, 9 deletions
diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 564c390f8a1b..93f2b1d18398 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c | |||
| @@ -28,10 +28,11 @@ | |||
| 28 | static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector) | 28 | static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector) |
| 29 | { | 29 | { |
| 30 | int lo, mid, hi; | 30 | int lo, mid, hi; |
| 31 | linear_conf_t *conf = mddev->private; | 31 | linear_conf_t *conf; |
| 32 | 32 | ||
| 33 | lo = 0; | 33 | lo = 0; |
| 34 | hi = mddev->raid_disks - 1; | 34 | hi = mddev->raid_disks - 1; |
| 35 | conf = rcu_dereference(mddev->private); | ||
| 35 | 36 | ||
| 36 | /* | 37 | /* |
| 37 | * Binary Search | 38 | * Binary Search |
| @@ -66,8 +67,10 @@ static int linear_mergeable_bvec(struct request_queue *q, | |||
| 66 | unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9; | 67 | unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9; |
| 67 | sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); | 68 | sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); |
| 68 | 69 | ||
| 70 | rcu_read_lock(); | ||
| 69 | dev0 = which_dev(mddev, sector); | 71 | dev0 = which_dev(mddev, sector); |
| 70 | maxsectors = dev0->end_sector - sector; | 72 | maxsectors = dev0->end_sector - sector; |
| 73 | rcu_read_unlock(); | ||
| 71 | 74 | ||
| 72 | if (maxsectors < bio_sectors) | 75 | if (maxsectors < bio_sectors) |
| 73 | maxsectors = 0; | 76 | maxsectors = 0; |
| @@ -86,36 +89,50 @@ static int linear_mergeable_bvec(struct request_queue *q, | |||
| 86 | static void linear_unplug(struct request_queue *q) | 89 | static void linear_unplug(struct request_queue *q) |
| 87 | { | 90 | { |
| 88 | mddev_t *mddev = q->queuedata; | 91 | mddev_t *mddev = q->queuedata; |
| 89 | linear_conf_t *conf = mddev->private; | 92 | linear_conf_t *conf; |
| 90 | int i; | 93 | int i; |
| 91 | 94 | ||
| 95 | rcu_read_lock(); | ||
| 96 | conf = rcu_dereference(mddev->private); | ||
| 97 | |||
| 92 | for (i=0; i < mddev->raid_disks; i++) { | 98 | for (i=0; i < mddev->raid_disks; i++) { |
| 93 | struct request_queue *r_queue = bdev_get_queue(conf->disks[i].rdev->bdev); | 99 | struct request_queue *r_queue = bdev_get_queue(conf->disks[i].rdev->bdev); |
| 94 | blk_unplug(r_queue); | 100 | blk_unplug(r_queue); |
| 95 | } | 101 | } |
| 102 | rcu_read_unlock(); | ||
| 96 | } | 103 | } |
| 97 | 104 | ||
| 98 | static int linear_congested(void *data, int bits) | 105 | static int linear_congested(void *data, int bits) |
| 99 | { | 106 | { |
| 100 | mddev_t *mddev = data; | 107 | mddev_t *mddev = data; |
| 101 | linear_conf_t *conf = mddev->private; | 108 | linear_conf_t *conf; |
| 102 | int i, ret = 0; | 109 | int i, ret = 0; |
| 103 | 110 | ||
| 111 | rcu_read_lock(); | ||
| 112 | conf = rcu_dereference(mddev->private); | ||
| 113 | |||
| 104 | for (i = 0; i < mddev->raid_disks && !ret ; i++) { | 114 | for (i = 0; i < mddev->raid_disks && !ret ; i++) { |
| 105 | struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); | 115 | struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); |
| 106 | ret |= bdi_congested(&q->backing_dev_info, bits); | 116 | ret |= bdi_congested(&q->backing_dev_info, bits); |
| 107 | } | 117 | } |
| 118 | |||
| 119 | rcu_read_unlock(); | ||
| 108 | return ret; | 120 | return ret; |
| 109 | } | 121 | } |
| 110 | 122 | ||
| 111 | static sector_t linear_size(mddev_t *mddev, sector_t sectors, int raid_disks) | 123 | static sector_t linear_size(mddev_t *mddev, sector_t sectors, int raid_disks) |
| 112 | { | 124 | { |
| 113 | linear_conf_t *conf = mddev->private; | 125 | linear_conf_t *conf; |
| 126 | sector_t array_sectors; | ||
| 114 | 127 | ||
| 128 | rcu_read_lock(); | ||
| 129 | conf = rcu_dereference(mddev->private); | ||
| 115 | WARN_ONCE(sectors || raid_disks, | 130 | WARN_ONCE(sectors || raid_disks, |
| 116 | "%s does not support generic reshape\n", __func__); | 131 | "%s does not support generic reshape\n", __func__); |
| 132 | array_sectors = conf->array_sectors; | ||
| 133 | rcu_read_unlock(); | ||
| 117 | 134 | ||
| 118 | return conf->array_sectors; | 135 | return array_sectors; |
| 119 | } | 136 | } |
| 120 | 137 | ||
| 121 | static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) | 138 | static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) |
| @@ -229,8 +246,8 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 229 | return -ENOMEM; | 246 | return -ENOMEM; |
| 230 | 247 | ||
| 231 | newconf->prev = mddev->private; | 248 | newconf->prev = mddev->private; |
| 232 | mddev->private = newconf; | ||
| 233 | mddev->raid_disks++; | 249 | mddev->raid_disks++; |
| 250 | rcu_assign_pointer(mddev->private, newconf); | ||
| 234 | md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); | 251 | md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); |
| 235 | set_capacity(mddev->gendisk, mddev->array_sectors); | 252 | set_capacity(mddev->gendisk, mddev->array_sectors); |
| 236 | return 0; | 253 | return 0; |
| @@ -239,7 +256,13 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) | |||
| 239 | static int linear_stop (mddev_t *mddev) | 256 | static int linear_stop (mddev_t *mddev) |
| 240 | { | 257 | { |
| 241 | linear_conf_t *conf = mddev->private; | 258 | linear_conf_t *conf = mddev->private; |
| 242 | 259 | ||
| 260 | /* | ||
| 261 | * We do not require rcu protection here since | ||
| 262 | * we hold reconfig_mutex for both linear_add and | ||
| 263 | * linear_stop, so they cannot race. | ||
| 264 | */ | ||
| 265 | |||
| 243 | blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ | 266 | blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ |
| 244 | do { | 267 | do { |
| 245 | linear_conf_t *t = conf->prev; | 268 | linear_conf_t *t = conf->prev; |
| @@ -269,9 +292,11 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) | |||
| 269 | bio_sectors(bio)); | 292 | bio_sectors(bio)); |
| 270 | part_stat_unlock(); | 293 | part_stat_unlock(); |
| 271 | 294 | ||
| 295 | rcu_read_lock(); | ||
| 272 | tmp_dev = which_dev(mddev, bio->bi_sector); | 296 | tmp_dev = which_dev(mddev, bio->bi_sector); |
| 273 | start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; | 297 | start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; |
| 274 | 298 | ||
| 299 | |||
| 275 | if (unlikely(bio->bi_sector >= (tmp_dev->end_sector) | 300 | if (unlikely(bio->bi_sector >= (tmp_dev->end_sector) |
| 276 | || (bio->bi_sector < start_sector))) { | 301 | || (bio->bi_sector < start_sector))) { |
| 277 | char b[BDEVNAME_SIZE]; | 302 | char b[BDEVNAME_SIZE]; |
| @@ -282,6 +307,7 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) | |||
| 282 | bdevname(tmp_dev->rdev->bdev, b), | 307 | bdevname(tmp_dev->rdev->bdev, b), |
| 283 | (unsigned long long)tmp_dev->rdev->sectors, | 308 | (unsigned long long)tmp_dev->rdev->sectors, |
| 284 | (unsigned long long)start_sector); | 309 | (unsigned long long)start_sector); |
| 310 | rcu_read_unlock(); | ||
| 285 | bio_io_error(bio); | 311 | bio_io_error(bio); |
| 286 | return 0; | 312 | return 0; |
| 287 | } | 313 | } |
| @@ -291,9 +317,11 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) | |||
| 291 | * split it. | 317 | * split it. |
| 292 | */ | 318 | */ |
| 293 | struct bio_pair *bp; | 319 | struct bio_pair *bp; |
| 320 | sector_t end_sector = tmp_dev->end_sector; | ||
| 321 | |||
| 322 | rcu_read_unlock(); | ||
| 294 | 323 | ||
| 295 | bp = bio_split(bio, | 324 | bp = bio_split(bio, end_sector - bio->bi_sector); |
| 296 | tmp_dev->end_sector - bio->bi_sector); | ||
| 297 | 325 | ||
| 298 | if (linear_make_request(q, &bp->bio1)) | 326 | if (linear_make_request(q, &bp->bio1)) |
| 299 | generic_make_request(&bp->bio1); | 327 | generic_make_request(&bp->bio1); |
| @@ -306,6 +334,7 @@ static int linear_make_request (struct request_queue *q, struct bio *bio) | |||
| 306 | bio->bi_bdev = tmp_dev->rdev->bdev; | 334 | bio->bi_bdev = tmp_dev->rdev->bdev; |
| 307 | bio->bi_sector = bio->bi_sector - start_sector | 335 | bio->bi_sector = bio->bi_sector - start_sector |
| 308 | + tmp_dev->rdev->data_offset; | 336 | + tmp_dev->rdev->data_offset; |
| 337 | rcu_read_unlock(); | ||
| 309 | 338 | ||
| 310 | return 1; | 339 | return 1; |
| 311 | } | 340 | } |
