/* * usr/kinit/do_mounts_md.c * * 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 "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, level, factor, fault, partitioned = 0; char *pername = ""; char *str1; int ent; if (*str == 'd') { partitioned = 1; str++; } if (get_option(&str, &minor) != 2) { /* MD Number */ fprintf(stderr,"md: Too few arguments supplied to md=.\n"); return 0; } str1 = str; if (minor >= MAX_MD_DEVS) { fprintf(stderr,"md: md=%d, Minor device number too high.\n", minor); return 0; } for (ent=0 ; ent< md_setup_ents ; ent++) if (md_setup_args[ent].minor == minor && md_setup_args[ent].partitioned == partitioned) { fprintf(stderr,"md: md=%s%d, Specified more than once. " "Replacing previous definition.\n", partitioned?"d":"", minor); break; } if (ent >= MAX_MD_DEVS) { fprintf(stderr,"md: md=%s%d - too many md initialisations\n", partitioned?"d":"", minor); 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%d (%s) from %s, below.\n", minor, pername, str); md_setup_args[ent].device_names = str; md_setup_args[ent].partitioned = partitioned; md_setup_args[ent].minor = minor; 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], devfs_name[16]; dev_minor = md_setup_args[ent].minor; partitioned = md_setup_args[ent].partitioned; devname = md_setup_args[ent].device_names; sprintf(name, "/dev/md%s%d", partitioned?"_d":"", dev_minor); sprintf(devfs_name, "/dev/md/%s%d", partitioned?"d":"", dev_minor); if (partitioned) dev = makedev(mdp_major(), dev_minor << MdpMinorShift); else dev = makedev(MD_MAJOR, dev_minor); create_dev(name, dev, devfs_name); 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<= 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(); }