2000-11-27 16:42:37

by T. Camp

[permalink] [raw]
Subject: Patch against 2.4-pre-8 for multiple boot-time root= root devices

This patch will allow you from something like lilo to do the following:

linux root=/dev/hda1,/dev/hdb1,/dev/hdd1

which will cause the kernel to attempt to probe all three devices in order
until it finds one with a valid filesytem. This is handy for people that
move devices around a lot (ie the devices changes ID) and provides an easy
and direct solution for bootable cd-roms that could be on anyone of
several types of cdrom device.

caveates: I don't know much about initrd and the change_root() handling,
to the best of my knowledge this co-exists happily.

I'm not on the list so if you have replies of any nature be sure to cc me
directly. thanks,

t. camp

--cut here--
diff --recursive --unified --ignore-all-space linux/Documentation/kernel-parameters.txt linux-2.4.0.8/Documentation/kernel-parameters.txt
--- linux/Documentation/kernel-parameters.txt Tue Sep 5 13:51:14 2000
+++ linux-2.4.0.8/Documentation/kernel-parameters.txt Fri Nov 24 16:51:12 2000
@@ -473,7 +473,11 @@

ro [KNL] Mount root device read-only on boot.

- root= [KNL] root filesystem.
+ root= [KNL] root filesystem, comma separated list of up
+ to eight /dev/ entries will be tried in order
+ presented until kernel suceeds in mounting a
+ filesystem, multiple independant 'root=' entries
+ allowed as well.

rw [KNL] Mount root device read-write on boot.

diff --recursive --unified --ignore-all-space linux/fs/super.c linux-2.4.0.8/fs/super.c
--- linux/fs/super.c Fri Aug 11 14:31:45 2000
+++ linux-2.4.0.8/fs/super.c Fri Nov 24 17:00:29 2000
@@ -18,6 +18,8 @@
* Torbj?rn Lindh ([email protected]), April 14, 1996.
* Added devfs support: Richard Gooch <[email protected]>, 13-JAN-1998
* Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
+ * Modifications to mount_root for trying a list of root devices.
+ * Tracy Camp <[email protected]> 11/22/00
*/

#include <linux/config.h>
@@ -1447,27 +1449,33 @@
struct block_device *bdev = NULL;
mode_t mode;
int retval;
+ int root_device_index = 0;
void *handle;
char path[64];
int path_start = -1;

