diff options
Diffstat (limited to 'usr/kinit/do_mounts_md.c')
-rw-r--r-- | usr/kinit/do_mounts_md.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/usr/kinit/do_mounts_md.c b/usr/kinit/do_mounts_md.c new file mode 100644 index 0000000..f446620 --- /dev/null +++ b/usr/kinit/do_mounts_md.c @@ -0,0 +1,400 @@ +/* + * Handle autoconfiguration of md devices. This is ugly, partially since + * it still relies on a sizable kernel component. + * + * This file is derived from the Linux kernel. + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <inttypes.h> +#include <sys/sysmacros.h> +#include <sys/md.h> +#include <linux/major.h> + +#include "kinit.h" +#include "do_mounts.h" + +#define LEVEL_NONE (-1000000) + +/* + * When md (and any require personalities) are compiled into the kernel + * (not a module), arrays can be assembles are boot time using with AUTODETECT + * where specially marked partitions are registered with md_autodetect_dev(), + * and with MD_BOOT where devices to be collected are given on the boot line + * with md=..... + * The code for that is here. + */ + +static int raid_noautodetect, raid_autopart; + +static struct { + int minor; + int partitioned; + int level; + int chunk; + char *device_names; +} md_setup_args[MAX_MD_DEVS]; + +static int md_setup_ents; + +/** + * get_option - Parse integer from an option string + * @str: option string + * @pint: (output) integer value parsed from @str + * + * Read an int from an option string; if available accept a subsequent + * comma as well. + * + * Return values: + * 0 : no int in string + * 1 : int found, no subsequent comma + * 2 : int found including a subsequent comma + */ + +static int get_option(char **str, int *pint) +{ + char *cur = *str; + + if (!cur || !(*cur)) + return 0; + *pint = strtol(cur, str, 0); + if (cur == *str) + return 0; + if (**str == ',') { + (*str)++; + return 2; + } + + return 1; +} + +/* + * Find the partitioned md device major number... of course this *HAD* + * to be done dynamically instead of using a registered number. + * Sigh. Double sigh. + */ +static int mdp_major(void) +{ + static int found = 0; + FILE *f; + char line[512], *p; + int is_blk, major_no; + + if (found) + return found; + + f = fopen("/proc/devices", "r"); + is_blk = 0; + while (fgets(line, sizeof line, f)) { + if (!strcmp(line, "Block devices:\n")) + is_blk = 1; + if (is_blk) { + major_no = strtol(line, &p, 10); + while (*p && isspace(*p)) + p++; + + if (major_no == 0) /* Not a number */ + is_blk = 0; + else if (major_no > 0 && !strcmp(p, "mdp")) { + found = major_no; + break; + } + } + } + fclose(f); + + if (!found) { + fprintf(stderr, + "Error: mdp devices detected but no mdp device found!\n"); + exit(1); + } + + return found; +} + +/* + * 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 persistent-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 + * 2001-06-03: Dave Cinege <dcinege@psychosis.com> + * Shifted name_to_kdev_t() and related operations to md_set_drive() + * for later execution. Rewrote section to make devfs compatible. + */ +static int md_setup(char *str) +{ + int minor_num, level, factor, fault, partitioned = 0; + char *pername = ""; + char *str1; + int ent; + + if (*str == 'd') { + partitioned = 1; + str++; + } + if (get_option(&str, &minor_num) != 2) { /* MD Number */ + fprintf(stderr, "md: Too few arguments supplied to md=.\n"); + return 0; + } + str1 = str; + if (minor_num >= MAX_MD_DEVS) { + fprintf(stderr, "md: md=%d, Minor device number too high.\n", + minor_num); + return 0; + } + for (ent = 0; ent < md_setup_ents; ent++) + if (md_setup_args[ent].minor == minor_num && + md_setup_args[ent].partitioned == partitioned) { + fprintf(stderr, + "md: md=%s%d, Specified more than once. " + "Replacing previous definition.\n", + partitioned ? "d" : "", minor_num); + break; + } + if (ent >= MAX_MD_DEVS) { + fprintf(stderr, "md: md=%s%d - too many md initialisations\n", + partitioned ? "d" : "", minor_num); + return 0; + } + if (ent >= md_setup_ents) + md_setup_ents++; + switch (get_option(&str, &level)) { /* RAID level */ + case 2: /* could be 0 or -1.. */ + if (level == 0 || level == LEVEL_LINEAR) { + if (get_option(&str, &factor) != 2 || /* Chunk Size */ + get_option(&str, &fault) != 2) { + fprintf(stderr, + "md: Too few arguments supplied to md=.\n"); + return 0; + } + md_setup_args[ent].level = level; + md_setup_args[ent].chunk = 1 << (factor + 12); + if (level == LEVEL_LINEAR) + pername = "linear"; + else + pername = "raid0"; + break; + } + /* FALL THROUGH */ + case 1: /* the first device is numeric */ + str = str1; + /* FALL THROUGH */ + case 0: + md_setup_args[ent].level = LEVEL_NONE; + pername = "super-block"; + } + + fprintf(stderr, "md: Will configure md%s%d (%s) from %s, below.\n", + partitioned ? "_d" : "", minor_num, pername, str); + md_setup_args[ent].device_names = str; + md_setup_args[ent].partitioned = partitioned; + md_setup_args[ent].minor = minor_num; + + return 1; +} + +#define MdpMinorShift 6 + +static void md_setup_drive(void) +{ + int dev_minor, i, ent, partitioned; + dev_t dev; + dev_t devices[MD_SB_DISKS + 1]; + + for (ent = 0; ent < md_setup_ents; ent++) { + int fd; + int err = 0; + char *devname; + mdu_disk_info_t dinfo; + char name[16]; + struct stat st_chk; + + dev_minor = md_setup_args[ent].minor; + partitioned = md_setup_args[ent].partitioned; + devname = md_setup_args[ent].device_names; + + snprintf(name, sizeof name, + "/dev/md%s%d", partitioned ? "_d" : "", dev_minor); + + if (stat(name, &st_chk) == 0) + continue; + + if (partitioned) + dev = makedev(mdp_major(), dev_minor << MdpMinorShift); + else + dev = makedev(MD_MAJOR, dev_minor); + create_dev(name, dev); + for (i = 0; i < MD_SB_DISKS && devname != 0; i++) { + char *p; + + p = strchr(devname, ','); + if (p) + *p++ = 0; + + dev = name_to_dev_t(devname); + if (!dev) { + fprintf(stderr, "md: Unknown device name: %s\n", + devname); + break; + } + + devices[i] = dev; + + devname = p; + } + devices[i] = 0; + + if (!i) + continue; + + fprintf(stderr, "md: Loading md%s%d: %s\n", + partitioned ? "_d" : "", dev_minor, + md_setup_args[ent].device_names); + + fd = open(name, 0, 0); + if (fd < 0) { + fprintf(stderr, "md: open failed - cannot start " + "array %s\n", name); + continue; + } + if (ioctl(fd, SET_ARRAY_INFO, 0) == -EBUSY) { + fprintf(stderr, + "md: Ignoring md=%d, already autodetected. (Use raid=noautodetect)\n", + dev_minor); + close(fd); + continue; + } + + if (md_setup_args[ent].level != LEVEL_NONE) { + /* non-persistent */ + mdu_array_info_t ainfo; + ainfo.level = md_setup_args[ent].level; + ainfo.size = 0; + ainfo.nr_disks = 0; + ainfo.raid_disks = 0; + while (devices[ainfo.raid_disks]) + ainfo.raid_disks++; + ainfo.md_minor = dev_minor; + ainfo.not_persistent = 1; + + ainfo.state = (1 << MD_SB_CLEAN); + ainfo.layout = 0; + ainfo.chunk_size = md_setup_args[ent].chunk; + err = ioctl(fd, SET_ARRAY_INFO, &ainfo); + for (i = 0; !err && i <= MD_SB_DISKS; i++) { + dev = devices[i]; + if (!dev) + break; + 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); + err = ioctl(fd, ADD_NEW_DISK, &dinfo); + } + } else { + /* persistent */ + for (i = 0; i <= MD_SB_DISKS; i++) { + dev = devices[i]; + if (!dev) + break; + dinfo.major = major(dev); + dinfo.minor = minor(dev); + ioctl(fd, ADD_NEW_DISK, &dinfo); + } + } + if (!err) + err = ioctl(fd, RUN_ARRAY, 0); + if (err) + fprintf(stderr, "md: starting md%d failed\n", + dev_minor); + else { + /* reread the partition table. + * I (neilb) and not sure why this is needed, but I + * cannot boot a kernel with devfs compiled in from + * partitioned md array without it + */ + close(fd); + fd = open(name, 0, 0); + ioctl(fd, BLKRRPART, 0); + } + close(fd); + } +} + +static int raid_setup(char *str) +{ + int len, pos; + + len = strlen(str) + 1; + pos = 0; + + while (pos < len) { + char *comma = strchr(str + pos, ','); + int wlen; + if (comma) + wlen = (comma - str) - pos; + else + wlen = (len - 1) - pos; + + if (!strncmp(str, "noautodetect", wlen)) + raid_noautodetect = 1; + if (strncmp(str, "partitionable", wlen) == 0) + raid_autopart = 1; + if (strncmp(str, "part", wlen) == 0) + raid_autopart = 1; + pos += wlen + 1; + } + return 1; +} + +static void md_run_setup(void) +{ + create_dev("/dev/md0", makedev(MD_MAJOR, 0)); + if (raid_noautodetect) + fprintf(stderr, + "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n"); + else { + int fd = open("/dev/md0", 0, 0); + if (fd >= 0) { + ioctl(fd, RAID_AUTORUN, + (void *)(intptr_t) raid_autopart); + close(fd); + } + } + md_setup_drive(); +} + +void md_run(int argc, char *argv[]) +{ + char **pp, *p; + + for (pp = argv; (p = *pp); pp++) { + if (!strncmp(p, "raid=", 5)) + raid_setup(p + 5); + else if (!strncmp(p, "md=", 3)) + md_setup(p + 3); + } + + md_run_setup(); +} |