/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 * 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(); }