diff options
Diffstat (limited to 'drivers/md/linear.c')
-rw-r--r-- | drivers/md/linear.c | 39 |
1 files changed, 34 insertions, 5 deletions
diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 86f5d435901d..b0c0aef92a37 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c | |||
@@ -52,18 +52,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) | |||
52 | return conf->disks + lo; | 52 | return conf->disks + lo; |
53 | } | 53 | } |
54 | 54 | ||
55 | /* | ||
56 | * In linear_congested() conf->raid_disks is used as a copy of | ||
57 | * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks | ||
58 | * and conf->disks[] are created in linear_conf(), they are always | ||
59 | * consitent with each other, but mddev->raid_disks does not. | ||
60 | */ | ||
55 | static int linear_congested(struct mddev *mddev, int bits) | 61 | static int linear_congested(struct mddev *mddev, int bits) |
56 | { | 62 | { |
57 | struct linear_conf *conf; | 63 | struct linear_conf *conf; |
58 | int i, ret = 0; | 64 | int i, ret = 0; |
59 | 65 | ||
60 | conf = mddev->private; | 66 | rcu_read_lock(); |
67 | conf = rcu_dereference(mddev->private); | ||
61 | 68 | ||
62 | for (i = 0; i < mddev->raid_disks && !ret ; i++) { | 69 | for (i = 0; i < conf->raid_disks && !ret ; i++) { |
63 | struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); | 70 | struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); |
64 | ret |= bdi_congested(&q->backing_dev_info, bits); | 71 | ret |= bdi_congested(&q->backing_dev_info, bits); |
65 | } | 72 | } |
66 | 73 | ||
74 | rcu_read_unlock(); | ||
67 | return ret; | 75 | return ret; |
68 | } | 76 | } |
69 | 77 | ||
@@ -143,6 +151,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) | |||
143 | conf->disks[i-1].end_sector + | 151 | conf->disks[i-1].end_sector + |
144 | conf->disks[i].rdev->sectors; | 152 | conf->disks[i].rdev->sectors; |
145 | 153 | ||
154 | /* | ||
155 | * conf->raid_disks is copy of mddev->raid_disks. The reason to | ||
156 | * keep a copy of mddev->raid_disks in struct linear_conf is, | ||
157 | * mddev->raid_disks may not be consistent with pointers number of | ||
158 | * conf->disks[] when it is updated in linear_add() and used to | ||
159 | * iterate old conf->disks[] earray in linear_congested(). | ||
160 | * Here conf->raid_disks is always consitent with number of | ||
161 | * pointers in conf->disks[] array, and mddev->private is updated | ||
162 | * with rcu_assign_pointer() in linear_addr(), such race can be | ||
163 | * avoided. | ||
164 | */ | ||
165 | conf->raid_disks = raid_disks; | ||
166 | |||
146 | return conf; | 167 | return conf; |
147 | 168 | ||
148 | out: | 169 | out: |
@@ -195,15 +216,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) | |||
195 | if (!newconf) | 216 | if (!newconf) |
196 | return -ENOMEM; | 217 | return -ENOMEM; |
197 | 218 | ||
219 | /* newconf->raid_disks already keeps a copy of * the increased | ||
220 | * value of mddev->raid_disks, WARN_ONCE() is just used to make | ||
221 | * sure of this. It is possible that oldconf is still referenced | ||
222 | * in linear_congested(), therefore kfree_rcu() is used to free | ||
223 | * oldconf until no one uses it anymore. | ||
224 | */ | ||
198 | mddev_suspend(mddev); | 225 | mddev_suspend(mddev); |
199 | oldconf = mddev->private; | 226 | oldconf = rcu_dereference(mddev->private); |
200 | mddev->raid_disks++; | 227 | mddev->raid_disks++; |
201 | mddev->private = newconf; | 228 | WARN_ONCE(mddev->raid_disks != newconf->raid_disks, |
229 | "copied raid_disks doesn't match mddev->raid_disks"); | ||
230 | rcu_assign_pointer(mddev->private, newconf); | ||
202 | md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); | 231 | md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); |
203 | set_capacity(mddev->gendisk, mddev->array_sectors); | 232 | set_capacity(mddev->gendisk, mddev->array_sectors); |
204 | mddev_resume(mddev); | 233 | mddev_resume(mddev); |
205 | revalidate_disk(mddev->gendisk); | 234 | revalidate_disk(mddev->gendisk); |
206 | kfree(oldconf); | 235 | kfree_rcu(oldconf, rcu); |
207 | return 0; | 236 | return 0; |
208 | } | 237 | } |
209 | 238 | ||