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