xref: /openbmc/linux/drivers/md/md-autodetect.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
14f5b246bSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
24f5b246bSChristoph Hellwig #include <linux/kernel.h>
34f5b246bSChristoph Hellwig #include <linux/blkdev.h>
44f5b246bSChristoph Hellwig #include <linux/init.h>
54f5b246bSChristoph Hellwig #include <linux/mount.h>
64f5b246bSChristoph Hellwig #include <linux/major.h>
74f5b246bSChristoph Hellwig #include <linux/delay.h>
8716308a5SChristoph Hellwig #include <linux/init_syscalls.h>
94f5b246bSChristoph Hellwig #include <linux/raid/detect.h>
104f5b246bSChristoph Hellwig #include <linux/raid/md_u.h>
114f5b246bSChristoph Hellwig #include <linux/raid/md_p.h>
12d82fa81cSChristoph Hellwig #include "md.h"
134f5b246bSChristoph Hellwig 
144f5b246bSChristoph Hellwig /*
154f5b246bSChristoph Hellwig  * When md (and any require personalities) are compiled into the kernel
164f5b246bSChristoph Hellwig  * (not a module), arrays can be assembles are boot time using with AUTODETECT
174f5b246bSChristoph Hellwig  * where specially marked partitions are registered with md_autodetect_dev(),
184f5b246bSChristoph Hellwig  * and with MD_BOOT where devices to be collected are given on the boot line
194f5b246bSChristoph Hellwig  * with md=.....
204f5b246bSChristoph Hellwig  * The code for that is here.
214f5b246bSChristoph Hellwig  */
224f5b246bSChristoph Hellwig 
234f5b246bSChristoph Hellwig #ifdef CONFIG_MD_AUTODETECT
244f5b246bSChristoph Hellwig static int __initdata raid_noautodetect;
254f5b246bSChristoph Hellwig #else
264f5b246bSChristoph Hellwig static int __initdata raid_noautodetect=1;
274f5b246bSChristoph Hellwig #endif
284f5b246bSChristoph Hellwig static int __initdata raid_autopart;
294f5b246bSChristoph Hellwig 
30d1100488SChristoph Hellwig static struct md_setup_args {
314f5b246bSChristoph Hellwig 	int minor;
324f5b246bSChristoph Hellwig 	int partitioned;
334f5b246bSChristoph Hellwig 	int level;
344f5b246bSChristoph Hellwig 	int chunk;
354f5b246bSChristoph Hellwig 	char *device_names;
364f5b246bSChristoph Hellwig } md_setup_args[256] __initdata;
374f5b246bSChristoph Hellwig 
384f5b246bSChristoph Hellwig static int md_setup_ents __initdata;
394f5b246bSChristoph Hellwig 
404f5b246bSChristoph Hellwig /*
414f5b246bSChristoph Hellwig  * Parse the command-line parameters given our kernel, but do not
424f5b246bSChristoph Hellwig  * actually try to invoke the MD device now; that is handled by
434f5b246bSChristoph Hellwig  * md_setup_drive after the low-level disk drivers have initialised.
444f5b246bSChristoph Hellwig  *
454f5b246bSChristoph Hellwig  * 27/11/1999: Fixed to work correctly with the 2.3 kernel (which
464f5b246bSChristoph Hellwig  *             assigns the task of parsing integer arguments to the
474f5b246bSChristoph Hellwig  *             invoked program now).  Added ability to initialise all
484f5b246bSChristoph Hellwig  *             the MD devices (by specifying multiple "md=" lines)
494f5b246bSChristoph Hellwig  *             instead of just one.  -- KTK
504f5b246bSChristoph Hellwig  * 18May2000: Added support for persistent-superblock arrays:
514f5b246bSChristoph Hellwig  *             md=n,0,factor,fault,device-list   uses RAID0 for device n
524f5b246bSChristoph Hellwig  *             md=n,-1,factor,fault,device-list  uses LINEAR for device n
534f5b246bSChristoph Hellwig  *             md=n,device-list      reads a RAID superblock from the devices
544f5b246bSChristoph Hellwig  *             elements in device-list are read by name_to_kdev_t so can be
554f5b246bSChristoph Hellwig  *             a hex number or something like /dev/hda1 /dev/sdb
564f5b246bSChristoph Hellwig  * 2001-06-03: Dave Cinege <dcinege@psychosis.com>
574f5b246bSChristoph Hellwig  *		Shifted name_to_kdev_t() and related operations to md_set_drive()
584f5b246bSChristoph Hellwig  *		for later execution. Rewrote section to make devfs compatible.
594f5b246bSChristoph Hellwig  */
md_setup(char * str)604f5b246bSChristoph Hellwig static int __init md_setup(char *str)
614f5b246bSChristoph Hellwig {
624f5b246bSChristoph Hellwig 	int minor, level, factor, fault, partitioned = 0;
634f5b246bSChristoph Hellwig 	char *pername = "";
644f5b246bSChristoph Hellwig 	char *str1;
654f5b246bSChristoph Hellwig 	int ent;
664f5b246bSChristoph Hellwig 
674f5b246bSChristoph Hellwig 	if (*str == 'd') {
684f5b246bSChristoph Hellwig 		partitioned = 1;
694f5b246bSChristoph Hellwig 		str++;
704f5b246bSChristoph Hellwig 	}
714f5b246bSChristoph Hellwig 	if (get_option(&str, &minor) != 2) {	/* MD Number */
724f5b246bSChristoph Hellwig 		printk(KERN_WARNING "md: Too few arguments supplied to md=.\n");
734f5b246bSChristoph Hellwig 		return 0;
744f5b246bSChristoph Hellwig 	}
754f5b246bSChristoph Hellwig 	str1 = str;
764f5b246bSChristoph Hellwig 	for (ent=0 ; ent< md_setup_ents ; ent++)
774f5b246bSChristoph Hellwig 		if (md_setup_args[ent].minor == minor &&
784f5b246bSChristoph Hellwig 		    md_setup_args[ent].partitioned == partitioned) {
794f5b246bSChristoph Hellwig 			printk(KERN_WARNING "md: md=%s%d, Specified more than once. "
804f5b246bSChristoph Hellwig 			       "Replacing previous definition.\n", partitioned?"d":"", minor);
814f5b246bSChristoph Hellwig 			break;
824f5b246bSChristoph Hellwig 		}
834f5b246bSChristoph Hellwig 	if (ent >= ARRAY_SIZE(md_setup_args)) {
844f5b246bSChristoph Hellwig 		printk(KERN_WARNING "md: md=%s%d - too many md initialisations\n", partitioned?"d":"", minor);
854f5b246bSChristoph Hellwig 		return 0;
864f5b246bSChristoph Hellwig 	}
874f5b246bSChristoph Hellwig 	if (ent >= md_setup_ents)
884f5b246bSChristoph Hellwig 		md_setup_ents++;
894f5b246bSChristoph Hellwig 	switch (get_option(&str, &level)) {	/* RAID level */
904f5b246bSChristoph Hellwig 	case 2: /* could be 0 or -1.. */
914f5b246bSChristoph Hellwig 		if (level == 0 || level == LEVEL_LINEAR) {
924f5b246bSChristoph Hellwig 			if (get_option(&str, &factor) != 2 ||	/* Chunk Size */
934f5b246bSChristoph Hellwig 					get_option(&str, &fault) != 2) {
944f5b246bSChristoph Hellwig 				printk(KERN_WARNING "md: Too few arguments supplied to md=.\n");
954f5b246bSChristoph Hellwig 				return 0;
964f5b246bSChristoph Hellwig 			}
974f5b246bSChristoph Hellwig 			md_setup_args[ent].level = level;
984f5b246bSChristoph Hellwig 			md_setup_args[ent].chunk = 1 << (factor+12);
994f5b246bSChristoph Hellwig 			if (level ==  LEVEL_LINEAR)
1004f5b246bSChristoph Hellwig 				pername = "linear";
1014f5b246bSChristoph Hellwig 			else
1024f5b246bSChristoph Hellwig 				pername = "raid0";
1034f5b246bSChristoph Hellwig 			break;
1044f5b246bSChristoph Hellwig 		}
105df561f66SGustavo A. R. Silva 		fallthrough;
1064f5b246bSChristoph Hellwig 	case 1: /* the first device is numeric */
1074f5b246bSChristoph Hellwig 		str = str1;
108df561f66SGustavo A. R. Silva 		fallthrough;
1094f5b246bSChristoph Hellwig 	case 0:
1104f5b246bSChristoph Hellwig 		md_setup_args[ent].level = LEVEL_NONE;
1114f5b246bSChristoph Hellwig 		pername="super-block";
1124f5b246bSChristoph Hellwig 	}
1134f5b246bSChristoph Hellwig 
1144f5b246bSChristoph Hellwig 	printk(KERN_INFO "md: Will configure md%d (%s) from %s, below.\n",
1154f5b246bSChristoph Hellwig 		minor, pername, str);
1164f5b246bSChristoph Hellwig 	md_setup_args[ent].device_names = str;
1174f5b246bSChristoph Hellwig 	md_setup_args[ent].partitioned = partitioned;
1184f5b246bSChristoph Hellwig 	md_setup_args[ent].minor = minor;
1194f5b246bSChristoph Hellwig 
1204f5b246bSChristoph Hellwig 	return 1;
1214f5b246bSChristoph Hellwig }
1224f5b246bSChristoph Hellwig 
md_setup_drive(struct md_setup_args * args)123d1100488SChristoph Hellwig static void __init md_setup_drive(struct md_setup_args *args)
1244f5b246bSChristoph Hellwig {
1257e0adbfcSChristoph Hellwig 	char *devname = args->device_names;
1267e0adbfcSChristoph Hellwig 	dev_t devices[MD_SB_DISKS + 1], mdev;
1277e0adbfcSChristoph Hellwig 	struct mdu_array_info_s ainfo = { };
1287e0adbfcSChristoph Hellwig 	struct mddev *mddev;
1297e0adbfcSChristoph Hellwig 	int err = 0, i;
1304f5b246bSChristoph Hellwig 	char name[16];
1314f5b246bSChristoph Hellwig 
1327e0adbfcSChristoph Hellwig 	if (args->partitioned) {
1337e0adbfcSChristoph Hellwig 		mdev = MKDEV(mdp_major, args->minor << MdpMinorShift);
1347e0adbfcSChristoph Hellwig 		sprintf(name, "md_d%d", args->minor);
1357e0adbfcSChristoph Hellwig 	} else {
1367e0adbfcSChristoph Hellwig 		mdev = MKDEV(MD_MAJOR, args->minor);
1377e0adbfcSChristoph Hellwig 		sprintf(name, "md%d", args->minor);
1387e0adbfcSChristoph Hellwig 	}
1394f5b246bSChristoph Hellwig 
1404f5b246bSChristoph Hellwig 	for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) {
1414f5b246bSChristoph Hellwig 		struct kstat stat;
1424f5b246bSChristoph Hellwig 		char *p;
1434f5b246bSChristoph Hellwig 		char comp_name[64];
1447e0adbfcSChristoph Hellwig 		dev_t dev;
1454f5b246bSChristoph Hellwig 
1464f5b246bSChristoph Hellwig 		p = strchr(devname, ',');
1474f5b246bSChristoph Hellwig 		if (p)
1484f5b246bSChristoph Hellwig 			*p++ = 0;
1494f5b246bSChristoph Hellwig 
150*cf056a43SChristoph Hellwig 		if (early_lookup_bdev(devname, &dev))
151*cf056a43SChristoph Hellwig 			dev = 0;
1524f5b246bSChristoph Hellwig 		if (strncmp(devname, "/dev/", 5) == 0)
1534f5b246bSChristoph Hellwig 			devname += 5;
1544f5b246bSChristoph Hellwig 		snprintf(comp_name, 63, "/dev/%s", devname);
155716308a5SChristoph Hellwig 		if (init_stat(comp_name, &stat, 0) == 0 && S_ISBLK(stat.mode))
1564f5b246bSChristoph Hellwig 			dev = new_decode_dev(stat.rdev);
1574f5b246bSChristoph Hellwig 		if (!dev) {
1587e0adbfcSChristoph Hellwig 			pr_warn("md: Unknown device name: %s\n", devname);
1594f5b246bSChristoph Hellwig 			break;
1604f5b246bSChristoph Hellwig 		}
1614f5b246bSChristoph Hellwig 
1624f5b246bSChristoph Hellwig 		devices[i] = dev;
1634f5b246bSChristoph Hellwig 		devname = p;
1644f5b246bSChristoph Hellwig 	}
1654f5b246bSChristoph Hellwig 	devices[i] = 0;
1664f5b246bSChristoph Hellwig 
1674f5b246bSChristoph Hellwig 	if (!i)
168d1100488SChristoph Hellwig 		return;
1694f5b246bSChristoph Hellwig 
1707e0adbfcSChristoph Hellwig 	pr_info("md: Loading %s: %s\n", name, args->device_names);
1714f5b246bSChristoph Hellwig 
17234cb92c0SChristoph Hellwig 	mddev = md_alloc(mdev, name);
17334cb92c0SChristoph Hellwig 	if (IS_ERR(mddev)) {
17434cb92c0SChristoph Hellwig 		pr_err("md: md_alloc failed - cannot start array %s\n", name);
175d1100488SChristoph Hellwig 		return;
1764f5b246bSChristoph Hellwig 	}
1777e0adbfcSChristoph Hellwig 
1787e0adbfcSChristoph Hellwig 	err = mddev_lock(mddev);
1797e0adbfcSChristoph Hellwig 	if (err) {
1807e0adbfcSChristoph Hellwig 		pr_err("md: failed to lock array %s\n", name);
18134cb92c0SChristoph Hellwig 		goto out_mddev_put;
1827e0adbfcSChristoph Hellwig 	}
1837e0adbfcSChristoph Hellwig 
1847e0adbfcSChristoph Hellwig 	if (!list_empty(&mddev->disks) || mddev->raid_disks) {
1857e0adbfcSChristoph Hellwig 		pr_warn("md: Ignoring %s, already autodetected. (Use raid=noautodetect)\n",
1867e0adbfcSChristoph Hellwig 		       name);
1877e0adbfcSChristoph Hellwig 		goto out_unlock;
1884f5b246bSChristoph Hellwig 	}
1894f5b246bSChristoph Hellwig 
190d1100488SChristoph Hellwig 	if (args->level != LEVEL_NONE) {
1914f5b246bSChristoph Hellwig 		/* non-persistent */
192d1100488SChristoph Hellwig 		ainfo.level = args->level;
1937e0adbfcSChristoph Hellwig 		ainfo.md_minor = args->minor;
1947e0adbfcSChristoph Hellwig 		ainfo.not_persistent = 1;
1957e0adbfcSChristoph Hellwig 		ainfo.state = (1 << MD_SB_CLEAN);
1967e0adbfcSChristoph Hellwig 		ainfo.chunk_size = args->chunk;
1974f5b246bSChristoph Hellwig 		while (devices[ainfo.raid_disks])
1984f5b246bSChristoph Hellwig 			ainfo.raid_disks++;
1997e0adbfcSChristoph Hellwig 	}
2004f5b246bSChristoph Hellwig 
2017e0adbfcSChristoph Hellwig 	err = md_set_array_info(mddev, &ainfo);
2027e0adbfcSChristoph Hellwig 
2037e0adbfcSChristoph Hellwig 	for (i = 0; i <= MD_SB_DISKS && devices[i]; i++) {
2047e0adbfcSChristoph Hellwig 		struct mdu_disk_info_s dinfo = {
2057e0adbfcSChristoph Hellwig 			.major	= MAJOR(devices[i]),
2067e0adbfcSChristoph Hellwig 			.minor	= MINOR(devices[i]),
2077e0adbfcSChristoph Hellwig 		};
2087e0adbfcSChristoph Hellwig 
2097e0adbfcSChristoph Hellwig 		if (args->level != LEVEL_NONE) {
2104f5b246bSChristoph Hellwig 			dinfo.number = i;
2114f5b246bSChristoph Hellwig 			dinfo.raid_disk = i;
2127e0adbfcSChristoph Hellwig 			dinfo.state =
2137e0adbfcSChristoph Hellwig 				(1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC);
2144f5b246bSChristoph Hellwig 		}
2157e0adbfcSChristoph Hellwig 
2167e0adbfcSChristoph Hellwig 		md_add_new_disk(mddev, &dinfo);
2174f5b246bSChristoph Hellwig 	}
2187e0adbfcSChristoph Hellwig 
2194f5b246bSChristoph Hellwig 	if (!err)
2207e0adbfcSChristoph Hellwig 		err = do_md_run(mddev);
2214f5b246bSChristoph Hellwig 	if (err)
2227e0adbfcSChristoph Hellwig 		pr_warn("md: starting %s failed\n", name);
2237e0adbfcSChristoph Hellwig out_unlock:
2247e0adbfcSChristoph Hellwig 	mddev_unlock(mddev);
22534cb92c0SChristoph Hellwig out_mddev_put:
22634cb92c0SChristoph Hellwig 	mddev_put(mddev);
2274f5b246bSChristoph Hellwig }
2284f5b246bSChristoph Hellwig 
raid_setup(char * str)2294f5b246bSChristoph Hellwig static int __init raid_setup(char *str)
2304f5b246bSChristoph Hellwig {
2314f5b246bSChristoph Hellwig 	int len, pos;
2324f5b246bSChristoph Hellwig 
2334f5b246bSChristoph Hellwig 	len = strlen(str) + 1;
2344f5b246bSChristoph Hellwig 	pos = 0;
2354f5b246bSChristoph Hellwig 
2364f5b246bSChristoph Hellwig 	while (pos < len) {
2374f5b246bSChristoph Hellwig 		char *comma = strchr(str+pos, ',');
2384f5b246bSChristoph Hellwig 		int wlen;
2394f5b246bSChristoph Hellwig 		if (comma)
2404f5b246bSChristoph Hellwig 			wlen = (comma-str)-pos;
2414f5b246bSChristoph Hellwig 		else	wlen = (len-1)-pos;
2424f5b246bSChristoph Hellwig 
2434f5b246bSChristoph Hellwig 		if (!strncmp(str, "noautodetect", wlen))
2444f5b246bSChristoph Hellwig 			raid_noautodetect = 1;
2454f5b246bSChristoph Hellwig 		if (!strncmp(str, "autodetect", wlen))
2464f5b246bSChristoph Hellwig 			raid_noautodetect = 0;
2474f5b246bSChristoph Hellwig 		if (strncmp(str, "partitionable", wlen)==0)
2484f5b246bSChristoph Hellwig 			raid_autopart = 1;
2494f5b246bSChristoph Hellwig 		if (strncmp(str, "part", wlen)==0)
2504f5b246bSChristoph Hellwig 			raid_autopart = 1;
2514f5b246bSChristoph Hellwig 		pos += wlen+1;
2524f5b246bSChristoph Hellwig 	}
2534f5b246bSChristoph Hellwig 	return 1;
2544f5b246bSChristoph Hellwig }
2554f5b246bSChristoph Hellwig 
2564f5b246bSChristoph Hellwig __setup("raid=", raid_setup);
2574f5b246bSChristoph Hellwig __setup("md=", md_setup);
2584f5b246bSChristoph Hellwig 
autodetect_raid(void)2594f5b246bSChristoph Hellwig static void __init autodetect_raid(void)
2604f5b246bSChristoph Hellwig {
2614f5b246bSChristoph Hellwig 	/*
2624f5b246bSChristoph Hellwig 	 * Since we don't want to detect and use half a raid array, we need to
2634f5b246bSChristoph Hellwig 	 * wait for the known devices to complete their probing
2644f5b246bSChristoph Hellwig 	 */
2654f5b246bSChristoph Hellwig 	printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n");
2664f5b246bSChristoph Hellwig 	printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n");
2674f5b246bSChristoph Hellwig 
2684f5b246bSChristoph Hellwig 	wait_for_device_probe();
269d82fa81cSChristoph Hellwig 	md_autostart_arrays(raid_autopart);
2704f5b246bSChristoph Hellwig }
2714f5b246bSChristoph Hellwig 
md_run_setup(void)2724f5b246bSChristoph Hellwig void __init md_run_setup(void)
2734f5b246bSChristoph Hellwig {
274d1100488SChristoph Hellwig 	int ent;
275d1100488SChristoph Hellwig 
2764f5b246bSChristoph Hellwig 	if (raid_noautodetect)
2774f5b246bSChristoph Hellwig 		printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=autodetect will force)\n");
2784f5b246bSChristoph Hellwig 	else
2794f5b246bSChristoph Hellwig 		autodetect_raid();
280d1100488SChristoph Hellwig 
281d1100488SChristoph Hellwig 	for (ent = 0; ent < md_setup_ents; ent++)
282d1100488SChristoph Hellwig 		md_setup_drive(&md_setup_args[ent]);
2834f5b246bSChristoph Hellwig }
284