diff options
-rw-r--r-- | drivers/md/md.c | 119 | ||||
-rw-r--r-- | include/linux/raid/md_k.h | 1 |
2 files changed, 102 insertions, 18 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 38697283aaf4..f5cbb9d2371a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -249,17 +249,51 @@ static mddev_t * mddev_find(dev_t unit) | |||
249 | 249 | ||
250 | retry: | 250 | retry: |
251 | spin_lock(&all_mddevs_lock); | 251 | spin_lock(&all_mddevs_lock); |
252 | list_for_each_entry(mddev, &all_mddevs, all_mddevs) | 252 | |
253 | if (mddev->unit == unit) { | 253 | if (unit) { |
254 | mddev_get(mddev); | 254 | list_for_each_entry(mddev, &all_mddevs, all_mddevs) |
255 | if (mddev->unit == unit) { | ||
256 | mddev_get(mddev); | ||
257 | spin_unlock(&all_mddevs_lock); | ||
258 | kfree(new); | ||
259 | return mddev; | ||
260 | } | ||
261 | |||
262 | if (new) { | ||
263 | list_add(&new->all_mddevs, &all_mddevs); | ||
255 | spin_unlock(&all_mddevs_lock); | 264 | spin_unlock(&all_mddevs_lock); |
256 | kfree(new); | 265 | new->hold_active = UNTIL_IOCTL; |
257 | return mddev; | 266 | return new; |
258 | } | 267 | } |
259 | 268 | } else if (new) { | |
260 | if (new) { | 269 | /* find an unused unit number */ |
270 | static int next_minor = 512; | ||
271 | int start = next_minor; | ||
272 | int is_free = 0; | ||
273 | int dev = 0; | ||
274 | while (!is_free) { | ||
275 | dev = MKDEV(MD_MAJOR, next_minor); | ||
276 | next_minor++; | ||
277 | if (next_minor > MINORMASK) | ||
278 | next_minor = 0; | ||
279 | if (next_minor == start) { | ||
280 | /* Oh dear, all in use. */ | ||
281 | spin_unlock(&all_mddevs_lock); | ||
282 | kfree(new); | ||
283 | return NULL; | ||
284 | } | ||
285 | |||
286 | is_free = 1; | ||
287 | list_for_each_entry(mddev, &all_mddevs, all_mddevs) | ||
288 | if (mddev->unit == dev) { | ||
289 | is_free = 0; | ||
290 | break; | ||
291 | } | ||
292 | } | ||
293 | new->unit = dev; | ||
294 | new->md_minor = MINOR(dev); | ||
295 | new->hold_active = UNTIL_STOP; | ||
261 | list_add(&new->all_mddevs, &all_mddevs); | 296 | list_add(&new->all_mddevs, &all_mddevs); |
262 | mddev->hold_active = UNTIL_IOCTL; | ||
263 | spin_unlock(&all_mddevs_lock); | 297 | spin_unlock(&all_mddevs_lock); |
264 | return new; | 298 | return new; |
265 | } | 299 | } |
@@ -3491,18 +3525,22 @@ static struct kobj_type md_ktype = { | |||
3491 | 3525 | ||
3492 | int mdp_major = 0; | 3526 | int mdp_major = 0; |
3493 | 3527 | ||
3494 | static struct kobject *md_probe(dev_t dev, int *part, void *data) | 3528 | static int md_alloc(dev_t dev, char *name) |
3495 | { | 3529 | { |
3496 | static DEFINE_MUTEX(disks_mutex); | 3530 | static DEFINE_MUTEX(disks_mutex); |
3497 | mddev_t *mddev = mddev_find(dev); | 3531 | mddev_t *mddev = mddev_find(dev); |
3498 | struct gendisk *disk; | 3532 | struct gendisk *disk; |
3499 | int partitioned = (MAJOR(dev) != MD_MAJOR); | 3533 | int partitioned; |
3500 | int shift = partitioned ? MdpMinorShift : 0; | 3534 | int shift; |
3501 | int unit = MINOR(dev) >> shift; | 3535 | int unit; |
3502 | int error; | 3536 | int error; |
3503 | 3537 | ||
3504 | if (!mddev) | 3538 | if (!mddev) |
3505 | return NULL; | 3539 | return -ENODEV; |
3540 | |||
3541 | partitioned = (MAJOR(mddev->unit) != MD_MAJOR); | ||
3542 | shift = partitioned ? MdpMinorShift : 0; | ||
3543 | unit = MINOR(mddev->unit) >> shift; | ||
3506 | 3544 | ||
3507 | /* wait for any previous instance if this device | 3545 | /* wait for any previous instance if this device |
3508 | * to be completed removed (mddev_delayed_delete). | 3546 | * to be completed removed (mddev_delayed_delete). |
@@ -3513,14 +3551,29 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) | |||
3513 | if (mddev->gendisk) { | 3551 | if (mddev->gendisk) { |
3514 | mutex_unlock(&disks_mutex); | 3552 | mutex_unlock(&disks_mutex); |
3515 | mddev_put(mddev); | 3553 | mddev_put(mddev); |
3516 | return NULL; | 3554 | return -EEXIST; |
3555 | } | ||
3556 | |||
3557 | if (name) { | ||
3558 | /* Need to ensure that 'name' is not a duplicate. | ||
3559 | */ | ||
3560 | mddev_t *mddev2; | ||
3561 | spin_lock(&all_mddevs_lock); | ||
3562 | |||
3563 | list_for_each_entry(mddev2, &all_mddevs, all_mddevs) | ||
3564 | if (mddev2->gendisk && | ||
3565 | strcmp(mddev2->gendisk->disk_name, name) == 0) { | ||
3566 | spin_unlock(&all_mddevs_lock); | ||
3567 | return -EEXIST; | ||
3568 | } | ||
3569 | spin_unlock(&all_mddevs_lock); | ||
3517 | } | 3570 | } |
3518 | 3571 | ||
3519 | mddev->queue = blk_alloc_queue(GFP_KERNEL); | 3572 | mddev->queue = blk_alloc_queue(GFP_KERNEL); |
3520 | if (!mddev->queue) { | 3573 | if (!mddev->queue) { |
3521 | mutex_unlock(&disks_mutex); | 3574 | mutex_unlock(&disks_mutex); |
3522 | mddev_put(mddev); | 3575 | mddev_put(mddev); |
3523 | return NULL; | 3576 | return -ENOMEM; |
3524 | } | 3577 | } |
3525 | /* Can be unlocked because the queue is new: no concurrency */ | 3578 | /* Can be unlocked because the queue is new: no concurrency */ |
3526 | queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue); | 3579 | queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue); |
@@ -3533,11 +3586,13 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) | |||
3533 | blk_cleanup_queue(mddev->queue); | 3586 | blk_cleanup_queue(mddev->queue); |
3534 | mddev->queue = NULL; | 3587 | mddev->queue = NULL; |
3535 | mddev_put(mddev); | 3588 | mddev_put(mddev); |
3536 | return NULL; | 3589 | return -ENOMEM; |
3537 | } | 3590 | } |
3538 | disk->major = MAJOR(dev); | 3591 | disk->major = MAJOR(mddev->unit); |
3539 | disk->first_minor = unit << shift; | 3592 | disk->first_minor = unit << shift; |
3540 | if (partitioned) | 3593 | if (name) |
3594 | strcpy(disk->disk_name, name); | ||
3595 | else if (partitioned) | ||
3541 | sprintf(disk->disk_name, "md_d%d", unit); | 3596 | sprintf(disk->disk_name, "md_d%d", unit); |
3542 | else | 3597 | else |
3543 | sprintf(disk->disk_name, "md%d", unit); | 3598 | sprintf(disk->disk_name, "md%d", unit); |
@@ -3562,9 +3617,34 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) | |||
3562 | mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state"); | 3617 | mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state"); |
3563 | } | 3618 | } |
3564 | mddev_put(mddev); | 3619 | mddev_put(mddev); |
3620 | return 0; | ||
3621 | } | ||
3622 | |||
3623 | static struct kobject *md_probe(dev_t dev, int *part, void *data) | ||
3624 | { | ||
3625 | md_alloc(dev, NULL); | ||
3565 | return NULL; | 3626 | return NULL; |
3566 | } | 3627 | } |
3567 | 3628 | ||
3629 | static int add_named_array(const char *val, struct kernel_param *kp) | ||
3630 | { | ||
3631 | /* val must be "md_*" where * is not all digits. | ||
3632 | * We allocate an array with a large free minor number, and | ||
3633 | * set the name to val. val must not already be an active name. | ||
3634 | */ | ||
3635 | int len = strlen(val); | ||
3636 | char buf[DISK_NAME_LEN]; | ||
3637 | |||
3638 | while (len && val[len-1] == '\n') | ||
3639 | len--; | ||
3640 | if (len >= DISK_NAME_LEN) | ||
3641 | return -E2BIG; | ||
3642 | strlcpy(buf, val, len+1); | ||
3643 | if (strncmp(buf, "md_", 3) != 0) | ||
3644 | return -EINVAL; | ||
3645 | return md_alloc(0, buf); | ||
3646 | } | ||
3647 | |||
3568 | static void md_safemode_timeout(unsigned long data) | 3648 | static void md_safemode_timeout(unsigned long data) |
3569 | { | 3649 | { |
3570 | mddev_t *mddev = (mddev_t *) data; | 3650 | mddev_t *mddev = (mddev_t *) data; |
@@ -4025,6 +4105,8 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) | |||
4025 | mddev->barriers_work = 0; | 4105 | mddev->barriers_work = 0; |
4026 | mddev->safemode = 0; | 4106 | mddev->safemode = 0; |
4027 | kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); | 4107 | kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); |
4108 | if (mddev->hold_active == UNTIL_STOP) | ||
4109 | mddev->hold_active = 0; | ||
4028 | 4110 | ||
4029 | } else if (mddev->pers) | 4111 | } else if (mddev->pers) |
4030 | printk(KERN_INFO "md: %s switched to read-only mode.\n", | 4112 | printk(KERN_INFO "md: %s switched to read-only mode.\n", |
@@ -6502,6 +6584,7 @@ static int set_ro(const char *val, struct kernel_param *kp) | |||
6502 | module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR); | 6584 | module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR); |
6503 | module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR); | 6585 | module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR); |
6504 | 6586 | ||
6587 | module_param_call(new_array, add_named_array, NULL, NULL, S_IWUSR); | ||
6505 | 6588 | ||
6506 | EXPORT_SYMBOL(register_md_personality); | 6589 | EXPORT_SYMBOL(register_md_personality); |
6507 | EXPORT_SYMBOL(unregister_md_personality); | 6590 | EXPORT_SYMBOL(unregister_md_personality); |
diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index e3d17c7f954e..dac4217194b8 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h | |||
@@ -139,6 +139,7 @@ struct mddev_s | |||
139 | struct kobject kobj; | 139 | struct kobject kobj; |
140 | int hold_active; | 140 | int hold_active; |
141 | #define UNTIL_IOCTL 1 | 141 | #define UNTIL_IOCTL 1 |
142 | #define UNTIL_STOP 2 | ||
142 | 143 | ||
143 | /* Superblock information */ | 144 | /* Superblock information */ |
144 | int major_version, | 145 | int major_version, |