162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci linear.c : Multiple Devices driver for Linux 462306a36Sopenharmony_ci Copyright (C) 1994-96 Marc ZYNGIER 562306a36Sopenharmony_ci <zyngier@ufr-info-p7.ibp.fr> or 662306a36Sopenharmony_ci <maz@gloups.fdn.fr> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci Linear mode management functions. 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci*/ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/blkdev.h> 1362306a36Sopenharmony_ci#include <linux/raid/md_u.h> 1462306a36Sopenharmony_ci#include <linux/seq_file.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <trace/events/block.h> 1862306a36Sopenharmony_ci#include "md.h" 1962306a36Sopenharmony_ci#include "md-linear.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * find which device holds a particular offset 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci int lo, mid, hi; 2762306a36Sopenharmony_ci struct linear_conf *conf; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci lo = 0; 3062306a36Sopenharmony_ci hi = mddev->raid_disks - 1; 3162306a36Sopenharmony_ci conf = mddev->private; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* 3462306a36Sopenharmony_ci * Binary Search 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci while (hi > lo) { 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci mid = (hi + lo) / 2; 4062306a36Sopenharmony_ci if (sector < conf->disks[mid].end_sector) 4162306a36Sopenharmony_ci hi = mid; 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci lo = mid + 1; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return conf->disks + lo; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disks) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct linear_conf *conf; 5262306a36Sopenharmony_ci sector_t array_sectors; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci conf = mddev->private; 5562306a36Sopenharmony_ci WARN_ONCE(sectors || raid_disks, 5662306a36Sopenharmony_ci "%s does not support generic reshape\n", __func__); 5762306a36Sopenharmony_ci array_sectors = conf->array_sectors; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return array_sectors; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct linear_conf *conf; 6562306a36Sopenharmony_ci struct md_rdev *rdev; 6662306a36Sopenharmony_ci int i, cnt; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci conf = kzalloc(struct_size(conf, disks, raid_disks), GFP_KERNEL); 6962306a36Sopenharmony_ci if (!conf) 7062306a36Sopenharmony_ci return NULL; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci cnt = 0; 7362306a36Sopenharmony_ci conf->array_sectors = 0; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci rdev_for_each(rdev, mddev) { 7662306a36Sopenharmony_ci int j = rdev->raid_disk; 7762306a36Sopenharmony_ci struct dev_info *disk = conf->disks + j; 7862306a36Sopenharmony_ci sector_t sectors; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (j < 0 || j >= raid_disks || disk->rdev) { 8162306a36Sopenharmony_ci pr_warn("md/linear:%s: disk numbering problem. Aborting!\n", 8262306a36Sopenharmony_ci mdname(mddev)); 8362306a36Sopenharmony_ci goto out; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci disk->rdev = rdev; 8762306a36Sopenharmony_ci if (mddev->chunk_sectors) { 8862306a36Sopenharmony_ci sectors = rdev->sectors; 8962306a36Sopenharmony_ci sector_div(sectors, mddev->chunk_sectors); 9062306a36Sopenharmony_ci rdev->sectors = sectors * mddev->chunk_sectors; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci disk_stack_limits(mddev->gendisk, rdev->bdev, 9462306a36Sopenharmony_ci rdev->data_offset << 9); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci conf->array_sectors += rdev->sectors; 9762306a36Sopenharmony_ci cnt++; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci if (cnt != raid_disks) { 10062306a36Sopenharmony_ci pr_warn("md/linear:%s: not enough drives present. Aborting!\n", 10162306a36Sopenharmony_ci mdname(mddev)); 10262306a36Sopenharmony_ci goto out; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * Here we calculate the device offsets. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci conf->disks[0].end_sector = conf->disks[0].rdev->sectors; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 1; i < raid_disks; i++) 11162306a36Sopenharmony_ci conf->disks[i].end_sector = 11262306a36Sopenharmony_ci conf->disks[i-1].end_sector + 11362306a36Sopenharmony_ci conf->disks[i].rdev->sectors; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * conf->raid_disks is copy of mddev->raid_disks. The reason to 11762306a36Sopenharmony_ci * keep a copy of mddev->raid_disks in struct linear_conf is, 11862306a36Sopenharmony_ci * mddev->raid_disks may not be consistent with pointers number of 11962306a36Sopenharmony_ci * conf->disks[] when it is updated in linear_add() and used to 12062306a36Sopenharmony_ci * iterate old conf->disks[] earray in linear_congested(). 12162306a36Sopenharmony_ci * Here conf->raid_disks is always consitent with number of 12262306a36Sopenharmony_ci * pointers in conf->disks[] array, and mddev->private is updated 12362306a36Sopenharmony_ci * with rcu_assign_pointer() in linear_addr(), such race can be 12462306a36Sopenharmony_ci * avoided. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci conf->raid_disks = raid_disks; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return conf; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciout: 13162306a36Sopenharmony_ci kfree(conf); 13262306a36Sopenharmony_ci return NULL; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int linear_run (struct mddev *mddev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct linear_conf *conf; 13862306a36Sopenharmony_ci int ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (md_check_no_bitmap(mddev)) 14162306a36Sopenharmony_ci return -EINVAL; 14262306a36Sopenharmony_ci conf = linear_conf(mddev, mddev->raid_disks); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!conf) 14562306a36Sopenharmony_ci return 1; 14662306a36Sopenharmony_ci mddev->private = conf; 14762306a36Sopenharmony_ci md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = md_integrity_register(mddev); 15062306a36Sopenharmony_ci if (ret) { 15162306a36Sopenharmony_ci kfree(conf); 15262306a36Sopenharmony_ci mddev->private = NULL; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci return ret; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int linear_add(struct mddev *mddev, struct md_rdev *rdev) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci /* Adding a drive to a linear array allows the array to grow. 16062306a36Sopenharmony_ci * It is permitted if the new drive has a matching superblock 16162306a36Sopenharmony_ci * already on it, with raid_disk equal to raid_disks. 16262306a36Sopenharmony_ci * It is achieved by creating a new linear_private_data structure 16362306a36Sopenharmony_ci * and swapping it in in-place of the current one. 16462306a36Sopenharmony_ci * The current one is never freed until the array is stopped. 16562306a36Sopenharmony_ci * This avoids races. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci struct linear_conf *newconf, *oldconf; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (rdev->saved_raid_disk != mddev->raid_disks) 17062306a36Sopenharmony_ci return -EINVAL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci rdev->raid_disk = rdev->saved_raid_disk; 17362306a36Sopenharmony_ci rdev->saved_raid_disk = -1; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci newconf = linear_conf(mddev,mddev->raid_disks+1); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (!newconf) 17862306a36Sopenharmony_ci return -ENOMEM; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* newconf->raid_disks already keeps a copy of * the increased 18162306a36Sopenharmony_ci * value of mddev->raid_disks, WARN_ONCE() is just used to make 18262306a36Sopenharmony_ci * sure of this. It is possible that oldconf is still referenced 18362306a36Sopenharmony_ci * in linear_congested(), therefore kfree_rcu() is used to free 18462306a36Sopenharmony_ci * oldconf until no one uses it anymore. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci mddev_suspend(mddev); 18762306a36Sopenharmony_ci oldconf = rcu_dereference_protected(mddev->private, 18862306a36Sopenharmony_ci lockdep_is_held(&mddev->reconfig_mutex)); 18962306a36Sopenharmony_ci mddev->raid_disks++; 19062306a36Sopenharmony_ci WARN_ONCE(mddev->raid_disks != newconf->raid_disks, 19162306a36Sopenharmony_ci "copied raid_disks doesn't match mddev->raid_disks"); 19262306a36Sopenharmony_ci rcu_assign_pointer(mddev->private, newconf); 19362306a36Sopenharmony_ci md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 19462306a36Sopenharmony_ci set_capacity_and_notify(mddev->gendisk, mddev->array_sectors); 19562306a36Sopenharmony_ci mddev_resume(mddev); 19662306a36Sopenharmony_ci kfree_rcu(oldconf, rcu); 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void linear_free(struct mddev *mddev, void *priv) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct linear_conf *conf = priv; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci kfree(conf); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic bool linear_make_request(struct mddev *mddev, struct bio *bio) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct dev_info *tmp_dev; 21062306a36Sopenharmony_ci sector_t start_sector, end_sector, data_offset; 21162306a36Sopenharmony_ci sector_t bio_sector = bio->bi_iter.bi_sector; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (unlikely(bio->bi_opf & REQ_PREFLUSH) 21462306a36Sopenharmony_ci && md_flush_request(mddev, bio)) 21562306a36Sopenharmony_ci return true; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci tmp_dev = which_dev(mddev, bio_sector); 21862306a36Sopenharmony_ci start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; 21962306a36Sopenharmony_ci end_sector = tmp_dev->end_sector; 22062306a36Sopenharmony_ci data_offset = tmp_dev->rdev->data_offset; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (unlikely(bio_sector >= end_sector || 22362306a36Sopenharmony_ci bio_sector < start_sector)) 22462306a36Sopenharmony_ci goto out_of_bounds; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (unlikely(is_rdev_broken(tmp_dev->rdev))) { 22762306a36Sopenharmony_ci md_error(mddev, tmp_dev->rdev); 22862306a36Sopenharmony_ci bio_io_error(bio); 22962306a36Sopenharmony_ci return true; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (unlikely(bio_end_sector(bio) > end_sector)) { 23362306a36Sopenharmony_ci /* This bio crosses a device boundary, so we have to split it */ 23462306a36Sopenharmony_ci struct bio *split = bio_split(bio, end_sector - bio_sector, 23562306a36Sopenharmony_ci GFP_NOIO, &mddev->bio_set); 23662306a36Sopenharmony_ci bio_chain(split, bio); 23762306a36Sopenharmony_ci submit_bio_noacct(bio); 23862306a36Sopenharmony_ci bio = split; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci md_account_bio(mddev, &bio); 24262306a36Sopenharmony_ci bio_set_dev(bio, tmp_dev->rdev->bdev); 24362306a36Sopenharmony_ci bio->bi_iter.bi_sector = bio->bi_iter.bi_sector - 24462306a36Sopenharmony_ci start_sector + data_offset; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && 24762306a36Sopenharmony_ci !bdev_max_discard_sectors(bio->bi_bdev))) { 24862306a36Sopenharmony_ci /* Just ignore it */ 24962306a36Sopenharmony_ci bio_endio(bio); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci if (mddev->gendisk) 25262306a36Sopenharmony_ci trace_block_bio_remap(bio, disk_devt(mddev->gendisk), 25362306a36Sopenharmony_ci bio_sector); 25462306a36Sopenharmony_ci mddev_check_write_zeroes(mddev, bio); 25562306a36Sopenharmony_ci submit_bio_noacct(bio); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciout_of_bounds: 26062306a36Sopenharmony_ci pr_err("md/linear:%s: make_request: Sector %llu out of bounds on dev %pg: %llu sectors, offset %llu\n", 26162306a36Sopenharmony_ci mdname(mddev), 26262306a36Sopenharmony_ci (unsigned long long)bio->bi_iter.bi_sector, 26362306a36Sopenharmony_ci tmp_dev->rdev->bdev, 26462306a36Sopenharmony_ci (unsigned long long)tmp_dev->rdev->sectors, 26562306a36Sopenharmony_ci (unsigned long long)start_sector); 26662306a36Sopenharmony_ci bio_io_error(bio); 26762306a36Sopenharmony_ci return true; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void linear_status (struct seq_file *seq, struct mddev *mddev) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci seq_printf(seq, " %dk rounding", mddev->chunk_sectors / 2); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void linear_error(struct mddev *mddev, struct md_rdev *rdev) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci if (!test_and_set_bit(MD_BROKEN, &mddev->flags)) { 27862306a36Sopenharmony_ci char *md_name = mdname(mddev); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci pr_crit("md/linear%s: Disk failure on %pg detected, failing array.\n", 28162306a36Sopenharmony_ci md_name, rdev->bdev); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void linear_quiesce(struct mddev *mddev, int state) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic struct md_personality linear_personality = 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci .name = "linear", 29262306a36Sopenharmony_ci .level = LEVEL_LINEAR, 29362306a36Sopenharmony_ci .owner = THIS_MODULE, 29462306a36Sopenharmony_ci .make_request = linear_make_request, 29562306a36Sopenharmony_ci .run = linear_run, 29662306a36Sopenharmony_ci .free = linear_free, 29762306a36Sopenharmony_ci .status = linear_status, 29862306a36Sopenharmony_ci .hot_add_disk = linear_add, 29962306a36Sopenharmony_ci .size = linear_size, 30062306a36Sopenharmony_ci .quiesce = linear_quiesce, 30162306a36Sopenharmony_ci .error_handler = linear_error, 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int __init linear_init (void) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci return register_md_personality (&linear_personality); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void linear_exit (void) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci unregister_md_personality (&linear_personality); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cimodule_init(linear_init); 31562306a36Sopenharmony_cimodule_exit(linear_exit); 31662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 31762306a36Sopenharmony_ciMODULE_DESCRIPTION("Linear device concatenation personality for MD (deprecated)"); 31862306a36Sopenharmony_ciMODULE_ALIAS("md-personality-1"); /* LINEAR - deprecated*/ 31962306a36Sopenharmony_ciMODULE_ALIAS("md-linear"); 32062306a36Sopenharmony_ciMODULE_ALIAS("md-level--1"); 321