#ifdef CONFIG_ROOT_NFS
void *data;
- if (MAJOR(ROOT_DEV) != UNNAMED_MAJOR)
+ if (MAJOR(ROOT_DEV) != UNNAMED_MAJOR) {
goto skip_nfs;
+ }
fs_type = get_fs_type("nfs");
- if (!fs_type)
+ if (!fs_type) {
goto no_nfs;
+ }
ROOT_DEV = get_unnamed_dev();
if (!ROOT_DEV)
/*
* Your /linuxrc sucks worse than MSExchange - that's the
* only way you could run out of anon devices at that point.
*/
+ {
goto no_anon;
+ }
data = nfs_root_data();
- if (!data)
+ if (!data) {
goto no_server;
+ }
sb = read_super(ROOT_DEV, NULL, fs_type, root_mountflags, data, 1);
if (sb)
/*
@@ -1475,7 +1483,9 @@
* chance anyway (no memory for vfsmnt and we _will_ need it,
* no matter which fs we try to mount).
*/
+ {
goto mount_it;
+ }
no_server:
put_unnamed_dev(ROOT_DEV);
no_anon:
@@ -1497,8 +1507,9 @@
printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n");
#else
/* rd_doload is 2 for a dual initrd/ramload setup */
- if(rd_doload==2)
+ if(rd_doload==2) {
rd_load_secondary();
+ }
else
#endif
{
@@ -1507,13 +1518,21 @@
}
}
#endif
+printk("VFS: trying to find root device\n");
+
+ for(root_device_index = 0; root_device_index < number_root_devs; root_device_index++) {

- devfs_make_root (root_device_name);
- handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
- MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
- DEVFS_SPECIAL_BLK, 1);
- if (handle) /* Sigh: bd*() functions only paper over the cracks */
+printk("VFS: trying %s at index %d\n",&root_device_name[root_device_index][0], root_device_index);
+ if(root_device_name[root_device_index][0] != '\0')
{
+ ROOT_DEV = name_to_kdev_t(&root_device_name[root_device_index][0]); /* translate */
+ }
+ devfs_make_root (&root_device_name[root_device_index][0]);
+
+ handle = devfs_find_handle (NULL, &root_device_name[root_device_index][0], MAJOR (ROOT_DEV), MINOR (ROOT_DEV), DEVFS_SPECIAL_BLK, 1);
+
+/* Sigh: bd*() functions only paper over the cracks */
+ if (handle) {
unsigned major, minor;

devfs_get_maj_min (handle, &major, &minor);
@@ -1524,17 +1543,20 @@
* Probably pure paranoia, but I'm less than happy about delving into
* devfs crap and checking it right now. Later.
*/
- if (!ROOT_DEV)
+ if (!ROOT_DEV) {
panic("I have no root and I want to scream");
+ }

bdev = bdget(kdev_t_to_nr(ROOT_DEV));
- if (!bdev)
+ if (!bdev) {
panic(__FUNCTION__ ": unable to allocate root device");
+ }
bdev->bd_op = devfs_get_ops (handle);
path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
mode = FMODE_READ;
- if (!(root_mountflags & MS_RDONLY))
+ if (!(root_mountflags & MS_RDONLY)) {
mode |= FMODE_WRITE;
+ }
retval = blkdev_get(bdev, mode, 0, BDEV_FS);
if (retval == -EROFS) {
root_mountflags |= MS_RDONLY;
@@ -1545,11 +1567,9 @@
* Allow the user to distinguish between failed open
* and bad superblock on root device.
*/
- printk ("VFS: Cannot open root device \"%s\" or %s\n",
- root_device_name, kdevname (ROOT_DEV));
- printk ("Please append a correct \"root=\" boot option\n");
- panic("VFS: Unable to mount root fs on %s",
- kdevname(ROOT_DEV));
+ printk ("VFS: Cannot open root device \"%s\" or %s\n", &root_device_name[root_device_index][0], kdevname (ROOT_DEV));
+ printk("VFS: Unable to mount root fs on %s\n", kdevname(ROOT_DEV));
+ continue;
}

check_disk_change(ROOT_DEV);
@@ -1560,39 +1580,45 @@
}

read_lock(&file_systems_lock);
- for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
- if (!(fs_type->fs_flags & FS_REQUIRES_DEV))
+ for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next)
+ {
+ if (!(fs_type->fs_flags & FS_REQUIRES_DEV)) {
continue;
- if (!try_inc_mod_count(fs_type->owner))
+ }
+ if (!try_inc_mod_count(fs_type->owner)) {
continue;
+ }
read_unlock(&file_systems_lock);
sb = read_super(ROOT_DEV,bdev,fs_type,root_mountflags,NULL,1);
- if (sb)
+ if (sb) {
goto mount_it;
+ }
read_lock(&file_systems_lock);
put_filesystem(fs_type);
}
read_unlock(&file_systems_lock);
- panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));
+ printk("VFS: Unable to mount root fs on %s\n", kdevname(ROOT_DEV));
+ }
+ printk("VFS: Please append a correct \"root=\" boot option\n");
+ panic("VFS: Unable to mount any root fs at all");

