Commit 0909dc44 authored by NeilBrown's avatar NeilBrown

md: tidy up error paths in md_alloc

As the recent bug in md_alloc showed, having a single exit path for
unlocking and putting is a good idea.  So restructure md_alloc to have
a single mutex_unlock and mddev_put, and use gotos where necessary.
Found-by: default avatarJiri Slaby <jirislaby@gmail.com>
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
parent 1ec22eb2
...@@ -3846,11 +3846,9 @@ static int md_alloc(dev_t dev, char *name) ...@@ -3846,11 +3846,9 @@ static int md_alloc(dev_t dev, char *name)
flush_scheduled_work(); flush_scheduled_work();
mutex_lock(&disks_mutex); mutex_lock(&disks_mutex);
if (mddev->gendisk) { error = -EEXIST;
mutex_unlock(&disks_mutex); if (mddev->gendisk)
mddev_put(mddev); goto abort;
return -EEXIST;
}
if (name) { if (name) {
/* Need to ensure that 'name' is not a duplicate. /* Need to ensure that 'name' is not a duplicate.
...@@ -3862,19 +3860,15 @@ static int md_alloc(dev_t dev, char *name) ...@@ -3862,19 +3860,15 @@ static int md_alloc(dev_t dev, char *name)
if (mddev2->gendisk && if (mddev2->gendisk &&
strcmp(mddev2->gendisk->disk_name, name) == 0) { strcmp(mddev2->gendisk->disk_name, name) == 0) {
spin_unlock(&all_mddevs_lock); spin_unlock(&all_mddevs_lock);
mutex_unlock(&disks_mutex); goto abort;
mddev_put(mddev);
return -EEXIST;
} }
spin_unlock(&all_mddevs_lock); spin_unlock(&all_mddevs_lock);
} }
error = -ENOMEM;
mddev->queue = blk_alloc_queue(GFP_KERNEL); mddev->queue = blk_alloc_queue(GFP_KERNEL);
if (!mddev->queue) { if (!mddev->queue)
mutex_unlock(&disks_mutex); goto abort;
mddev_put(mddev);
return -ENOMEM;
}
mddev->queue->queuedata = mddev; mddev->queue->queuedata = mddev;
/* Can be unlocked because the queue is new: no concurrency */ /* Can be unlocked because the queue is new: no concurrency */
...@@ -3884,11 +3878,9 @@ static int md_alloc(dev_t dev, char *name) ...@@ -3884,11 +3878,9 @@ static int md_alloc(dev_t dev, char *name)
disk = alloc_disk(1 << shift); disk = alloc_disk(1 << shift);
if (!disk) { if (!disk) {
mutex_unlock(&disks_mutex);
blk_cleanup_queue(mddev->queue); blk_cleanup_queue(mddev->queue);
mddev->queue = NULL; mddev->queue = NULL;
mddev_put(mddev); goto abort;
return -ENOMEM;
} }
disk->major = MAJOR(mddev->unit); disk->major = MAJOR(mddev->unit);
disk->first_minor = unit << shift; disk->first_minor = unit << shift;
...@@ -3910,16 +3902,22 @@ static int md_alloc(dev_t dev, char *name) ...@@ -3910,16 +3902,22 @@ static int md_alloc(dev_t dev, char *name)
mddev->gendisk = disk; mddev->gendisk = disk;
error = kobject_init_and_add(&mddev->kobj, &md_ktype, error = kobject_init_and_add(&mddev->kobj, &md_ktype,
&disk_to_dev(disk)->kobj, "%s", "md"); &disk_to_dev(disk)->kobj, "%s", "md");
mutex_unlock(&disks_mutex); if (error) {
if (error) /* This isn't possible, but as kobject_init_and_add is marked
* __must_check, we must do something with the result
*/
printk(KERN_WARNING "md: cannot register %s/md - name in use\n", printk(KERN_WARNING "md: cannot register %s/md - name in use\n",
disk->disk_name); disk->disk_name);
else { error = 0;
}
abort:
mutex_unlock(&disks_mutex);
if (!error) {
kobject_uevent(&mddev->kobj, KOBJ_ADD); kobject_uevent(&mddev->kobj, KOBJ_ADD);
mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state"); mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state");
} }
mddev_put(mddev); mddev_put(mddev);
return 0; return error;
} }
static struct kobject *md_probe(dev_t dev, int *part, void *data) static struct kobject *md_probe(dev_t dev, int *part, void *data)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment