patch-2.4.0-test2 linux/drivers/block/md.c

Next file: linux/drivers/block/nbd.c
Previous file: linux/drivers/block/loop.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test1/linux/drivers/block/md.c linux/drivers/block/md.c
@@ -22,10 +22,10 @@
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
    any later version.
-   
+
    You should have received a copy of the GNU General Public License
    (for example /usr/src/linux/COPYING); if not, write to the Free
-   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #include <linux/config.h>
@@ -43,7 +43,7 @@
 #include <asm/unaligned.h>
 
 extern asmlinkage int sys_sched_yield(void);
-extern asmlinkage int sys_setsid(void);
+extern asmlinkage long sys_setsid(void);
 
 #define MAJOR_NR MD_MAJOR
 #define MD_DRIVER
@@ -258,7 +258,7 @@
 
 	/*
 	 * The 'base' mddev is the one with data NULL.
-	 * personalities can create additional mddevs 
+	 * personalities can create additional mddevs
 	 * if necessary.
 	 */
 	add_mddev_mapping(mddev, dev, 0);
@@ -327,11 +327,13 @@
 	 * ok, add this new device name to the list
 	 */
 	hd = find_gendisk (dev);
-
-	if (!hd)
-		sprintf (dname->name, "[dev %s]", kdevname(dev));
-	else
-		disk_name (hd, MINOR(dev), dname->name);
+	dname->name = NULL;
+	if (hd)
+		dname->name = disk_name (hd, MINOR(dev), dname->namebuf);
+	if (!dname->name) {
+		sprintf (dname->namebuf, "[dev %s]", kdevname(dev));
+		dname->name = dname->namebuf;
+	}
 
 	dname->dev = dev;
 	MD_INIT_LIST_HEAD(&dname->list);
@@ -435,7 +437,7 @@
 			if (rdev->desc_nr == i)
 				c++;
 		}
-		if (c == 0) {
+		if (!c) {
 			printk("md: md%d, missing disk #%d, aborting.\n",
 				mdidx(mddev), i);
 			goto abort;
@@ -1010,7 +1012,7 @@
 skip:
 	return 0;
 }
-#undef GETBLK_FAILED KERN_ERR
+#undef GETBLK_FAILED 
 
 static void set_this_disk(mddev_t *mddev, mdk_rdev_t *rdev)
 {
@@ -1425,7 +1427,7 @@
 				break;
 			}
 		}
-		if (found) 
+		if (found)
 			continue;
 
 		printk("md%d: former device %s is unavailable, removing from array!\n", mdidx(mddev), partition_name(dev));
@@ -1525,7 +1527,7 @@
 	 * Do device size calculation. Bail out if too small.
 	 * (we have to do this after having validated chunk_size,
 	 * because device size has to be modulo chunk_size)
-	 */ 
+	 */
 	persistent = !mddev->sb->not_persistent;
 	ITERATE_RDEV(mddev,rdev,tmp) {
 		if (rdev->faulty)
@@ -1576,7 +1578,7 @@
 	readahead = MD_READAHEAD;
 	if ((sb->level == 0) || (sb->level == 4) || (sb->level == 5))
 		readahead = mddev->sb->chunk_size * 4 * data_disks;
-		if (readahead < data_disks * MAX_SECTORS*512*2) 
+		if (readahead < data_disks * MAX_SECTORS*512*2)
 			readahead = data_disks * MAX_SECTORS*512*2;
 	else {
 		if (sb->level == -3)
@@ -1617,7 +1619,7 @@
 		MD_BUG();
 		return -EINVAL;
 	}
-  
+
 	if (mddev->pers)
 		return -EBUSY;
 
@@ -1629,7 +1631,7 @@
 
 	/*
 	 * Analyze all RAID superblock(s)
-	 */ 
+	 */
 	if (analyze_sbs(mddev)) {
 		MD_BUG();
 		return -EINVAL;
@@ -1683,7 +1685,7 @@
 #endif
 			return -EINVAL;
 	}
-  
+
 	if (device_size_calculation(mddev))
 		return -EINVAL;
 
@@ -1698,9 +1700,9 @@
 		fsync_dev(rdev->dev);
 		invalidate_buffers(rdev->dev);
 	}
-  
+
 	mddev->pers = pers[pnum];
