18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci linear.c : Multiple Devices driver for Linux 48c2ecf20Sopenharmony_ci Copyright (C) 1994-96 Marc ZYNGIER 58c2ecf20Sopenharmony_ci <zyngier@ufr-info-p7.ibp.fr> or 68c2ecf20Sopenharmony_ci <maz@gloups.fdn.fr> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci Linear mode management functions. 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci*/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 138c2ecf20Sopenharmony_ci#include <linux/raid/md_u.h> 148c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <trace/events/block.h> 188c2ecf20Sopenharmony_ci#include "md.h" 198c2ecf20Sopenharmony_ci#include "md-linear.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * find which device holds a particular offset 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int lo, mid, hi; 278c2ecf20Sopenharmony_ci struct linear_conf *conf; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci lo = 0; 308c2ecf20Sopenharmony_ci hi = mddev->raid_disks - 1; 318c2ecf20Sopenharmony_ci conf = mddev->private; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* 348c2ecf20Sopenharmony_ci * Binary Search 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci while (hi > lo) { 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci mid = (hi + lo) / 2; 408c2ecf20Sopenharmony_ci if (sector < conf->disks[mid].end_sector) 418c2ecf20Sopenharmony_ci hi = mid; 428c2ecf20Sopenharmony_ci else 438c2ecf20Sopenharmony_ci lo = mid + 1; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return conf->disks + lo; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disks) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct linear_conf *conf; 528c2ecf20Sopenharmony_ci sector_t array_sectors; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci conf = mddev->private; 558c2ecf20Sopenharmony_ci WARN_ONCE(sectors || raid_disks, 568c2ecf20Sopenharmony_ci "%s does not support generic reshape\n", __func__); 578c2ecf20Sopenharmony_ci array_sectors = conf->array_sectors; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return array_sectors; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct linear_conf *conf; 658c2ecf20Sopenharmony_ci struct md_rdev *rdev; 668c2ecf20Sopenharmony_ci int i, cnt; 678c2ecf20Sopenharmony_ci bool discard_supported = false; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci conf = kzalloc(struct_size(conf, disks, raid_disks), GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!conf) 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci cnt = 0; 748c2ecf20Sopenharmony_ci conf->array_sectors = 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci rdev_for_each(rdev, mddev) { 778c2ecf20Sopenharmony_ci int j = rdev->raid_disk; 788c2ecf20Sopenharmony_ci struct dev_info *disk = conf->disks + j; 798c2ecf20Sopenharmony_ci sector_t sectors; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (j < 0 || j >= raid_disks || disk->rdev) { 828c2ecf20Sopenharmony_ci pr_warn("md/linear:%s: disk numbering problem. Aborting!\n", 838c2ecf20Sopenharmony_ci mdname(mddev)); 848c2ecf20Sopenharmony_ci goto out; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci disk->rdev = rdev; 888c2ecf20Sopenharmony_ci if (mddev->chunk_sectors) { 898c2ecf20Sopenharmony_ci sectors = rdev->sectors; 908c2ecf20Sopenharmony_ci sector_div(sectors, mddev->chunk_sectors); 918c2ecf20Sopenharmony_ci rdev->sectors = sectors * mddev->chunk_sectors; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci disk_stack_limits(mddev->gendisk, rdev->bdev, 958c2ecf20Sopenharmony_ci rdev->data_offset << 9); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci conf->array_sectors += rdev->sectors; 988c2ecf20Sopenharmony_ci cnt++; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (blk_queue_discard(bdev_get_queue(rdev->bdev))) 1018c2ecf20Sopenharmony_ci discard_supported = true; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci if (cnt != raid_disks) { 1048c2ecf20Sopenharmony_ci pr_warn("md/linear:%s: not enough drives present. Aborting!\n", 1058c2ecf20Sopenharmony_ci mdname(mddev)); 1068c2ecf20Sopenharmony_ci goto out; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!discard_supported) 1108c2ecf20Sopenharmony_ci blk_queue_flag_clear(QUEUE_FLAG_DISCARD, mddev->queue); 1118c2ecf20Sopenharmony_ci else 1128c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_DISCARD, mddev->queue); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * Here we calculate the device offsets. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci conf->disks[0].end_sector = conf->disks[0].rdev->sectors; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 1; i < raid_disks; i++) 1208c2ecf20Sopenharmony_ci conf->disks[i].end_sector = 1218c2ecf20Sopenharmony_ci conf->disks[i-1].end_sector + 1228c2ecf20Sopenharmony_ci conf->disks[i].rdev->sectors; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * conf->raid_disks is copy of mddev->raid_disks. The reason to 1268c2ecf20Sopenharmony_ci * keep a copy of mddev->raid_disks in struct linear_conf is, 1278c2ecf20Sopenharmony_ci * mddev->raid_disks may not be consistent with pointers number of 1288c2ecf20Sopenharmony_ci * conf->disks[] when it is updated in linear_add() and used to 1298c2ecf20Sopenharmony_ci * iterate old conf->disks[] earray in linear_congested(). 1308c2ecf20Sopenharmony_ci * Here conf->raid_disks is always consitent with number of 1318c2ecf20Sopenharmony_ci * pointers in conf->disks[] array, and mddev->private is updated 1328c2ecf20Sopenharmony_ci * with rcu_assign_pointer() in linear_addr(), such race can be 1338c2ecf20Sopenharmony_ci * avoided. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci conf->raid_disks = raid_disks; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return conf; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciout: 1408c2ecf20Sopenharmony_ci kfree(conf); 1418c2ecf20Sopenharmony_ci return NULL; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int linear_run (struct mddev *mddev) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct linear_conf *conf; 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (md_check_no_bitmap(mddev)) 1508c2ecf20Sopenharmony_ci return -EINVAL; 1518c2ecf20Sopenharmony_ci conf = linear_conf(mddev, mddev->raid_disks); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!conf) 1548c2ecf20Sopenharmony_ci return 1; 1558c2ecf20Sopenharmony_ci mddev->private = conf; 1568c2ecf20Sopenharmony_ci md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ret = md_integrity_register(mddev); 1598c2ecf20Sopenharmony_ci if (ret) { 1608c2ecf20Sopenharmony_ci kfree(conf); 1618c2ecf20Sopenharmony_ci mddev->private = NULL; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int linear_add(struct mddev *mddev, struct md_rdev *rdev) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci /* Adding a drive to a linear array allows the array to grow. 1698c2ecf20Sopenharmony_ci * It is permitted if the new drive has a matching superblock 1708c2ecf20Sopenharmony_ci * already on it, with raid_disk equal to raid_disks. 1718c2ecf20Sopenharmony_ci * It is achieved by creating a new linear_private_data structure 1728c2ecf20Sopenharmony_ci * and swapping it in in-place of the current one. 1738c2ecf20Sopenharmony_ci * The current one is never freed until the array is stopped. 1748c2ecf20Sopenharmony_ci * This avoids races. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci struct linear_conf *newconf, *oldconf; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (rdev->saved_raid_disk != mddev->raid_disks) 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci rdev->raid_disk = rdev->saved_raid_disk; 1828c2ecf20Sopenharmony_ci rdev->saved_raid_disk = -1; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci newconf = linear_conf(mddev,mddev->raid_disks+1); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!newconf) 1878c2ecf20Sopenharmony_ci return -ENOMEM; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* newconf->raid_disks already keeps a copy of * the increased 1908c2ecf20Sopenharmony_ci * value of mddev->raid_disks, WARN_ONCE() is just used to make 1918c2ecf20Sopenharmony_ci * sure of this. It is possible that oldconf is still referenced 1928c2ecf20Sopenharmony_ci * in linear_congested(), therefore kfree_rcu() is used to free 1938c2ecf20Sopenharmony_ci * oldconf until no one uses it anymore. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci mddev_suspend(mddev); 1968c2ecf20Sopenharmony_ci oldconf = rcu_dereference_protected(mddev->private, 1978c2ecf20Sopenharmony_ci lockdep_is_held(&mddev->reconfig_mutex)); 1988c2ecf20Sopenharmony_ci mddev->raid_disks++; 1998c2ecf20Sopenharmony_ci WARN_ONCE(mddev->raid_disks != newconf->raid_disks, 2008c2ecf20Sopenharmony_ci "copied raid_disks doesn't match mddev->raid_disks"); 2018c2ecf20Sopenharmony_ci rcu_assign_pointer(mddev->private, newconf); 2028c2ecf20Sopenharmony_ci md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 2038c2ecf20Sopenharmony_ci set_capacity(mddev->gendisk, mddev->array_sectors); 2048c2ecf20Sopenharmony_ci mddev_resume(mddev); 2058c2ecf20Sopenharmony_ci revalidate_disk_size(mddev->gendisk, true); 2068c2ecf20Sopenharmony_ci kfree_rcu(oldconf, rcu); 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void linear_free(struct mddev *mddev, void *priv) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct linear_conf *conf = priv; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci kfree(conf); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic bool linear_make_request(struct mddev *mddev, struct bio *bio) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci char b[BDEVNAME_SIZE]; 2208c2ecf20Sopenharmony_ci struct dev_info *tmp_dev; 2218c2ecf20Sopenharmony_ci sector_t start_sector, end_sector, data_offset; 2228c2ecf20Sopenharmony_ci sector_t bio_sector = bio->bi_iter.bi_sector; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (unlikely(bio->bi_opf & REQ_PREFLUSH) 2258c2ecf20Sopenharmony_ci && md_flush_request(mddev, bio)) 2268c2ecf20Sopenharmony_ci return true; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci tmp_dev = which_dev(mddev, bio_sector); 2298c2ecf20Sopenharmony_ci start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; 2308c2ecf20Sopenharmony_ci end_sector = tmp_dev->end_sector; 2318c2ecf20Sopenharmony_ci data_offset = tmp_dev->rdev->data_offset; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (unlikely(bio_sector >= end_sector || 2348c2ecf20Sopenharmony_ci bio_sector < start_sector)) 2358c2ecf20Sopenharmony_ci goto out_of_bounds; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (unlikely(is_mddev_broken(tmp_dev->rdev, "linear"))) { 2388c2ecf20Sopenharmony_ci bio_io_error(bio); 2398c2ecf20Sopenharmony_ci return true; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (unlikely(bio_end_sector(bio) > end_sector)) { 2438c2ecf20Sopenharmony_ci /* This bio crosses a device boundary, so we have to split it */ 2448c2ecf20Sopenharmony_ci struct bio *split = bio_split(bio, end_sector - bio_sector, 2458c2ecf20Sopenharmony_ci GFP_NOIO, &mddev->bio_set); 2468c2ecf20Sopenharmony_ci bio_chain(split, bio); 2478c2ecf20Sopenharmony_ci submit_bio_noacct(bio); 2488c2ecf20Sopenharmony_ci bio = split; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci bio_set_dev(bio, tmp_dev->rdev->bdev); 2528c2ecf20Sopenharmony_ci bio->bi_iter.bi_sector = bio->bi_iter.bi_sector - 2538c2ecf20Sopenharmony_ci start_sector + data_offset; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && 2568c2ecf20Sopenharmony_ci !blk_queue_discard(bio->bi_disk->queue))) { 2578c2ecf20Sopenharmony_ci /* Just ignore it */ 2588c2ecf20Sopenharmony_ci bio_endio(bio); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci if (mddev->gendisk) 2618c2ecf20Sopenharmony_ci trace_block_bio_remap(bio->bi_disk->queue, 2628c2ecf20Sopenharmony_ci bio, disk_devt(mddev->gendisk), 2638c2ecf20Sopenharmony_ci bio_sector); 2648c2ecf20Sopenharmony_ci mddev_check_writesame(mddev, bio); 2658c2ecf20Sopenharmony_ci mddev_check_write_zeroes(mddev, bio); 2668c2ecf20Sopenharmony_ci submit_bio_noacct(bio); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci return true; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciout_of_bounds: 2718c2ecf20Sopenharmony_ci pr_err("md/linear:%s: make_request: Sector %llu out of bounds on dev %s: %llu sectors, offset %llu\n", 2728c2ecf20Sopenharmony_ci mdname(mddev), 2738c2ecf20Sopenharmony_ci (unsigned long long)bio->bi_iter.bi_sector, 2748c2ecf20Sopenharmony_ci bdevname(tmp_dev->rdev->bdev, b), 2758c2ecf20Sopenharmony_ci (unsigned long long)tmp_dev->rdev->sectors, 2768c2ecf20Sopenharmony_ci (unsigned long long)start_sector); 2778c2ecf20Sopenharmony_ci bio_io_error(bio); 2788c2ecf20Sopenharmony_ci return true; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void linear_status (struct seq_file *seq, struct mddev *mddev) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci seq_printf(seq, " %dk rounding", mddev->chunk_sectors / 2); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void linear_quiesce(struct mddev *mddev, int state) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic struct md_personality linear_personality = 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci .name = "linear", 2938c2ecf20Sopenharmony_ci .level = LEVEL_LINEAR, 2948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2958c2ecf20Sopenharmony_ci .make_request = linear_make_request, 2968c2ecf20Sopenharmony_ci .run = linear_run, 2978c2ecf20Sopenharmony_ci .free = linear_free, 2988c2ecf20Sopenharmony_ci .status = linear_status, 2998c2ecf20Sopenharmony_ci .hot_add_disk = linear_add, 3008c2ecf20Sopenharmony_ci .size = linear_size, 3018c2ecf20Sopenharmony_ci .quiesce = linear_quiesce, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int __init linear_init (void) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci return register_md_personality (&linear_personality); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic void linear_exit (void) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci unregister_md_personality (&linear_personality); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cimodule_init(linear_init); 3158c2ecf20Sopenharmony_cimodule_exit(linear_exit); 3168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linear device concatenation personality for MD"); 3188c2ecf20Sopenharmony_ciMODULE_ALIAS("md-personality-1"); /* LINEAR - deprecated*/ 3198c2ecf20Sopenharmony_ciMODULE_ALIAS("md-linear"); 3208c2ecf20Sopenharmony_ciMODULE_ALIAS("md-level--1"); 321