diff options
author | NeilBrown <neilb@suse.de> | 2009-01-08 16:31:10 -0500 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2009-01-08 16:31:10 -0500 |
commit | efeb53c0e57213e843b7ef3cc6ebcdea7d6186ac (patch) | |
tree | ef580effd8809fb0bc5d6ffa6f3b8abaa9180539 /drivers/md/md.c | |
parent | d3374825ce57ba2214d375023979f6197ccc1385 (diff) |
md: Allow md devices to be created by name.
Using sequential numbers to identify md devices is somewhat artificial.
Using names can be a lot more user-friendly.
Also, creating md devices by opening the device special file is a bit
awkward.
So this patch provides a new option for creating and naming devices.
Writing a name such as "md_home" to
/sys/modules/md_mod/parameters/new_array
will cause an array with that name to be created. It will appear in
/sys/block/ /proc/partitions and /proc/mdstat as 'md_home'.
It will have an arbitrary minor number allocated.
md devices that a created by an open are destroyed on the last
close when the device is inactive.
For named md devices, they will not be destroyed until the array
is explicitly stopped, either with the STOP_ARRAY ioctl or by
writing 'clear' to /sys/block/md_XXXX/md/array_state.
The name of the array must start 'md_' to avoid conflict with
other devices.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r-- | drivers/md/md.c | 119 |
1 files changed, 101 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); |