-  
+
 	err = mddev->pers->run(mddev);
 	if (err) {
 		printk("pers->run() failed ...\n");
@@ -1717,7 +1719,7 @@
 	 */
 	md_hd_struct[mdidx(mddev)].start_sect = 0;
 	md_hd_struct[mdidx(mddev)].nr_sects = md_size[mdidx(mddev)] << 1;
-  
+
 	read_ahead[MD_MAJOR] = 1024;
 	return (0);
 }
@@ -1730,7 +1732,7 @@
 static int restart_array (mddev_t *mddev)
 {
 	int err = 0;
- 
+
 	/*
 	 * Complain if it has no devices
 	 */
@@ -1754,7 +1756,7 @@
 			mddev->pers->restart_resync(mddev);
 	} else
 		err = -EINVAL;
- 
+
 out:
 	return err;
 }
@@ -1766,12 +1768,12 @@
 {
 	int err = 0, resync_interrupted = 0;
 	kdev_t dev = mddev_to_kdev(mddev);
- 
+
 	if (!ro && get_super(dev)) {
 		printk (STILL_MOUNTED, mdidx(mddev));
 		OUT(-EBUSY);
 	}
- 
+
 	if (mddev->pers) {
 		/*
 		 * It is safe to call stop here, it only frees private
@@ -1833,14 +1835,14 @@
 		if (ro)
 			set_device_ro(dev, 1);
 	}
- 
+
 	/*
 	 * Free resources if final stop
 	 */
 	if (!ro) {
+		printk (KERN_INFO "md%d stopped.\n", mdidx(mddev));
 		free_mddev(mddev);
 
-		printk (KERN_INFO "md%d stopped.\n", mdidx(mddev));
 	} else
 		printk (KERN_INFO
 			"md%d switched to read-only mode.\n", mdidx(mddev));
@@ -2069,14 +2071,25 @@
 
 } raid_setup_args md__initdata = { 0, 0 };
 
+void md_setup_drive(void) md__init;
+
 /*
  * Searches all registered partitions for autorun RAID arrays
  * at boot time.
  */
-void md__init autodetect_raid(void)
+#ifdef CONFIG_AUTODETECT_RAID
+static int detected_devices[128] md__initdata;
+static int dev_cnt md__initdata=0;
+void md__init md_autodetect_dev(kdev_t dev)
+{
+	if (dev_cnt < 127)
+		detected_devices[dev_cnt++] = dev;
+}
+#endif
+
+void md__init md_run_setup(void)
 {
 #ifdef CONFIG_AUTODETECT_RAID
-	struct gendisk *disk;
 	mdk_rdev_t *rdev;
 	int i;
 
@@ -2086,36 +2099,35 @@
 	}
 	printk(KERN_INFO "autodetecting RAID arrays\n");
 
-	for (disk = gendisk_head ; disk ; disk = disk->next) {
-		for (i = 0; i < disk->max_p*disk->nr_real; i++) {
-			kdev_t dev = MKDEV(disk->major,i);
+	for (i=0; i<dev_cnt; i++) {
+		kdev_t dev = detected_devices[i];
 
-			if (disk->part[i].type != LINUX_RAID_PARTITION)
-				continue;
-
-			if (md_import_device(dev,1)) {
-				printk(KERN_ALERT "could not import %s!\n",
-							partition_name(dev));
-				continue;
-			}
-			/*
-			 * Sanity checks:
-			 */
-			rdev = find_rdev_all(dev);
-			if (!rdev) {
-				MD_BUG();
-				continue;
-			}
-			if (rdev->faulty) {
-				MD_BUG();
-				continue;
-			}
-			md_list_add(&rdev->pending, &pending_raid_disks);
+		if (md_import_device(dev,1)) {
+			printk(KERN_ALERT "could not import %s!\n",
+			       partition_name(dev));
+			continue;
+		}
+		/*
+		 * Sanity checks:
+		 */
+		rdev = find_rdev_all(dev);
+		if (!rdev) {
+			MD_BUG();
+			continue;
 		}
+		if (rdev->faulty) {
+			MD_BUG();
+			continue;
+		}
+		md_list_add(&rdev->pending, &pending_raid_disks);
 	}
 
 	autorun_devices();
 #endif
+#ifdef CONFIG_MD_BOOT
+	md_setup_drive();
+#endif
+	
 }
 
 static int get_version (void * arg)
@@ -2196,41 +2208,62 @@
 }
 #undef SET_FROM_SB
 
-#define SET_SB(x) mddev->sb->disks[nr].x = info.x
+#define SET_SB(x) mddev->sb->disks[nr].x = info->x
 
-static int add_new_disk (mddev_t * mddev, void * arg)
+static int add_new_disk (mddev_t * mddev, mdu_disk_info_t *info)
 {
 	int err, size, persistent;
-	mdu_disk_info_t info;
 	mdk_rdev_t *rdev;
 	unsigned int nr;
 	kdev_t dev;
-
-	if (!mddev->sb)
-		return -EINVAL;
-
-	if (md_copy_from_user(&info, arg, sizeof(info)))
-		return -EFAULT;
-
-	nr = info.number;
-	if (nr >= mddev->sb->nr_disks)
-		return -EINVAL;
-
-	dev = MKDEV(info.major,info.minor);
+	dev = MKDEV(info->major,info->minor);
 
 	if (find_rdev_all(dev)) {
-		printk("device %s already used in a RAID array!\n", 
+		printk("device %s already used in a RAID array!\n",
 				partition_name(dev));
 		return -EBUSY;
 	}
+	if (!mddev->sb) {
+		/* expecting a device which has a superblock */
+		err = md_import_device(dev, 1);
+		if (err) {
+			printk("md error, md_import_device returned %d\n", err);
+			return -EINVAL;
+		}
+		rdev = find_rdev_all(dev);
+		if (!rdev) {
+			MD_BUG();
+			return -EINVAL;
+		}
+		if (mddev->nb_dev) {
+			mdk_rdev_t *rdev0 = md_list_entry(mddev->disks.next,
+							  mdk_rdev_t, same_set);
+			if (!uuid_equal(rdev0, rdev)) {
+				printk("md: %s has different UUID to %s\n", partition_name(rdev->dev), partition_name(rdev0->dev));
+				export_rdev(rdev);
+				return -EINVAL;
+			}
+			if (!sb_equal(rdev0->sb, rdev->sb)) {
+				printk("md: %s has same UUID but different superblock to %s\n", partition_name(rdev->dev), partition_name(rdev0->dev));
+				export_rdev(rdev);
+				return -EINVAL;
+			}
+		}
+		bind_rdev_to_array(rdev, mddev);
+		return 0;
+	}
+
+	nr = info->number;
+	if (nr >= mddev->sb->nr_disks)
+		return -EINVAL;
 
 	SET_SB(number);
 	SET_SB(major);
 	SET_SB(minor);
 	SET_SB(raid_disk);
 	SET_SB(state);
- 
-	if ((info.state & (1<<MD_DISK_FAULTY))==0) {
+
+	if ((info->state & (1<<MD_DISK_FAULTY))==0) {
 		err = md_import_device (dev, 0);
 		if (err) {
 			printk("md: error, md_import_device() returned %d\n", err);
@@ -2241,25 +2274,25 @@
 			MD_BUG();
 			return -EINVAL;
 		}
- 
+
 		rdev->old_dev = dev;
-		rdev->desc_nr = info.number;
- 
+		rdev->desc_nr = info->number;
+
 		bind_rdev_to_array(rdev, mddev);
- 
+
 		persistent = !mddev->sb->not_persistent;
 		if (!persistent)
 			printk("nonpersistent superblock ...\n");
 		if (!mddev->sb->chunk_size)
 			printk("no chunksize?\n");
- 
+
 		size = calc_dev_size(dev, mddev, persistent);
 		rdev->sb_offset = calc_dev_sboffset(dev, mddev, persistent);
- 
+
 		if (!mddev->sb->size || (mddev->sb->size > size))
 			mddev->sb->size = size;
 	}
- 
+
 	/*
 	 * sync all other superblocks with the main superblock
 	 */
@@ -2444,19 +2477,9 @@
 	return err;
 }
 
-#define SET_SB(x) mddev->sb->x = info.x
-static int set_array_info (mddev_t * mddev, void * arg)
+#define SET_SB(x) mddev->sb->x = info->x
+static int set_array_info (mddev_t * mddev, mdu_array_info_t *info)
 {
-	mdu_array_info_t info;
-
-	if (mddev->sb) {
-		printk("array md%d already has a superblock!\n", 
-				mdidx(mddev));
-		return -EBUSY;
-	}
-
-	if (md_copy_from_user(&info, arg, sizeof(info)))
-		return -EFAULT;
 
 	if (alloc_array_sb(mddev))
 		return -ENOMEM;
@@ -2634,11 +2657,25 @@
 				printk("ioctl, reason %d, cmd %d\n", err, cmd);
 				goto abort;
 			}
-			err = set_array_info(mddev, (void *)arg);
-			if (err) {
-				printk("couldnt set array info. %d\n", err);
+
+			if (mddev->sb) {
+				printk("array md%d already has a superblock!\n",
+				       mdidx(mddev));
+				err = -EBUSY;
 				goto abort_unlock;
 			}
+			if (arg) {
+				mdu_array_info_t info;
+				if (md_copy_from_user(&info, (void*)arg, sizeof(info))) {
+					err = -EFAULT;
+					goto abort_unlock;
+				}
+				err = set_array_info(mddev, &info);
+				if (err) {
+					printk("couldnt set array info. %d\n", err);
+					goto abort_unlock;
+				}
+			}
 			goto done_unlock;
 
 		case START_ARRAY:
@@ -2669,6 +2706,11 @@
 		printk("ioctl lock interrupted, reason %d, cmd %d\n",err, cmd);
 		goto abort;
 	}
+	/* if we don't have a superblock yet, only ADD_NEW_DISK or STOP_ARRAY is allowed */
+	if (!mddev->sb && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY && cmd != RUN_ARRAY) {
+		err = -ENODEV;
+		goto abort_unlock;
+	}
 
 	/*
 	 * Commands even a read-only array can execute:
@@ -2689,7 +2731,10 @@
 
 		case STOP_ARRAY:
 			err = do_md_stop (mddev, 0);
-			goto done;
+			if (err)
+				goto done_unlock;
+			else
+				goto done;
 
 		case STOP_ARRAY_RO:
 			err = do_md_stop (mddev, 1);
@@ -2738,9 +2783,14 @@
 			goto done_unlock;
 
 		case ADD_NEW_DISK:
-			err = add_new_disk(mddev, (void *)arg);
+		{
+			mdu_disk_info_t info;
+			if (md_copy_from_user(&info, (void*)arg, sizeof(info)))
+				err = -EFAULT;
+			else
+				err = add_new_disk(mddev, (void *)arg);
 			goto done_unlock;
-
+		}
 		case HOT_REMOVE_DISK:
 			err = hot_remove_disk(mddev, (kdev_t)arg);
 			goto done_unlock;
@@ -2771,13 +2821,13 @@
 
 		case RUN_ARRAY:
 		{
+/* The data is never used....
 			mdu_param_t param;
-
 			err = md_copy_from_user(&param, (mdu_param_t *)arg,
 							 sizeof(param));
 			if (err)
 				goto abort_unlock;
-
+*/
 			err = do_md_run (mddev);
 			/*
 			 * we have to clean up the mess if
@@ -2825,7 +2875,7 @@
 	open:		md_open,
 	ioctl:		md_ioctl,
 };
- 
+
 
 int md_thread(void * arg)
 {
@@ -3020,9 +3070,9 @@
 static int status_resync (char * page, mddev_t * mddev)
 {
 	int sz = 0;
-	unsigned int max_blocks, resync, res, dt, tt, et;
+	unsigned long max_blocks, resync, res, dt, db, rt;
 
-	resync = mddev->curr_resync;
+	resync = mddev->curr_resync - atomic_read(&mddev->recovery_active);
 	max_blocks = mddev->sb->size;
 
 	/*
@@ -3047,13 +3097,13 @@
 		/*
 		 * true resync
 		 */
-		sz += sprintf(page + sz, " resync =%3u.%u%% (%u/%u)",
+		sz += sprintf(page + sz, " resync =%3lu.%lu%% (%lu/%lu)",
 				res/10, res % 10, resync, max_blocks);
 	else
 		/*
 		 * recovery ...
 		 */
-		sz += sprintf(page + sz, " recovery =%3u.%u%% (%u/%u)",
+		sz += sprintf(page + sz, " recovery =%3lu.%lu%% (%lu/%lu)",
 				res/10, res % 10, resync, max_blocks);
 
 	/*
@@ -3061,21 +3111,18 @@
 	 * the * 100 / 100 trick are important. We do a +1 to be
 	 * safe against division by zero. We only estimate anyway.
 	 *
-	 * dt: time until now
-	 * tt: total time
-	 * et: estimated finish time
-	 */
-	dt = ((jiffies - mddev->resync_start) / HZ);
-	tt = (dt * (max_blocks / (resync/100+1)))/100;
-	if (tt > dt)
-		et = tt - dt;
-	else
-		/*
-		 * ignore rounding effects near finish time
-		 */
-		et = 0;
+	 * dt: time from mark until now
+	 * db: blocks written from mark until now
+	 * rt: remaining time
+	 */
+	dt = ((jiffies - mddev->resync_mark) / HZ);
+	if (!dt) dt++;
+	db = resync - mddev->resync_mark_cnt;
+	rt = (dt * ((max_blocks-resync) / (db/100+1)))/100;
 	
-	sz += sprintf(page + sz, " finish=%u.%umin", et / 60, (et % 60)/6);
+	sz += sprintf(page + sz, " finish=%lu.%lumin", rt / 60, (rt % 60)/6);
+
+	sz += sprintf(page + sz, " speed=%ldK/sec", db/dt);
 
 	return sz;
 }
@@ -3101,7 +3148,7 @@
 		sz += sprintf(page+sz, "not set\n");
 	else
 		sz += sprintf(page+sz, "%d sectors\n", read_ahead[MD_MAJOR]);
-  
+
 	ITERATE_MDDEV(mddev,tmp) {
 		sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev),
 						mddev->pers ? "" : "in");
@@ -3158,7 +3205,7 @@
 
 	if (pers[pnum])
 		return -EBUSY;
-  
+
 	pers[pnum] = p;
 	printk(KERN_INFO "%s personality registered\n", p->name);
 	return 0;
@@ -3172,7 +3219,7 @@
 	printk(KERN_INFO "%s personality unregistered\n", pers[pnum]->name);
 	pers[pnum] = NULL;
 	return 0;
-} 
+}
 
 static mdp_disk_t *get_spare(mddev_t *mddev)
 {
@@ -3236,13 +3283,17 @@
 	}
 }
 
