diff options
author | NeilBrown <neilb@suse.de> | 2006-06-26 03:27:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 12:58:37 -0400 |
commit | 7c7546ccf6463edbeee8d9aac6de7be1cd80d08a (patch) | |
tree | 56c1f734db9f8a66a7c8c5ef11fb35e07dbb2f91 /drivers/md/linear.c | |
parent | 5fd6c1dce06ec24ef3de20fe0c7ecf2ba9fe5ef9 (diff) |
[PATCH] md: allow a linear array to have drives added while active
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/md/linear.c')
-rw-r--r-- | drivers/md/linear.c | 74 |
1 files changed, 61 insertions, 13 deletions
diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 777585458c85..ff83c9b5979e 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c | |||
@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk, | |||
111 | return ret; | 111 | return ret; |
112 | } | 112 | } |
113 | 113 | ||
114 | static int linear_run (mddev_t *mddev) | 114 | static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) |
115 | { | 115 | { |
116 | linear_conf_t *conf; | 116 | linear_conf_t *conf; |
117 | dev_info_t **table; | 117 | dev_info_t **table; |
@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev) | |||
121 | sector_t curr_offset; | 121 | sector_t curr_offset; |
122 | struct list_head *tmp; | 122 | struct list_head *tmp; |
123 | 123 | ||
124 | conf = kzalloc (sizeof (*conf) + mddev->raid_disks*sizeof(dev_info_t), | 124 | conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t), |
125 | GFP_KERNEL); | 125 | GFP_KERNEL); |
126 | if (!conf) | 126 | if (!conf) |
127 | goto out; | 127 | return NULL; |
128 | |||
128 | mddev->private = conf; | 129 | mddev->private = conf; |
129 | 130 | ||
130 | cnt = 0; | 131 | cnt = 0; |
131 | mddev->array_size = 0; | 132 | conf->array_size = 0; |
132 | 133 | ||
133 | ITERATE_RDEV(mddev,rdev,tmp) { | 134 | ITERATE_RDEV(mddev,rdev,tmp) { |
134 | int j = rdev->raid_disk; | 135 | int j = rdev->raid_disk; |
135 | dev_info_t *disk = conf->disks + j; | 136 | dev_info_t *disk = conf->disks + j; |
136 | 137 | ||
137 | if (j < 0 || j > mddev->raid_disks || disk->rdev) { | 138 | if (j < 0 || j > raid_disks || disk->rdev) { |
138 | printk("linear: disk numbering problem. Aborting!\n"); | 139 | printk("linear: disk numbering problem. Aborting!\n"); |
139 | goto out; | 140 | goto out; |
140 | } | 141 | } |
@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev) | |||
152 | blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); | 153 | blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); |
153 | 154 | ||
154 | disk->size = rdev->size; | 155 | disk->size = rdev->size; |
155 | mddev->array_size += rdev->size; | 156 | conf->array_size += rdev->size; |
156 | 157 | ||
157 | cnt++; | 158 | cnt++; |
158 | } | 159 | } |
159 | if (cnt != mddev->raid_disks) { | 160 | if (cnt != raid_disks) { |
160 | printk("linear: not enough drives present. Aborting!\n"); | 161 | printk("linear: not enough drives present. Aborting!\n"); |
161 | goto out; | 162 | goto out; |
162 | } | 163 | } |
@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev) | |||
200 | unsigned round; | 201 | unsigned round; |
201 | unsigned long base; | 202 | unsigned long base; |
202 | 203 | ||
203 | sz = mddev->array_size >> conf->preshift; | 204 | sz = conf->array_size >> conf->preshift; |
204 | sz += 1; /* force round-up */ | 205 | sz += 1; /* force round-up */ |
205 | base = conf->hash_spacing >> conf->preshift; | 206 | base = conf->hash_spacing >> conf->preshift; |
206 | round = sector_div(sz, base); | 207 | round = sector_div(sz, base); |
@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev) | |||
247 | 248 | ||
248 | BUG_ON(table - conf->hash_table > nb_zone); | 249 | BUG_ON(table - conf->hash_table > nb_zone); |
249 | 250 | ||
251 | return conf; | ||
252 | |||
253 | out: | ||
254 | kfree(conf); | ||
255 | return NULL; | ||
256 | } | ||
257 | |||
258 | static int linear_run (mddev_t *mddev) | ||
259 | { | ||
260 | linear_conf_t *conf; | ||
261 | |||
262 | conf = linear_conf(mddev, mddev->raid_disks); | ||
263 | |||
264 | if (!conf) | ||
265 | return 1; | ||
266 | mddev->private = conf; | ||
267 | mddev->array_size = conf->array_size; | ||
268 | |||
250 | blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); | 269 | blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); |
251 | mddev->queue->unplug_fn = linear_unplug; | 270 | mddev->queue->unplug_fn = linear_unplug; |
252 | mddev->queue->issue_flush_fn = linear_issue_flush; | 271 | mddev->queue->issue_flush_fn = linear_issue_flush; |
253 | return 0; | 272 | return 0; |
273 | } | ||
254 | 274 | ||
255 | out: | 275 | static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) |
256 | kfree(conf); | 276 | { |
257 | return 1; | 277 | /* Adding a drive to a linear array allows the array to grow. |
278 | * It is permitted if the new drive has a matching superblock | ||
279 | * already on it, with raid_disk equal to raid_disks. | ||
280 | * It is achieved by creating a new linear_private_data structure | ||
281 | * and swapping it in in-place of the current one. | ||
282 | * The current one is never freed until the array is stopped. | ||
283 | * This avoids races. | ||
284 | */ | ||
285 | linear_conf_t *newconf; | ||
286 | |||
287 | if (rdev->raid_disk != mddev->raid_disks) | ||
288 | return -EINVAL; | ||
289 | |||
290 | newconf = linear_conf(mddev,mddev->raid_disks+1); | ||
291 | |||
292 | if (!newconf) | ||
293 | return -ENOMEM; | ||
294 | |||
295 | newconf->prev = mddev_to_conf(mddev); | ||
296 | mddev->private = newconf; | ||
297 | mddev->raid_disks++; | ||
298 | mddev->array_size = newconf->array_size; | ||
299 | set_capacity(mddev->gendisk, mddev->array_size << 1); | ||
300 | return 0; | ||
258 | } | 301 | } |
259 | 302 | ||
260 | static int linear_stop (mddev_t *mddev) | 303 | static int linear_stop (mddev_t *mddev) |
@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev) | |||
262 | linear_conf_t *conf = mddev_to_conf(mddev); | 305 | linear_conf_t *conf = mddev_to_conf(mddev); |
263 | 306 | ||
264 | blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ | 307 | blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ |
265 | kfree(conf->hash_table); | 308 | do { |
266 | kfree(conf); | 309 | linear_conf_t *t = conf->prev; |
310 | kfree(conf->hash_table); | ||
311 | kfree(conf); | ||
312 | conf = t; | ||
313 | } while (conf); | ||
267 | 314 | ||
268 | return 0; | 315 | return 0; |
269 | } | 316 | } |
@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality = | |||
360 | .run = linear_run, | 407 | .run = linear_run, |
361 | .stop = linear_stop, | 408 | .stop = linear_stop, |
362 | .status = linear_status, | 409 | .status = linear_status, |
410 | .hot_add_disk = linear_add, | ||
363 | }; | 411 | }; |
364 | 412 | ||
365 | static int __init linear_init (void) | 413 | static int __init linear_init (void) |