Commit 62da9e6b authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] md.c

AAAARGH.  OK, I've got it to work with the current tree, but...  I don't
like the lifetime rules - both old and new ones.  Summary:

	* we create a gendisk upon attempt to open or upon successful
	  attempt to run (which _can_ happen without open).
	* we never remove them until the rmmod (or ever).
	* mddev is held by gendisk.

The source of trouble is our API - ioctls on possibly unconfigured
devices when no IO is possible, potentially setting up this (or another,
or many other) devices.  I _think_ we can get the old rules (mddev
exists when somebody holds it open or when it runs or when somebody is
looking at it funny), but the way I see will take more messing with
driverfs objects than I want right now. 

Notice that current API is, was and will be ripe with interesting races
- it really ought to be replaced with something saner, but that would
take serious modifications of userland tools.  I'll talk with Neil about
possible solution, this patch just gets it back to life. 
parent 6dc95b4c
......@@ -200,6 +200,7 @@ static mddev_t * mddev_find(int unit)
INIT_LIST_HEAD(&new->disks);
INIT_LIST_HEAD(&new->all_mddevs);
atomic_set(&new->active, 1);
blk_queue_make_request(&new->queue, md_fail_request);
goto retry;
}
......@@ -1275,6 +1276,39 @@ static int device_size_calculation(mddev_t * mddev)
return 1;
}
static struct gendisk *md_probe(dev_t dev, int *part, void *data)
{
static DECLARE_MUTEX(disks_sem);
int unit = MINOR(dev);
mddev_t *mddev = mddev_find(unit);
struct gendisk *disk;
if (!mddev)
return NULL;
down(&disks_sem);
if (disks[unit]) {
up(&disks_sem);
mddev_put(mddev);
return NULL;
}
disk = alloc_disk(1);
if (!disk) {
up(&disks_sem);
mddev_put(mddev);
return NULL;
}
disk->major = MD_MAJOR;
disk->first_minor = mdidx(mddev);
sprintf(disk->disk_name, "md%d", mdidx(mddev));
disk->fops = &md_fops;
disk->private_data = mddev;
disk->queue = &mddev->queue;
add_disk(disk);
disks[mdidx(mddev)] = disk;
up(&disks_sem);
return NULL;
}
#define TOO_BIG_CHUNKSIZE KERN_ERR \
"too big chunk_size: %d > %d\n"
......@@ -1394,14 +1428,10 @@ static int do_md_run(mddev_t * mddev)
#endif
}
disk = alloc_disk(1);
md_probe(mdidx(mddev), NULL, NULL);
disk = disks[mdidx(mddev)];
if (!disk)
return -ENOMEM;
disk->major = MD_MAJOR;
disk->first_minor = mdidx(mddev);
sprintf(disk->disk_name, "md%d", mdidx(mddev));
disk->fops = &md_fops;
mddev->pers = pers[pnum];
blk_queue_make_request(&mddev->queue, mddev->pers->make_request);
......@@ -1417,7 +1447,6 @@ static int do_md_run(mddev_t * mddev)
if (err) {
printk(KERN_ERR "md: pers->run() failed ...\n");
mddev->pers = NULL;
put_disk(disk);
return -EINVAL;
}
......@@ -1430,9 +1459,6 @@ static int do_md_run(mddev_t * mddev)
md_update_sb(mddev);
md_recover_arrays();
set_capacity(disk, md_size[mdidx(mddev)]<<1);
add_disk(disk);
disks[mdidx(mddev)] = disk;
return (0);
}
......@@ -1543,13 +1569,8 @@ static int do_md_stop(mddev_t * mddev, int ro)
md_size[mdidx(mddev)] = 0;
disk = disks[mdidx(mddev)];
disks[mdidx(mddev)] = NULL;
if (disk) {
del_gendisk(disk);
put_disk(disk);
}
if (disk)
set_capacity(disk, 0);
} else
printk(KERN_INFO "md: md%d switched to read-only mode.\n", mdidx(mddev));
err = 0;
......@@ -3083,18 +3104,6 @@ static void md_geninit(void)
#endif
}
request_queue_t * md_queue_proc(kdev_t dev)
{
mddev_t *mddev = mddev_find(minor(dev));
request_queue_t *q = BLK_DEFAULT_QUEUE(MAJOR_NR);
if (mddev) {
if (mddev->pers)
q = &mddev->queue;
mddev_put(mddev);
}
return q;
}
int __init md_init(void)
{
static char * name = "mdrecoveryd";
......@@ -3109,6 +3118,8 @@ int __init md_init(void)
return (-1);
}
devfs_handle = devfs_mk_dir (NULL, "md", NULL);
blk_register_region(MKDEV(MAJOR_NR, 0), MAX_MD_DEVS, THIS_MODULE,
md_probe, NULL, NULL);
for (minor=0; minor < MAX_MD_DEVS; ++minor) {
char devname[128];
sprintf (devname, "%u", minor);
......@@ -3117,10 +3128,6 @@ int __init md_init(void)
S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL);
}
/* all requests on an uninitialised device get failed... */
blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), md_fail_request);
blk_dev[MAJOR_NR].queue = md_queue_proc;
md_recovery_thread = md_register_thread(md_do_recovery, NULL, name);
if (!md_recovery_thread)
printk(KERN_ALERT
......@@ -3447,6 +3454,8 @@ int init_module(void)
void cleanup_module(void)
{
int i;
blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS);
md_unregister_thread(md_recovery_thread);
devfs_unregister(devfs_handle);
......@@ -3456,8 +3465,16 @@ void cleanup_module(void)
#ifdef CONFIG_PROC_FS
remove_proc_entry("mdstat", NULL);
#endif
blk_dev[MAJOR_NR].queue = NULL;
for (i = 0; i < MAX_MD_DEVS; i++) {
struct gendisk *disk = disks[i];
mddev_t *mddev;
if (!disks[i])
continue;
mddev = disk->private_data;
del_gendisk(disk);
put_disk(disk);
mddev_put(mddev);
}
}
#endif
......
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