+#define SYNC_MARKS	10
+#define	SYNC_MARK_STEP	(3*HZ)
 int md_do_sync(mddev_t *mddev, mdp_disk_t *spare)
 {
 	mddev_t *mddev2;
 	unsigned int max_blocks, currspeed,
 		j, window, err, serialize;
 	kdev_t read_disk = mddev_to_kdev(mddev);
-	unsigned long starttime;
+	unsigned long mark[SYNC_MARKS];
+	unsigned long mark_cnt[SYNC_MARKS];	
+	int last_mark,m;
 	struct md_list_head *tmp;
 	unsigned long last_check;
 
@@ -3287,8 +3338,13 @@
 	current->priority = 1;
 
 	is_mddev_idle(mddev); /* this also initializes IO event counters */
-	starttime = jiffies;
-	mddev->resync_start = starttime;
+	for (m = 0; m < SYNC_MARKS; m++) {
+		mark[m] = jiffies;
+		mark_cnt[m] = 0;
+	}
+	last_mark = 0;
+	mddev->resync_mark = mark[last_mark];
+	mddev->resync_mark_cnt = mark_cnt[last_mark];
 
 	/*
 	 * Tune reconstruction:
@@ -3301,12 +3357,7 @@
 	last_check = 0;
 	for (j = 0; j < max_blocks;) {
 		int blocks;
-		if (j)
-			mddev->curr_resync = j;
 
-/*		wait_event(mddev->recovery_wait,
-			   atomic_read(&mddev->recovery_active) < window);
-*/
 		blocks = mddev->pers->sync_request(mddev, j);
 
 		if (blocks < 0) {
@@ -3315,12 +3366,24 @@
 		}
 		atomic_add(blocks, &mddev->recovery_active);
 		j += blocks;
+		mddev->curr_resync = j;
 
 		if (last_check + window > j)
 			continue;
 		
 		run_task_queue(&tq_disk); //??
 
+		if (jiffies >= mark[last_mark] + SYNC_MARK_STEP ) {
+			/* step marks */
+			int next = (last_mark+1) % SYNC_MARKS;
+			
+			mddev->resync_mark = mark[next];
+			mddev->resync_mark_cnt = mark_cnt[next];
+			mark[next] = jiffies;
+			mark_cnt[next] = j - atomic_read(&mddev->recovery_active);
+			last_mark = next;
+		}
+			
 
 		if (md_signal_pending(current)) {
 			/*
@@ -3345,7 +3408,8 @@
 		if (md_need_resched(current))
 			schedule();
 
-		currspeed = j/((jiffies-starttime)/HZ + 1) + 1;
+		currspeed = (j-mddev->resync_mark_cnt)/((jiffies-mddev->resync_mark)/HZ +1) +1;
+
 		if (currspeed > sysctl_speed_limit_min) {
 			current->priority = 1;
 
@@ -3359,7 +3423,6 @@
 		} else
 			current->priority = 40;
 	}
-	wait_event(mddev->recovery_wait, atomic_read(&mddev->recovery_active)==0);
 	fsync_dev(read_disk);
 	printk(KERN_INFO "md: md%d: sync done.\n",mdidx(mddev));
 	err = 0;
@@ -3367,6 +3430,7 @@
 	 * this also signals 'finished resyncing' to md_stop
 	 */
 out:
+	wait_event(mddev->recovery_wait, atomic_read(&mddev->recovery_active)==0);
 	up(&mddev->resync_sem);
 out_nolock:
 	mddev->curr_resync = 0;
@@ -3433,9 +3497,9 @@
 			if (disk_faulty(spare))
 				mddev->pers->diskop(mddev, &spare,
 						DISKOP_SPARE_INACTIVE);
-		if (err == -EINTR) {
+		if (err == -EINTR || err == -ENOMEM) {
 			/*
-			 * Recovery got interrupted ...
+			 * Recovery got interrupted, or ran out of mem ...
 			 * signal back that we have finished using the array.
 			 */
 			mddev->pers->diskop(mddev, &spare,
@@ -3565,7 +3629,7 @@
 	}
 	devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL);
 	devfs_register_series (devfs_handle, "%u",MAX_MD_DEVS,DEVFS_FL_DEFAULT,
-				MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, 0, 0,
+				MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR,
 				&md_fops, NULL);
 
 	blk_dev[MD_MAJOR].queue = md_get_queue;
@@ -3603,6 +3667,173 @@
 	md_geninit();
 	return (0);
 }
+
+#ifdef CONFIG_MD_BOOT
+#define MAX_MD_BOOT_DEVS	8
+struct {
+	unsigned long set;
+	int pers[MAX_MD_BOOT_DEVS];
+	int chunk[MAX_MD_BOOT_DEVS];
+	kdev_t devices[MAX_MD_BOOT_DEVS][MAX_REAL];
+} md_setup_args md__initdata;
+
+/*
+ * Parse the command-line parameters given our kernel, but do not
+ * actually try to invoke the MD device now; that is handled by
+ * md_setup_drive after the low-level disk drivers have initialised.
+ *
+ * 27/11/1999: Fixed to work correctly with the 2.3 kernel (which
+ *             assigns the task of parsing integer arguments to the
+ *             invoked program now).  Added ability to initialise all
+ *             the MD devices (by specifying multiple "md=" lines)
+ *             instead of just one.  -- KTK
+ * 18May2000: Added support for persistant-superblock arrays:
+ *             md=n,0,factor,fault,device-list   uses RAID0 for device n
+ *             md=n,-1,factor,fault,device-list  uses LINEAR for device n
+ *             md=n,device-list      reads a RAID superblock from the devices
+ *             elements in device-list are read by name_to_kdev_t so can be
+ *             a hex number or something like /dev/hda1 /dev/sdb
+ */
+extern kdev_t name_to_kdev_t(char *line) md__init;
+static int md__init md_setup(char *str)
+{
+	int minor, level, factor, fault, i=0;
+	kdev_t device;
+	char *devnames, *pername = "";
+
+	if(get_option(&str, &minor) != 2) {	/* MD Number */
+		printk("md: Too few arguments supplied to md=.\n");
+		return 0;
+	}
+	if (minor >= MAX_MD_BOOT_DEVS) {
+		printk ("md: Minor device number too high.\n");
+		return 0;
+	} else if (md_setup_args.set & (1 << minor)) {
+		printk ("md: Warning - md=%d,... has been specified twice;\n"
+			"    will discard the first definition.\n", minor);
+	}
+	switch(get_option(&str, &level)) {	/* RAID Personality */
+	case 2: /* could be 0 or -1.. */
+		if (level == 0 || level == -1) {
+			if (get_option(&str, &factor) != 2 ||	/* Chunk Size */
+			    get_option(&str, &fault) != 2) {
+				printk("md: Too few arguments supplied to md=.\n");
+				return 0;
+			}
+			md_setup_args.pers[minor] = level;
+			md_setup_args.chunk[minor] = 1 << (factor+12);
+			switch(level) {
+			case -1:
+				level = LINEAR;
+				pername = "linear";
+				break;
+			case 0:
+				level = RAID0;
+				pername = "raid0";
+				break;
+			default:
+				printk ("md: The kernel has not been configured for raid%d"
+					" support!\n", level);
+				return 0;
+			}
+			md_setup_args.pers[minor] = level;
+			break;
+		}
+		/* FALL THROUGH */
+	case 1: /* the first device is numeric */
+		md_setup_args.devices[minor][i++] = level;
+		/* FALL THROUGH */
+	case 0:
+		md_setup_args.pers[minor] = 0;
+		pername="super-block";
+	}
+	devnames = str;
+	for (; i<MAX_REAL && str; i++) {
+		if ((device = name_to_kdev_t(str))) {
+			md_setup_args.devices[minor][i] = device;
+		} else {
+			printk ("md: Unknown device name, %s.\n", str);
+			return 0;
+		}
+		if ((str = strchr(str, ',')) != NULL)
+			str++;
+	}
+	if (!i) {
+		printk ("md: No devices specified for md%d?\n", minor);
+		return 0;
+	}
+
+	printk ("md: Will configure md%d (%s) from %s, below.\n",
+		minor, pername, devnames);
+	md_setup_args.devices[minor][i] = (kdev_t) 0;
+	md_setup_args.set |= (1 << minor);
+	return 1;
+}
+
+void md__init md_setup_drive(void)
+{
+	int minor, i;
+	kdev_t dev;
+	mddev_t*mddev;
+
+	for (minor = 0; minor < MAX_MD_BOOT_DEVS; minor++) {
+		mdu_disk_info_t dinfo;
+		int err=0;
+		if (!(md_setup_args.set & (1 << minor)))
+			continue;
+		printk("md: Loading md%d.\n", minor);
+		mddev = alloc_mddev(MKDEV(MD_MAJOR,minor));
+		if (md_setup_args.pers[minor]) {
+			/* non-persistent */
+			mdu_array_info_t ainfo;
+			ainfo.level = pers_to_level(md_setup_args.pers[minor]);
+			ainfo.size = 0;
+			ainfo.nr_disks =0;
+			ainfo.raid_disks =0;
+			ainfo.md_minor =minor;
+			ainfo.not_persistent = 1;
+
+			ainfo.state = MD_SB_CLEAN;
+			ainfo.active_disks = 0;
+			ainfo.working_disks = 0;
+			ainfo.failed_disks = 0;
+			ainfo.spare_disks = 0;
+			ainfo.layout = 0;
+			ainfo.chunk_size = md_setup_args.chunk[minor];
+			err = set_array_info(mddev, &ainfo);
+			for (i=0; !err && (dev = md_setup_args.devices[minor][i]); i++) {
+				dinfo.number = i;
+				dinfo.raid_disk = i;
+				dinfo.state = (1<<MD_DISK_ACTIVE)|(1<<MD_DISK_SYNC);
+				dinfo.major = MAJOR(dev);
+				dinfo.minor = MINOR(dev);
+				mddev->sb->nr_disks++;
+				mddev->sb->raid_disks++;
+				mddev->sb->active_disks++;
+				mddev->sb->working_disks++;
+				err = add_new_disk (mddev, &dinfo);
+			}
+		} else {
+			/* persistent */
+			for (i = 0; (dev = md_setup_args.devices[minor][i]); i++) {
+				dinfo.major = MAJOR(dev);
+				dinfo.minor = MINOR(dev);
+				add_new_disk (mddev, &dinfo);
+			}
+		}
+		if (!err)
+			err = do_md_run(mddev);
+		if (err) {
+			mddev->sb_dirty = 0;
+			do_md_stop(mddev, 0);
+			printk("md: starting md%d failed\n", minor);
+		}
+	}
+}
+
+__setup("md=", md_setup);
+#endif
+
 
 MD_EXPORT_SYMBOL(md_size);
 MD_EXPORT_SYMBOL(register_md_personality);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)