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 /drivers/md/linear.c | |
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>
Diffstat (limited to 'drivers/md/linear.c')
-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 | } |