aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/linear.c
diff options
context:
space:
mode:
authorSandeepKsinha <sandeepksinha@gmail.com>2009-06-17 18:49:35 -0400
committerNeilBrown <neilb@suse.de>2009-06-17 18:49:35 -0400
commitaf11c397fd8835c70ec0bb777104e4ab98b2d660 (patch)
tree59371fb1f955083f890dd6434e4c2f0ab9b7a97a /drivers/md/linear.c
parent0894cc3066aaa3e75a99383c0d25feebf9b688ac (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.c47
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 @@
28static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector) 28static 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,
86static void linear_unplug(struct request_queue *q) 89static 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
98static int linear_congested(void *data, int bits) 105static 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
111static sector_t linear_size(mddev_t *mddev, sector_t sectors, int raid_disks) 123static 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
121static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) 138static 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)
239static int linear_stop (mddev_t *mddev) 256static 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}