18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
48c2ecf20Sopenharmony_ci#include <linux/init.h>
58c2ecf20Sopenharmony_ci#include <linux/mount.h>
68c2ecf20Sopenharmony_ci#include <linux/major.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/init_syscalls.h>
98c2ecf20Sopenharmony_ci#include <linux/raid/detect.h>
108c2ecf20Sopenharmony_ci#include <linux/raid/md_u.h>
118c2ecf20Sopenharmony_ci#include <linux/raid/md_p.h>
128c2ecf20Sopenharmony_ci#include "md.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * When md (and any require personalities) are compiled into the kernel
168c2ecf20Sopenharmony_ci * (not a module), arrays can be assembles are boot time using with AUTODETECT
178c2ecf20Sopenharmony_ci * where specially marked partitions are registered with md_autodetect_dev(),
188c2ecf20Sopenharmony_ci * and with MD_BOOT where devices to be collected are given on the boot line
198c2ecf20Sopenharmony_ci * with md=.....
208c2ecf20Sopenharmony_ci * The code for that is here.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#ifdef CONFIG_MD_AUTODETECT
248c2ecf20Sopenharmony_cistatic int __initdata raid_noautodetect;
258c2ecf20Sopenharmony_ci#else
268c2ecf20Sopenharmony_cistatic int __initdata raid_noautodetect=1;
278c2ecf20Sopenharmony_ci#endif
288c2ecf20Sopenharmony_cistatic int __initdata raid_autopart;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct md_setup_args {
318c2ecf20Sopenharmony_ci	int minor;
328c2ecf20Sopenharmony_ci	int partitioned;
338c2ecf20Sopenharmony_ci	int level;
348c2ecf20Sopenharmony_ci	int chunk;
358c2ecf20Sopenharmony_ci	char *device_names;
368c2ecf20Sopenharmony_ci} md_setup_args[256] __initdata;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int md_setup_ents __initdata;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Parse the command-line parameters given our kernel, but do not
428c2ecf20Sopenharmony_ci * actually try to invoke the MD device now; that is handled by
438c2ecf20Sopenharmony_ci * md_setup_drive after the low-level disk drivers have initialised.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * 27/11/1999: Fixed to work correctly with the 2.3 kernel (which
468c2ecf20Sopenharmony_ci *             assigns the task of parsing integer arguments to the
478c2ecf20Sopenharmony_ci *             invoked program now).  Added ability to initialise all
488c2ecf20Sopenharmony_ci *             the MD devices (by specifying multiple "md=" lines)
498c2ecf20Sopenharmony_ci *             instead of just one.  -- KTK
508c2ecf20Sopenharmony_ci * 18May2000: Added support for persistent-superblock arrays:
518c2ecf20Sopenharmony_ci *             md=n,0,factor,fault,device-list   uses RAID0 for device n
528c2ecf20Sopenharmony_ci *             md=n,-1,factor,fault,device-list  uses LINEAR for device n
538c2ecf20Sopenharmony_ci *             md=n,device-list      reads a RAID superblock from the devices
548c2ecf20Sopenharmony_ci *             elements in device-list are read by name_to_kdev_t so can be
558c2ecf20Sopenharmony_ci *             a hex number or something like /dev/hda1 /dev/sdb
568c2ecf20Sopenharmony_ci * 2001-06-03: Dave Cinege <dcinege@psychosis.com>
578c2ecf20Sopenharmony_ci *		Shifted name_to_kdev_t() and related operations to md_set_drive()
588c2ecf20Sopenharmony_ci *		for later execution. Rewrote section to make devfs compatible.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic int __init md_setup(char *str)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	int minor, level, factor, fault, partitioned = 0;
638c2ecf20Sopenharmony_ci	char *pername = "";
648c2ecf20Sopenharmony_ci	char *str1;
658c2ecf20Sopenharmony_ci	int ent;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (*str == 'd') {
688c2ecf20Sopenharmony_ci		partitioned = 1;
698c2ecf20Sopenharmony_ci		str++;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci	if (get_option(&str, &minor) != 2) {	/* MD Number */
728c2ecf20Sopenharmony_ci		printk(KERN_WARNING "md: Too few arguments supplied to md=.\n");
738c2ecf20Sopenharmony_ci		return 0;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	str1 = str;
768c2ecf20Sopenharmony_ci	for (ent=0 ; ent< md_setup_ents ; ent++)
778c2ecf20Sopenharmony_ci		if (md_setup_args[ent].minor == minor &&
788c2ecf20Sopenharmony_ci		    md_setup_args[ent].partitioned == partitioned) {
798c2ecf20Sopenharmony_ci			printk(KERN_WARNING "md: md=%s%d, Specified more than once. "
808c2ecf20Sopenharmony_ci			       "Replacing previous definition.\n", partitioned?"d":"", minor);
818c2ecf20Sopenharmony_ci			break;
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci	if (ent >= ARRAY_SIZE(md_setup_args)) {
848c2ecf20Sopenharmony_ci		printk(KERN_WARNING "md: md=%s%d - too many md initialisations\n", partitioned?"d":"", minor);
858c2ecf20Sopenharmony_ci		return 0;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	if (ent >= md_setup_ents)
888c2ecf20Sopenharmony_ci		md_setup_ents++;
898c2ecf20Sopenharmony_ci	switch (get_option(&str, &level)) {	/* RAID level */
908c2ecf20Sopenharmony_ci	case 2: /* could be 0 or -1.. */
918c2ecf20Sopenharmony_ci		if (level == 0 || level == LEVEL_LINEAR) {
928c2ecf20Sopenharmony_ci			if (get_option(&str, &factor) != 2 ||	/* Chunk Size */
938c2ecf20Sopenharmony_ci					get_option(&str, &fault) != 2) {
948c2ecf20Sopenharmony_ci				printk(KERN_WARNING "md: Too few arguments supplied to md=.\n");
958c2ecf20Sopenharmony_ci				return 0;
968c2ecf20Sopenharmony_ci			}
978c2ecf20Sopenharmony_ci			md_setup_args[ent].level = level;
988c2ecf20Sopenharmony_ci			md_setup_args[ent].chunk = 1 << (factor+12);
998c2ecf20Sopenharmony_ci			if (level ==  LEVEL_LINEAR)
1008c2ecf20Sopenharmony_ci				pername = "linear";
1018c2ecf20Sopenharmony_ci			else
1028c2ecf20Sopenharmony_ci				pername = "raid0";
1038c2ecf20Sopenharmony_ci			break;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci		fallthrough;
1068c2ecf20Sopenharmony_ci	case 1: /* the first device is numeric */
1078c2ecf20Sopenharmony_ci		str = str1;
1088c2ecf20Sopenharmony_ci		fallthrough;
1098c2ecf20Sopenharmony_ci	case 0:
1108c2ecf20Sopenharmony_ci		md_setup_args[ent].level = LEVEL_NONE;
1118c2ecf20Sopenharmony_ci		pername="super-block";
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	printk(KERN_INFO "md: Will configure md%d (%s) from %s, below.\n",
1158c2ecf20Sopenharmony_ci		minor, pername, str);
1168c2ecf20Sopenharmony_ci	md_setup_args[ent].device_names = str;
1178c2ecf20Sopenharmony_ci	md_setup_args[ent].partitioned = partitioned;
1188c2ecf20Sopenharmony_ci	md_setup_args[ent].minor = minor;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return 1;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic void __init md_setup_drive(struct md_setup_args *args)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	char *devname = args->device_names;
1268c2ecf20Sopenharmony_ci	dev_t devices[MD_SB_DISKS + 1], mdev;
1278c2ecf20Sopenharmony_ci	struct mdu_array_info_s ainfo = { };
1288c2ecf20Sopenharmony_ci	struct block_device *bdev;
1298c2ecf20Sopenharmony_ci	struct mddev *mddev;
1308c2ecf20Sopenharmony_ci	int err = 0, i;
1318c2ecf20Sopenharmony_ci	char name[16];
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (args->partitioned) {
1348c2ecf20Sopenharmony_ci		mdev = MKDEV(mdp_major, args->minor << MdpMinorShift);
1358c2ecf20Sopenharmony_ci		sprintf(name, "md_d%d", args->minor);
1368c2ecf20Sopenharmony_ci	} else {
1378c2ecf20Sopenharmony_ci		mdev = MKDEV(MD_MAJOR, args->minor);
1388c2ecf20Sopenharmony_ci		sprintf(name, "md%d", args->minor);
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	for (i = 0; i < MD_SB_DISKS && devname != NULL; i++) {
1428c2ecf20Sopenharmony_ci		struct kstat stat;
1438c2ecf20Sopenharmony_ci		char *p;
1448c2ecf20Sopenharmony_ci		char comp_name[64];
1458c2ecf20Sopenharmony_ci		dev_t dev;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		p = strchr(devname, ',');
1488c2ecf20Sopenharmony_ci		if (p)
1498c2ecf20Sopenharmony_ci			*p++ = 0;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		dev = name_to_dev_t(devname);
1528c2ecf20Sopenharmony_ci		if (strncmp(devname, "/dev/", 5) == 0)
1538c2ecf20Sopenharmony_ci			devname += 5;
1548c2ecf20Sopenharmony_ci		snprintf(comp_name, 63, "/dev/%s", devname);
1558c2ecf20Sopenharmony_ci		if (init_stat(comp_name, &stat, 0) == 0 && S_ISBLK(stat.mode))
1568c2ecf20Sopenharmony_ci			dev = new_decode_dev(stat.rdev);
1578c2ecf20Sopenharmony_ci		if (!dev) {
1588c2ecf20Sopenharmony_ci			pr_warn("md: Unknown device name: %s\n", devname);
1598c2ecf20Sopenharmony_ci			break;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		devices[i] = dev;
1638c2ecf20Sopenharmony_ci		devname = p;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	devices[i] = 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (!i)
1688c2ecf20Sopenharmony_ci		return;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	pr_info("md: Loading %s: %s\n", name, args->device_names);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	bdev = blkdev_get_by_dev(mdev, FMODE_READ, NULL);
1738c2ecf20Sopenharmony_ci	if (IS_ERR(bdev)) {
1748c2ecf20Sopenharmony_ci		pr_err("md: open failed - cannot start array %s\n", name);
1758c2ecf20Sopenharmony_ci		return;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	err = -EIO;
1798c2ecf20Sopenharmony_ci	if (WARN(bdev->bd_disk->fops != &md_fops,
1808c2ecf20Sopenharmony_ci			"Opening block device %x resulted in non-md device\n",
1818c2ecf20Sopenharmony_ci			mdev))
1828c2ecf20Sopenharmony_ci		goto out_blkdev_put;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	mddev = bdev->bd_disk->private_data;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	err = mddev_lock(mddev);
1878c2ecf20Sopenharmony_ci	if (err) {
1888c2ecf20Sopenharmony_ci		pr_err("md: failed to lock array %s\n", name);
1898c2ecf20Sopenharmony_ci		goto out_blkdev_put;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!list_empty(&mddev->disks) || mddev->raid_disks) {
1938c2ecf20Sopenharmony_ci		pr_warn("md: Ignoring %s, already autodetected. (Use raid=noautodetect)\n",
1948c2ecf20Sopenharmony_ci		       name);
1958c2ecf20Sopenharmony_ci		goto out_unlock;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (args->level != LEVEL_NONE) {
1998c2ecf20Sopenharmony_ci		/* non-persistent */
2008c2ecf20Sopenharmony_ci		ainfo.level = args->level;
2018c2ecf20Sopenharmony_ci		ainfo.md_minor = args->minor;
2028c2ecf20Sopenharmony_ci		ainfo.not_persistent = 1;
2038c2ecf20Sopenharmony_ci		ainfo.state = (1 << MD_SB_CLEAN);
2048c2ecf20Sopenharmony_ci		ainfo.chunk_size = args->chunk;
2058c2ecf20Sopenharmony_ci		while (devices[ainfo.raid_disks])
2068c2ecf20Sopenharmony_ci			ainfo.raid_disks++;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	err = md_set_array_info(mddev, &ainfo);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (i = 0; i <= MD_SB_DISKS && devices[i]; i++) {
2128c2ecf20Sopenharmony_ci		struct mdu_disk_info_s dinfo = {
2138c2ecf20Sopenharmony_ci			.major	= MAJOR(devices[i]),
2148c2ecf20Sopenharmony_ci			.minor	= MINOR(devices[i]),
2158c2ecf20Sopenharmony_ci		};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		if (args->level != LEVEL_NONE) {
2188c2ecf20Sopenharmony_ci			dinfo.number = i;
2198c2ecf20Sopenharmony_ci			dinfo.raid_disk = i;
2208c2ecf20Sopenharmony_ci			dinfo.state =
2218c2ecf20Sopenharmony_ci				(1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC);
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		md_add_new_disk(mddev, &dinfo);
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!err)
2288c2ecf20Sopenharmony_ci		err = do_md_run(mddev);
2298c2ecf20Sopenharmony_ci	if (err)
2308c2ecf20Sopenharmony_ci		pr_warn("md: starting %s failed\n", name);
2318c2ecf20Sopenharmony_ciout_unlock:
2328c2ecf20Sopenharmony_ci	mddev_unlock(mddev);
2338c2ecf20Sopenharmony_ciout_blkdev_put:
2348c2ecf20Sopenharmony_ci	blkdev_put(bdev, FMODE_READ);
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int __init raid_setup(char *str)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int len, pos;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	len = strlen(str) + 1;
2428c2ecf20Sopenharmony_ci	pos = 0;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	while (pos < len) {
2458c2ecf20Sopenharmony_ci		char *comma = strchr(str+pos, ',');
2468c2ecf20Sopenharmony_ci		int wlen;
2478c2ecf20Sopenharmony_ci		if (comma)
2488c2ecf20Sopenharmony_ci			wlen = (comma-str)-pos;
2498c2ecf20Sopenharmony_ci		else	wlen = (len-1)-pos;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		if (!strncmp(str, "noautodetect", wlen))
2528c2ecf20Sopenharmony_ci			raid_noautodetect = 1;
2538c2ecf20Sopenharmony_ci		if (!strncmp(str, "autodetect", wlen))
2548c2ecf20Sopenharmony_ci			raid_noautodetect = 0;
2558c2ecf20Sopenharmony_ci		if (strncmp(str, "partitionable", wlen)==0)
2568c2ecf20Sopenharmony_ci			raid_autopart = 1;
2578c2ecf20Sopenharmony_ci		if (strncmp(str, "part", wlen)==0)
2588c2ecf20Sopenharmony_ci			raid_autopart = 1;
2598c2ecf20Sopenharmony_ci		pos += wlen+1;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci	return 1;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci__setup("raid=", raid_setup);
2658c2ecf20Sopenharmony_ci__setup("md=", md_setup);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void __init autodetect_raid(void)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	/*
2708c2ecf20Sopenharmony_ci	 * Since we don't want to detect and use half a raid array, we need to
2718c2ecf20Sopenharmony_ci	 * wait for the known devices to complete their probing
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n");
2748c2ecf20Sopenharmony_ci	printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n");
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	wait_for_device_probe();
2778c2ecf20Sopenharmony_ci	md_autostart_arrays(raid_autopart);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_civoid __init md_run_setup(void)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	int ent;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (raid_noautodetect)
2858c2ecf20Sopenharmony_ci		printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=autodetect will force)\n");
2868c2ecf20Sopenharmony_ci	else
2878c2ecf20Sopenharmony_ci		autodetect_raid();
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	for (ent = 0; ent < md_setup_ents; ent++)
2908c2ecf20Sopenharmony_ci		md_setup_drive(&md_setup_args[ent]);
2918c2ecf20Sopenharmony_ci}
292