mount_it:
- printk ("VFS: Mounted root (%s filesystem)%s.\n",
- fs_type->name,
- (sb->s_flags & MS_RDONLY) ? " readonly" : "");
+ printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : "");
if (path_start >= 0) {
- devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
- path + 5 + path_start, NULL, NULL);
+ devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT, path + 5 + path_start, NULL, NULL);
memcpy (path + path_start, "/dev/", 5);
vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start);
}
- else
+ else {
vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root");
+ }
/* FIXME: if something will try to umount us right now... */
if (vfsmnt) {
set_fs_root(current->fs, vfsmnt, sb->s_root);
set_fs_pwd(current->fs, vfsmnt, sb->s_root);
- if (bdev)
+ if (bdev) {
bdput(bdev); /* sb holds a reference */
+ }
return;
}
panic("VFS: add_vfsmnt failed for root fs");
@@ -1735,6 +1761,8 @@
struct nameidata devfs_nd, nd;
int error = 0;

+printk("fs/super.c:change_root() called\n");
+
read_lock(&current->fs->lock);
old_rootmnt = mntget(current->fs->rootmnt);
read_unlock(&current->fs->lock);
@@ -1752,6 +1780,15 @@
} else
path_release(&devfs_nd);
}
+/* this function seems to only be used when after an initrd usage,
+* so the actual value of ROOT_DEV hasn't been determined until mount_root
+* is called, because we try a list of root devices before giving up,
+* this ROOT_DEV global is somewhat misleading now, as its only valid after
+* a call to mount_root because mount_root ignores the ROOT_DEV value
+* except for nfs and ram mount attempts going in, but will realise
+* to not mount RAM again so it should be okay to leave this be.
+* - 11/22/00 <[email protected]>
+*/
ROOT_DEV = new_root_dev;
mount_root();
#if 1
diff --recursive --unified --ignore-all-space linux/include/linux/fs.h linux-2.4.0.8/include/linux/fs.h
--- linux/include/linux/fs.h Fri Sep 8 12:52:42 2000
+++ linux-2.4.0.8/include/linux/fs.h Thu Nov 23 21:25:52 2000
@@ -1200,7 +1200,10 @@
unsigned long generate_cluster(kdev_t, int b[], int);
unsigned long generate_cluster_swab32(kdev_t, int b[], int);
extern kdev_t ROOT_DEV;
-extern char root_device_name[];
+/* following added 11/22/00 by Tracy Camp <[email protected]> */
+extern char root_device_name[8][64];
+extern int number_root_devs;
+extern kdev_t name_to_kdev_t(char *);


extern void show_buffers(void);
diff --recursive --unified --ignore-all-space linux/init/main.c linux-2.4.0.8/init/main.c
--- linux/init/main.c Fri Sep 1 14:25:26 2000
+++ linux-2.4.0.8/init/main.c Fri Nov 24 16:57:15 2000
@@ -7,6 +7,11 @@
* Added initrd & change_root: Werner Almesberger & Hans Lermen, Feb '96
* Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96
* Simplified starting of init: Michael A. Griffith <[email protected]>
+ *
+ * 11/22/00 - added support for up to 8 comma separated and multiple root=
+ * arguements. Modified root_dev_setup, and name_to_kdev_t, other changes in
+ * fs/super.c and include/linux/fs.h to support processing root device list.
+ * Tracy Camp <[email protected]>
*/

#define __KERNEL_SYSCALLS__
@@ -129,7 +134,8 @@

int root_mountflags = MS_RDONLY;
char *execute_command;
-char root_device_name[64];
+char root_device_name[8][64];
+int number_root_devs = 0;


static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
@@ -257,13 +263,13 @@
{ NULL, 0 }
};

-kdev_t __init name_to_kdev_t(char *line)
-{
+kdev_t __init name_to_kdev_t(char *line) {
int base = 0;
+ struct dev_name_struct *dev = root_dev_names;

if (strncmp(line,"/dev/",5) == 0) {
- struct dev_name_struct *dev = root_dev_names;
line += 5;
+ }
do {
int len = strlen(dev->name);
if (strncmp(line,dev->name,len) == 0) {
@@ -272,25 +278,51 @@
break;
}
dev++;
- } while (dev->name);
}
+ while (dev->name);
return to_kdev_t(base + simple_strtoul(line,NULL,base?10:16));
}

-static int __init root_dev_setup(char *line)
-{
+static int __init root_dev_setup(char *line) {
int i;
char ch;

- ROOT_DEV = name_to_kdev_t(line);
- memset (root_device_name, 0, sizeof root_device_name);
- if (strncmp (line, "/dev/", 5) == 0) line += 5;
- for (i = 0; i < sizeof root_device_name - 1; ++i)
- {
+/* number_root_devs is initially 0 and not touched except for
+incrementing, so this function should be re-callable with the
+desired results */
+ if (strncmp (line, "/dev/", 5) == 0) {
+ line += 5;
+ }
+ memset (root_device_name, 0, sizeof root_device_name[number_root_devs]);
+ for (i = 0; i < sizeof root_device_name[number_root_devs] - 1; ++i) {
ch = line[i];
- if ( isspace (ch) || (ch == ',') || (ch == '\0') ) break;
- root_device_name[i] = ch;
+ if ( ch == ',') {
+ if(number_root_devs == 0) {
+ ROOT_DEV = name_to_kdev_t(&root_device_name[number_root_devs][0]);
+ }
+ number_root_devs++;
+ if(number_root_devs >= 8) {
+ break;
}
+ i++;
+ line += i;
+ i = 0;
+ if (strncmp (line, "/dev/", 5) == 0) {
+ line += 5;
+ }
+ ch = line[i];
+ memset (root_device_name[number_root_devs], 0, sizeof root_device_name[number_root_devs]);
+ }
+ if ( isspace (ch) || (ch == '\0') ) {
+ if(number_root_devs == 0) {
+ ROOT_DEV = name_to_kdev_t(&root_device_name[number_root_devs][0]);
+ }
+ number_root_devs++;
+ break;
+ }
+ root_device_name[number_root_devs][i] = ch;
+ }
+printk("root_dev_setup number_root_devs: %d\n",number_root_devs);
return 1;
}

@@ -543,10 +575,8 @@
sti();
calibrate_delay();
#ifdef CONFIG_BLK_DEV_INITRD
- if (initrd_start && !initrd_below_start_ok &&
- initrd_start < min_low_pfn << PAGE_SHIFT) {
- printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
- "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
+ if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) {
+ printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
@@ -693,10 +723,10 @@
sock_init();

#ifdef CONFIG_BLK_DEV_INITRD
- real_root_dev = ROOT_DEV;
+ real_root_dev = ROOT_DEV; /* as decided upon by root_dev_setup() */
real_root_mountflags = root_mountflags;
- if (initrd_start && mount_initrd) root_mountflags &= ~MS_RDONLY;
- else mount_initrd =0;
+ if (initrd_start && mount_initrd) { root_mountflags &= ~MS_RDONLY; }
+ else { mount_initrd =0; }
#endif

do_initcalls();
@@ -717,23 +747,22 @@

#ifdef CONFIG_BLK_DEV_INITRD
root_mountflags = real_root_mountflags;
- if (mount_initrd && ROOT_DEV != real_root_dev
- && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) {
+ if (mount_initrd && ROOT_DEV != real_root_dev && MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) {
int error;
int i, pid;

pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
- if (pid>0)
+ if (pid>0) {
while (pid != wait(&i));
- if (MAJOR(real_root_dev) != RAMDISK_MAJOR
- || MINOR(real_root_dev) != 0) {
+ }
+ if (MAJOR(real_root_dev) != RAMDISK_MAJOR || MINOR(real_root_dev) != 0) {
#ifdef CONFIG_BLK_DEV_MD
md_run_setup();
#endif
error = change_root(real_root_dev,"/initrd");
- if (error)
- printk(KERN_ERR "Change root to /initrd: "
- "error %d\n",error);
+ if (error) {
+ printk(KERN_ERR "Change root to /initrd: error %d\n",error);
+ }
}
}
#endif