162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci raid0.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 Copyright (C) 1999, 2000 Ingo Molnar, Red Hat 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci RAID-0 management functions. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci*/ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/blkdev.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 "raid0.h" 2062306a36Sopenharmony_ci#include "raid5.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int default_layout = 0; 2362306a36Sopenharmony_cimodule_param(default_layout, int, 0644); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define UNSUPPORTED_MDDEV_FLAGS \ 2662306a36Sopenharmony_ci ((1L << MD_HAS_JOURNAL) | \ 2762306a36Sopenharmony_ci (1L << MD_JOURNAL_CLEAN) | \ 2862306a36Sopenharmony_ci (1L << MD_FAILFAST_SUPPORTED) |\ 2962306a36Sopenharmony_ci (1L << MD_HAS_PPL) | \ 3062306a36Sopenharmony_ci (1L << MD_HAS_MULTIPLE_PPLS)) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * inform the user of the raid configuration 3462306a36Sopenharmony_ci*/ 3562306a36Sopenharmony_cistatic void dump_zones(struct mddev *mddev) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int j, k; 3862306a36Sopenharmony_ci sector_t zone_size = 0; 3962306a36Sopenharmony_ci sector_t zone_start = 0; 4062306a36Sopenharmony_ci struct r0conf *conf = mddev->private; 4162306a36Sopenharmony_ci int raid_disks = conf->strip_zone[0].nb_dev; 4262306a36Sopenharmony_ci pr_debug("md: RAID0 configuration for %s - %d zone%s\n", 4362306a36Sopenharmony_ci mdname(mddev), 4462306a36Sopenharmony_ci conf->nr_strip_zones, conf->nr_strip_zones==1?"":"s"); 4562306a36Sopenharmony_ci for (j = 0; j < conf->nr_strip_zones; j++) { 4662306a36Sopenharmony_ci char line[200]; 4762306a36Sopenharmony_ci int len = 0; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci for (k = 0; k < conf->strip_zone[j].nb_dev; k++) 5062306a36Sopenharmony_ci len += scnprintf(line+len, 200-len, "%s%pg", k?"/":"", 5162306a36Sopenharmony_ci conf->devlist[j * raid_disks + k]->bdev); 5262306a36Sopenharmony_ci pr_debug("md: zone%d=[%s]\n", j, line); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci zone_size = conf->strip_zone[j].zone_end - zone_start; 5562306a36Sopenharmony_ci pr_debug(" zone-offset=%10lluKB, device-offset=%10lluKB, size=%10lluKB\n", 5662306a36Sopenharmony_ci (unsigned long long)zone_start>>1, 5762306a36Sopenharmony_ci (unsigned long long)conf->strip_zone[j].dev_start>>1, 5862306a36Sopenharmony_ci (unsigned long long)zone_size>>1); 5962306a36Sopenharmony_ci zone_start = conf->strip_zone[j].zone_end; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int i, c, err; 6662306a36Sopenharmony_ci sector_t curr_zone_end, sectors; 6762306a36Sopenharmony_ci struct md_rdev *smallest, *rdev1, *rdev2, *rdev, **dev; 6862306a36Sopenharmony_ci struct strip_zone *zone; 6962306a36Sopenharmony_ci int cnt; 7062306a36Sopenharmony_ci struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL); 7162306a36Sopenharmony_ci unsigned blksize = 512; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci *private_conf = ERR_PTR(-ENOMEM); 7462306a36Sopenharmony_ci if (!conf) 7562306a36Sopenharmony_ci return -ENOMEM; 7662306a36Sopenharmony_ci rdev_for_each(rdev1, mddev) { 7762306a36Sopenharmony_ci pr_debug("md/raid0:%s: looking at %pg\n", 7862306a36Sopenharmony_ci mdname(mddev), 7962306a36Sopenharmony_ci rdev1->bdev); 8062306a36Sopenharmony_ci c = 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* round size to chunk_size */ 8362306a36Sopenharmony_ci sectors = rdev1->sectors; 8462306a36Sopenharmony_ci sector_div(sectors, mddev->chunk_sectors); 8562306a36Sopenharmony_ci rdev1->sectors = sectors * mddev->chunk_sectors; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci blksize = max(blksize, queue_logical_block_size( 8862306a36Sopenharmony_ci rdev1->bdev->bd_disk->queue)); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci rdev_for_each(rdev2, mddev) { 9162306a36Sopenharmony_ci pr_debug("md/raid0:%s: comparing %pg(%llu)" 9262306a36Sopenharmony_ci " with %pg(%llu)\n", 9362306a36Sopenharmony_ci mdname(mddev), 9462306a36Sopenharmony_ci rdev1->bdev, 9562306a36Sopenharmony_ci (unsigned long long)rdev1->sectors, 9662306a36Sopenharmony_ci rdev2->bdev, 9762306a36Sopenharmony_ci (unsigned long long)rdev2->sectors); 9862306a36Sopenharmony_ci if (rdev2 == rdev1) { 9962306a36Sopenharmony_ci pr_debug("md/raid0:%s: END\n", 10062306a36Sopenharmony_ci mdname(mddev)); 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci if (rdev2->sectors == rdev1->sectors) { 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * Not unique, don't count it as a new 10662306a36Sopenharmony_ci * group 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci pr_debug("md/raid0:%s: EQUAL\n", 10962306a36Sopenharmony_ci mdname(mddev)); 11062306a36Sopenharmony_ci c = 1; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci pr_debug("md/raid0:%s: NOT EQUAL\n", 11462306a36Sopenharmony_ci mdname(mddev)); 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci if (!c) { 11762306a36Sopenharmony_ci pr_debug("md/raid0:%s: ==> UNIQUE\n", 11862306a36Sopenharmony_ci mdname(mddev)); 11962306a36Sopenharmony_ci conf->nr_strip_zones++; 12062306a36Sopenharmony_ci pr_debug("md/raid0:%s: %d zones\n", 12162306a36Sopenharmony_ci mdname(mddev), conf->nr_strip_zones); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci pr_debug("md/raid0:%s: FINAL %d zones\n", 12562306a36Sopenharmony_ci mdname(mddev), conf->nr_strip_zones); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * now since we have the hard sector sizes, we can make sure 12962306a36Sopenharmony_ci * chunk size is a multiple of that sector size 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if ((mddev->chunk_sectors << 9) % blksize) { 13262306a36Sopenharmony_ci pr_warn("md/raid0:%s: chunk_size of %d not multiple of block size %d\n", 13362306a36Sopenharmony_ci mdname(mddev), 13462306a36Sopenharmony_ci mddev->chunk_sectors << 9, blksize); 13562306a36Sopenharmony_ci err = -EINVAL; 13662306a36Sopenharmony_ci goto abort; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci err = -ENOMEM; 14062306a36Sopenharmony_ci conf->strip_zone = kcalloc(conf->nr_strip_zones, 14162306a36Sopenharmony_ci sizeof(struct strip_zone), 14262306a36Sopenharmony_ci GFP_KERNEL); 14362306a36Sopenharmony_ci if (!conf->strip_zone) 14462306a36Sopenharmony_ci goto abort; 14562306a36Sopenharmony_ci conf->devlist = kzalloc(array3_size(sizeof(struct md_rdev *), 14662306a36Sopenharmony_ci conf->nr_strip_zones, 14762306a36Sopenharmony_ci mddev->raid_disks), 14862306a36Sopenharmony_ci GFP_KERNEL); 14962306a36Sopenharmony_ci if (!conf->devlist) 15062306a36Sopenharmony_ci goto abort; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* The first zone must contain all devices, so here we check that 15362306a36Sopenharmony_ci * there is a proper alignment of slots to devices and find them all 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci zone = &conf->strip_zone[0]; 15662306a36Sopenharmony_ci cnt = 0; 15762306a36Sopenharmony_ci smallest = NULL; 15862306a36Sopenharmony_ci dev = conf->devlist; 15962306a36Sopenharmony_ci err = -EINVAL; 16062306a36Sopenharmony_ci rdev_for_each(rdev1, mddev) { 16162306a36Sopenharmony_ci int j = rdev1->raid_disk; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (mddev->level == 10) { 16462306a36Sopenharmony_ci /* taking over a raid10-n2 array */ 16562306a36Sopenharmony_ci j /= 2; 16662306a36Sopenharmony_ci rdev1->new_raid_disk = j; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (mddev->level == 1) { 17062306a36Sopenharmony_ci /* taiking over a raid1 array- 17162306a36Sopenharmony_ci * we have only one active disk 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci j = 0; 17462306a36Sopenharmony_ci rdev1->new_raid_disk = j; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (j < 0) { 17862306a36Sopenharmony_ci pr_warn("md/raid0:%s: remove inactive devices before converting to RAID0\n", 17962306a36Sopenharmony_ci mdname(mddev)); 18062306a36Sopenharmony_ci goto abort; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci if (j >= mddev->raid_disks) { 18362306a36Sopenharmony_ci pr_warn("md/raid0:%s: bad disk number %d - aborting!\n", 18462306a36Sopenharmony_ci mdname(mddev), j); 18562306a36Sopenharmony_ci goto abort; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci if (dev[j]) { 18862306a36Sopenharmony_ci pr_warn("md/raid0:%s: multiple devices for %d - aborting!\n", 18962306a36Sopenharmony_ci mdname(mddev), j); 19062306a36Sopenharmony_ci goto abort; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci dev[j] = rdev1; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!smallest || (rdev1->sectors < smallest->sectors)) 19562306a36Sopenharmony_ci smallest = rdev1; 19662306a36Sopenharmony_ci cnt++; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci if (cnt != mddev->raid_disks) { 19962306a36Sopenharmony_ci pr_warn("md/raid0:%s: too few disks (%d of %d) - aborting!\n", 20062306a36Sopenharmony_ci mdname(mddev), cnt, mddev->raid_disks); 20162306a36Sopenharmony_ci goto abort; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci zone->nb_dev = cnt; 20462306a36Sopenharmony_ci zone->zone_end = smallest->sectors * cnt; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci curr_zone_end = zone->zone_end; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* now do the other zones */ 20962306a36Sopenharmony_ci for (i = 1; i < conf->nr_strip_zones; i++) 21062306a36Sopenharmony_ci { 21162306a36Sopenharmony_ci int j; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci zone = conf->strip_zone + i; 21462306a36Sopenharmony_ci dev = conf->devlist + i * mddev->raid_disks; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci pr_debug("md/raid0:%s: zone %d\n", mdname(mddev), i); 21762306a36Sopenharmony_ci zone->dev_start = smallest->sectors; 21862306a36Sopenharmony_ci smallest = NULL; 21962306a36Sopenharmony_ci c = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for (j=0; j<cnt; j++) { 22262306a36Sopenharmony_ci rdev = conf->devlist[j]; 22362306a36Sopenharmony_ci if (rdev->sectors <= zone->dev_start) { 22462306a36Sopenharmony_ci pr_debug("md/raid0:%s: checking %pg ... nope\n", 22562306a36Sopenharmony_ci mdname(mddev), 22662306a36Sopenharmony_ci rdev->bdev); 22762306a36Sopenharmony_ci continue; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci pr_debug("md/raid0:%s: checking %pg ..." 23062306a36Sopenharmony_ci " contained as device %d\n", 23162306a36Sopenharmony_ci mdname(mddev), 23262306a36Sopenharmony_ci rdev->bdev, c); 23362306a36Sopenharmony_ci dev[c] = rdev; 23462306a36Sopenharmony_ci c++; 23562306a36Sopenharmony_ci if (!smallest || rdev->sectors < smallest->sectors) { 23662306a36Sopenharmony_ci smallest = rdev; 23762306a36Sopenharmony_ci pr_debug("md/raid0:%s: (%llu) is smallest!.\n", 23862306a36Sopenharmony_ci mdname(mddev), 23962306a36Sopenharmony_ci (unsigned long long)rdev->sectors); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci zone->nb_dev = c; 24462306a36Sopenharmony_ci sectors = (smallest->sectors - zone->dev_start) * c; 24562306a36Sopenharmony_ci pr_debug("md/raid0:%s: zone->nb_dev: %d, sectors: %llu\n", 24662306a36Sopenharmony_ci mdname(mddev), 24762306a36Sopenharmony_ci zone->nb_dev, (unsigned long long)sectors); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci curr_zone_end += sectors; 25062306a36Sopenharmony_ci zone->zone_end = curr_zone_end; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pr_debug("md/raid0:%s: current zone start: %llu\n", 25362306a36Sopenharmony_ci mdname(mddev), 25462306a36Sopenharmony_ci (unsigned long long)smallest->sectors); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (conf->nr_strip_zones == 1 || conf->strip_zone[1].nb_dev == 1) { 25862306a36Sopenharmony_ci conf->layout = RAID0_ORIG_LAYOUT; 25962306a36Sopenharmony_ci } else if (mddev->layout == RAID0_ORIG_LAYOUT || 26062306a36Sopenharmony_ci mddev->layout == RAID0_ALT_MULTIZONE_LAYOUT) { 26162306a36Sopenharmony_ci conf->layout = mddev->layout; 26262306a36Sopenharmony_ci } else if (default_layout == RAID0_ORIG_LAYOUT || 26362306a36Sopenharmony_ci default_layout == RAID0_ALT_MULTIZONE_LAYOUT) { 26462306a36Sopenharmony_ci conf->layout = default_layout; 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci pr_err("md/raid0:%s: cannot assemble multi-zone RAID0 with default_layout setting\n", 26762306a36Sopenharmony_ci mdname(mddev)); 26862306a36Sopenharmony_ci pr_err("md/raid0: please set raid0.default_layout to 1 or 2\n"); 26962306a36Sopenharmony_ci err = -EOPNOTSUPP; 27062306a36Sopenharmony_ci goto abort; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (conf->layout == RAID0_ORIG_LAYOUT) { 27462306a36Sopenharmony_ci for (i = 1; i < conf->nr_strip_zones; i++) { 27562306a36Sopenharmony_ci sector_t first_sector = conf->strip_zone[i-1].zone_end; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci sector_div(first_sector, mddev->chunk_sectors); 27862306a36Sopenharmony_ci zone = conf->strip_zone + i; 27962306a36Sopenharmony_ci /* disk_shift is first disk index used in the zone */ 28062306a36Sopenharmony_ci zone->disk_shift = sector_div(first_sector, 28162306a36Sopenharmony_ci zone->nb_dev); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci pr_debug("md/raid0:%s: done.\n", mdname(mddev)); 28662306a36Sopenharmony_ci *private_conf = conf; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ciabort: 29062306a36Sopenharmony_ci kfree(conf->strip_zone); 29162306a36Sopenharmony_ci kfree(conf->devlist); 29262306a36Sopenharmony_ci kfree(conf); 29362306a36Sopenharmony_ci *private_conf = ERR_PTR(err); 29462306a36Sopenharmony_ci return err; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Find the zone which holds a particular offset 29862306a36Sopenharmony_ci * Update *sectorp to be an offset in that zone 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic struct strip_zone *find_zone(struct r0conf *conf, 30162306a36Sopenharmony_ci sector_t *sectorp) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int i; 30462306a36Sopenharmony_ci struct strip_zone *z = conf->strip_zone; 30562306a36Sopenharmony_ci sector_t sector = *sectorp; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci for (i = 0; i < conf->nr_strip_zones; i++) 30862306a36Sopenharmony_ci if (sector < z[i].zone_end) { 30962306a36Sopenharmony_ci if (i) 31062306a36Sopenharmony_ci *sectorp = sector - z[i-1].zone_end; 31162306a36Sopenharmony_ci return z + i; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci BUG(); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/* 31762306a36Sopenharmony_ci * remaps the bio to the target device. we separate two flows. 31862306a36Sopenharmony_ci * power 2 flow and a general flow for the sake of performance 31962306a36Sopenharmony_ci*/ 32062306a36Sopenharmony_cistatic struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone, 32162306a36Sopenharmony_ci sector_t sector, sector_t *sector_offset) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci unsigned int sect_in_chunk; 32462306a36Sopenharmony_ci sector_t chunk; 32562306a36Sopenharmony_ci struct r0conf *conf = mddev->private; 32662306a36Sopenharmony_ci int raid_disks = conf->strip_zone[0].nb_dev; 32762306a36Sopenharmony_ci unsigned int chunk_sects = mddev->chunk_sectors; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (is_power_of_2(chunk_sects)) { 33062306a36Sopenharmony_ci int chunksect_bits = ffz(~chunk_sects); 33162306a36Sopenharmony_ci /* find the sector offset inside the chunk */ 33262306a36Sopenharmony_ci sect_in_chunk = sector & (chunk_sects - 1); 33362306a36Sopenharmony_ci sector >>= chunksect_bits; 33462306a36Sopenharmony_ci /* chunk in zone */ 33562306a36Sopenharmony_ci chunk = *sector_offset; 33662306a36Sopenharmony_ci /* quotient is the chunk in real device*/ 33762306a36Sopenharmony_ci sector_div(chunk, zone->nb_dev << chunksect_bits); 33862306a36Sopenharmony_ci } else{ 33962306a36Sopenharmony_ci sect_in_chunk = sector_div(sector, chunk_sects); 34062306a36Sopenharmony_ci chunk = *sector_offset; 34162306a36Sopenharmony_ci sector_div(chunk, chunk_sects * zone->nb_dev); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci /* 34462306a36Sopenharmony_ci * position the bio over the real device 34562306a36Sopenharmony_ci * real sector = chunk in device + starting of zone 34662306a36Sopenharmony_ci * + the position in the chunk 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci *sector_offset = (chunk * chunk_sects) + sect_in_chunk; 34962306a36Sopenharmony_ci return conf->devlist[(zone - conf->strip_zone)*raid_disks 35062306a36Sopenharmony_ci + sector_div(sector, zone->nb_dev)]; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci sector_t array_sectors = 0; 35662306a36Sopenharmony_ci struct md_rdev *rdev; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci WARN_ONCE(sectors || raid_disks, 35962306a36Sopenharmony_ci "%s does not support generic reshape\n", __func__); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci rdev_for_each(rdev, mddev) 36262306a36Sopenharmony_ci array_sectors += (rdev->sectors & 36362306a36Sopenharmony_ci ~(sector_t)(mddev->chunk_sectors-1)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return array_sectors; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void free_conf(struct mddev *mddev, struct r0conf *conf) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci kfree(conf->strip_zone); 37162306a36Sopenharmony_ci kfree(conf->devlist); 37262306a36Sopenharmony_ci kfree(conf); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void raid0_free(struct mddev *mddev, void *priv) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct r0conf *conf = priv; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci free_conf(mddev, conf); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int raid0_run(struct mddev *mddev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct r0conf *conf; 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (mddev->chunk_sectors == 0) { 38862306a36Sopenharmony_ci pr_warn("md/raid0:%s: chunk size must be set.\n", mdname(mddev)); 38962306a36Sopenharmony_ci return -EINVAL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci if (md_check_no_bitmap(mddev)) 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* if private is not null, we are here after takeover */ 39562306a36Sopenharmony_ci if (mddev->private == NULL) { 39662306a36Sopenharmony_ci ret = create_strip_zones(mddev, &conf); 39762306a36Sopenharmony_ci if (ret < 0) 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci mddev->private = conf; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci conf = mddev->private; 40262306a36Sopenharmony_ci if (mddev->queue) { 40362306a36Sopenharmony_ci struct md_rdev *rdev; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors); 40662306a36Sopenharmony_ci blk_queue_max_write_zeroes_sectors(mddev->queue, mddev->chunk_sectors); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci blk_queue_io_min(mddev->queue, mddev->chunk_sectors << 9); 40962306a36Sopenharmony_ci blk_queue_io_opt(mddev->queue, 41062306a36Sopenharmony_ci (mddev->chunk_sectors << 9) * mddev->raid_disks); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci rdev_for_each(rdev, mddev) { 41362306a36Sopenharmony_ci disk_stack_limits(mddev->gendisk, rdev->bdev, 41462306a36Sopenharmony_ci rdev->data_offset << 9); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* calculate array device size */ 41962306a36Sopenharmony_ci md_set_array_sectors(mddev, raid0_size(mddev, 0, 0)); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci pr_debug("md/raid0:%s: md_size is %llu sectors.\n", 42262306a36Sopenharmony_ci mdname(mddev), 42362306a36Sopenharmony_ci (unsigned long long)mddev->array_sectors); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci dump_zones(mddev); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = md_integrity_register(mddev); 42862306a36Sopenharmony_ci if (ret) 42962306a36Sopenharmony_ci free_conf(mddev, conf); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * Convert disk_index to the disk order in which it is read/written. 43662306a36Sopenharmony_ci * For example, if we have 4 disks, they are numbered 0,1,2,3. If we 43762306a36Sopenharmony_ci * write the disks starting at disk 3, then the read/write order would 43862306a36Sopenharmony_ci * be disk 3, then 0, then 1, and then disk 2 and we want map_disk_shift() 43962306a36Sopenharmony_ci * to map the disks as follows 0,1,2,3 => 1,2,3,0. So disk 0 would map 44062306a36Sopenharmony_ci * to 1, 1 to 2, 2 to 3, and 3 to 0. That way we can compare disks in 44162306a36Sopenharmony_ci * that 'output' space to understand the read/write disk ordering. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic int map_disk_shift(int disk_index, int num_disks, int disk_shift) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci return ((disk_index + num_disks - disk_shift) % num_disks); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void raid0_handle_discard(struct mddev *mddev, struct bio *bio) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct r0conf *conf = mddev->private; 45162306a36Sopenharmony_ci struct strip_zone *zone; 45262306a36Sopenharmony_ci sector_t start = bio->bi_iter.bi_sector; 45362306a36Sopenharmony_ci sector_t end; 45462306a36Sopenharmony_ci unsigned int stripe_size; 45562306a36Sopenharmony_ci sector_t first_stripe_index, last_stripe_index; 45662306a36Sopenharmony_ci sector_t start_disk_offset; 45762306a36Sopenharmony_ci unsigned int start_disk_index; 45862306a36Sopenharmony_ci sector_t end_disk_offset; 45962306a36Sopenharmony_ci unsigned int end_disk_index; 46062306a36Sopenharmony_ci unsigned int disk; 46162306a36Sopenharmony_ci sector_t orig_start, orig_end; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci orig_start = start; 46462306a36Sopenharmony_ci zone = find_zone(conf, &start); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (bio_end_sector(bio) > zone->zone_end) { 46762306a36Sopenharmony_ci struct bio *split = bio_split(bio, 46862306a36Sopenharmony_ci zone->zone_end - bio->bi_iter.bi_sector, GFP_NOIO, 46962306a36Sopenharmony_ci &mddev->bio_set); 47062306a36Sopenharmony_ci bio_chain(split, bio); 47162306a36Sopenharmony_ci submit_bio_noacct(bio); 47262306a36Sopenharmony_ci bio = split; 47362306a36Sopenharmony_ci end = zone->zone_end; 47462306a36Sopenharmony_ci } else 47562306a36Sopenharmony_ci end = bio_end_sector(bio); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci orig_end = end; 47862306a36Sopenharmony_ci if (zone != conf->strip_zone) 47962306a36Sopenharmony_ci end = end - zone[-1].zone_end; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Now start and end is the offset in zone */ 48262306a36Sopenharmony_ci stripe_size = zone->nb_dev * mddev->chunk_sectors; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci first_stripe_index = start; 48562306a36Sopenharmony_ci sector_div(first_stripe_index, stripe_size); 48662306a36Sopenharmony_ci last_stripe_index = end; 48762306a36Sopenharmony_ci sector_div(last_stripe_index, stripe_size); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* In the first zone the original and alternate layouts are the same */ 49062306a36Sopenharmony_ci if ((conf->layout == RAID0_ORIG_LAYOUT) && (zone != conf->strip_zone)) { 49162306a36Sopenharmony_ci sector_div(orig_start, mddev->chunk_sectors); 49262306a36Sopenharmony_ci start_disk_index = sector_div(orig_start, zone->nb_dev); 49362306a36Sopenharmony_ci start_disk_index = map_disk_shift(start_disk_index, 49462306a36Sopenharmony_ci zone->nb_dev, 49562306a36Sopenharmony_ci zone->disk_shift); 49662306a36Sopenharmony_ci sector_div(orig_end, mddev->chunk_sectors); 49762306a36Sopenharmony_ci end_disk_index = sector_div(orig_end, zone->nb_dev); 49862306a36Sopenharmony_ci end_disk_index = map_disk_shift(end_disk_index, 49962306a36Sopenharmony_ci zone->nb_dev, zone->disk_shift); 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci start_disk_index = (int)(start - first_stripe_index * stripe_size) / 50262306a36Sopenharmony_ci mddev->chunk_sectors; 50362306a36Sopenharmony_ci end_disk_index = (int)(end - last_stripe_index * stripe_size) / 50462306a36Sopenharmony_ci mddev->chunk_sectors; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci start_disk_offset = ((int)(start - first_stripe_index * stripe_size) % 50762306a36Sopenharmony_ci mddev->chunk_sectors) + 50862306a36Sopenharmony_ci first_stripe_index * mddev->chunk_sectors; 50962306a36Sopenharmony_ci end_disk_offset = ((int)(end - last_stripe_index * stripe_size) % 51062306a36Sopenharmony_ci mddev->chunk_sectors) + 51162306a36Sopenharmony_ci last_stripe_index * mddev->chunk_sectors; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (disk = 0; disk < zone->nb_dev; disk++) { 51462306a36Sopenharmony_ci sector_t dev_start, dev_end; 51562306a36Sopenharmony_ci struct md_rdev *rdev; 51662306a36Sopenharmony_ci int compare_disk; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci compare_disk = map_disk_shift(disk, zone->nb_dev, 51962306a36Sopenharmony_ci zone->disk_shift); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (compare_disk < start_disk_index) 52262306a36Sopenharmony_ci dev_start = (first_stripe_index + 1) * 52362306a36Sopenharmony_ci mddev->chunk_sectors; 52462306a36Sopenharmony_ci else if (compare_disk > start_disk_index) 52562306a36Sopenharmony_ci dev_start = first_stripe_index * mddev->chunk_sectors; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci dev_start = start_disk_offset; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (compare_disk < end_disk_index) 53062306a36Sopenharmony_ci dev_end = (last_stripe_index + 1) * mddev->chunk_sectors; 53162306a36Sopenharmony_ci else if (compare_disk > end_disk_index) 53262306a36Sopenharmony_ci dev_end = last_stripe_index * mddev->chunk_sectors; 53362306a36Sopenharmony_ci else 53462306a36Sopenharmony_ci dev_end = end_disk_offset; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (dev_end <= dev_start) 53762306a36Sopenharmony_ci continue; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci rdev = conf->devlist[(zone - conf->strip_zone) * 54062306a36Sopenharmony_ci conf->strip_zone[0].nb_dev + disk]; 54162306a36Sopenharmony_ci md_submit_discard_bio(mddev, rdev, bio, 54262306a36Sopenharmony_ci dev_start + zone->dev_start + rdev->data_offset, 54362306a36Sopenharmony_ci dev_end - dev_start); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci bio_endio(bio); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void raid0_map_submit_bio(struct mddev *mddev, struct bio *bio) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct r0conf *conf = mddev->private; 55162306a36Sopenharmony_ci struct strip_zone *zone; 55262306a36Sopenharmony_ci struct md_rdev *tmp_dev; 55362306a36Sopenharmony_ci sector_t bio_sector = bio->bi_iter.bi_sector; 55462306a36Sopenharmony_ci sector_t sector = bio_sector; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci md_account_bio(mddev, &bio); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci zone = find_zone(mddev->private, §or); 55962306a36Sopenharmony_ci switch (conf->layout) { 56062306a36Sopenharmony_ci case RAID0_ORIG_LAYOUT: 56162306a36Sopenharmony_ci tmp_dev = map_sector(mddev, zone, bio_sector, §or); 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci case RAID0_ALT_MULTIZONE_LAYOUT: 56462306a36Sopenharmony_ci tmp_dev = map_sector(mddev, zone, sector, §or); 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci default: 56762306a36Sopenharmony_ci WARN(1, "md/raid0:%s: Invalid layout\n", mdname(mddev)); 56862306a36Sopenharmony_ci bio_io_error(bio); 56962306a36Sopenharmony_ci return; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (unlikely(is_rdev_broken(tmp_dev))) { 57362306a36Sopenharmony_ci bio_io_error(bio); 57462306a36Sopenharmony_ci md_error(mddev, tmp_dev); 57562306a36Sopenharmony_ci return; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci bio_set_dev(bio, tmp_dev->bdev); 57962306a36Sopenharmony_ci bio->bi_iter.bi_sector = sector + zone->dev_start + 58062306a36Sopenharmony_ci tmp_dev->data_offset; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (mddev->gendisk) 58362306a36Sopenharmony_ci trace_block_bio_remap(bio, disk_devt(mddev->gendisk), 58462306a36Sopenharmony_ci bio_sector); 58562306a36Sopenharmony_ci mddev_check_write_zeroes(mddev, bio); 58662306a36Sopenharmony_ci submit_bio_noacct(bio); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic bool raid0_make_request(struct mddev *mddev, struct bio *bio) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci sector_t sector; 59262306a36Sopenharmony_ci unsigned chunk_sects; 59362306a36Sopenharmony_ci unsigned sectors; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (unlikely(bio->bi_opf & REQ_PREFLUSH) 59662306a36Sopenharmony_ci && md_flush_request(mddev, bio)) 59762306a36Sopenharmony_ci return true; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (unlikely((bio_op(bio) == REQ_OP_DISCARD))) { 60062306a36Sopenharmony_ci raid0_handle_discard(mddev, bio); 60162306a36Sopenharmony_ci return true; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci sector = bio->bi_iter.bi_sector; 60562306a36Sopenharmony_ci chunk_sects = mddev->chunk_sectors; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci sectors = chunk_sects - 60862306a36Sopenharmony_ci (likely(is_power_of_2(chunk_sects)) 60962306a36Sopenharmony_ci ? (sector & (chunk_sects-1)) 61062306a36Sopenharmony_ci : sector_div(sector, chunk_sects)); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (sectors < bio_sectors(bio)) { 61362306a36Sopenharmony_ci struct bio *split = bio_split(bio, sectors, GFP_NOIO, 61462306a36Sopenharmony_ci &mddev->bio_set); 61562306a36Sopenharmony_ci bio_chain(split, bio); 61662306a36Sopenharmony_ci raid0_map_submit_bio(mddev, bio); 61762306a36Sopenharmony_ci bio = split; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci raid0_map_submit_bio(mddev, bio); 62162306a36Sopenharmony_ci return true; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void raid0_status(struct seq_file *seq, struct mddev *mddev) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci seq_printf(seq, " %dk chunks", mddev->chunk_sectors / 2); 62762306a36Sopenharmony_ci return; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void raid0_error(struct mddev *mddev, struct md_rdev *rdev) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci if (!test_and_set_bit(MD_BROKEN, &mddev->flags)) { 63362306a36Sopenharmony_ci char *md_name = mdname(mddev); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci pr_crit("md/raid0%s: Disk failure on %pg detected, failing array.\n", 63662306a36Sopenharmony_ci md_name, rdev->bdev); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic void *raid0_takeover_raid45(struct mddev *mddev) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct md_rdev *rdev; 64362306a36Sopenharmony_ci struct r0conf *priv_conf; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (mddev->degraded != 1) { 64662306a36Sopenharmony_ci pr_warn("md/raid0:%s: raid5 must be degraded! Degraded disks: %d\n", 64762306a36Sopenharmony_ci mdname(mddev), 64862306a36Sopenharmony_ci mddev->degraded); 64962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci rdev_for_each(rdev, mddev) { 65362306a36Sopenharmony_ci /* check slot number for a disk */ 65462306a36Sopenharmony_ci if (rdev->raid_disk == mddev->raid_disks-1) { 65562306a36Sopenharmony_ci pr_warn("md/raid0:%s: raid5 must have missing parity disk!\n", 65662306a36Sopenharmony_ci mdname(mddev)); 65762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci rdev->sectors = mddev->dev_sectors; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Set new parameters */ 66362306a36Sopenharmony_ci mddev->new_level = 0; 66462306a36Sopenharmony_ci mddev->new_layout = 0; 66562306a36Sopenharmony_ci mddev->new_chunk_sectors = mddev->chunk_sectors; 66662306a36Sopenharmony_ci mddev->raid_disks--; 66762306a36Sopenharmony_ci mddev->delta_disks = -1; 66862306a36Sopenharmony_ci /* make sure it will be not marked as dirty */ 66962306a36Sopenharmony_ci mddev->recovery_cp = MaxSector; 67062306a36Sopenharmony_ci mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci create_strip_zones(mddev, &priv_conf); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return priv_conf; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic void *raid0_takeover_raid10(struct mddev *mddev) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct r0conf *priv_conf; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Check layout: 68262306a36Sopenharmony_ci * - far_copies must be 1 68362306a36Sopenharmony_ci * - near_copies must be 2 68462306a36Sopenharmony_ci * - disks number must be even 68562306a36Sopenharmony_ci * - all mirrors must be already degraded 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci if (mddev->layout != ((1 << 8) + 2)) { 68862306a36Sopenharmony_ci pr_warn("md/raid0:%s:: Raid0 cannot takeover layout: 0x%x\n", 68962306a36Sopenharmony_ci mdname(mddev), 69062306a36Sopenharmony_ci mddev->layout); 69162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci if (mddev->raid_disks & 1) { 69462306a36Sopenharmony_ci pr_warn("md/raid0:%s: Raid0 cannot takeover Raid10 with odd disk number.\n", 69562306a36Sopenharmony_ci mdname(mddev)); 69662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci if (mddev->degraded != (mddev->raid_disks>>1)) { 69962306a36Sopenharmony_ci pr_warn("md/raid0:%s: All mirrors must be already degraded!\n", 70062306a36Sopenharmony_ci mdname(mddev)); 70162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* Set new parameters */ 70562306a36Sopenharmony_ci mddev->new_level = 0; 70662306a36Sopenharmony_ci mddev->new_layout = 0; 70762306a36Sopenharmony_ci mddev->new_chunk_sectors = mddev->chunk_sectors; 70862306a36Sopenharmony_ci mddev->delta_disks = - mddev->raid_disks / 2; 70962306a36Sopenharmony_ci mddev->raid_disks += mddev->delta_disks; 71062306a36Sopenharmony_ci mddev->degraded = 0; 71162306a36Sopenharmony_ci /* make sure it will be not marked as dirty */ 71262306a36Sopenharmony_ci mddev->recovery_cp = MaxSector; 71362306a36Sopenharmony_ci mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci create_strip_zones(mddev, &priv_conf); 71662306a36Sopenharmony_ci return priv_conf; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void *raid0_takeover_raid1(struct mddev *mddev) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct r0conf *priv_conf; 72262306a36Sopenharmony_ci int chunksect; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Check layout: 72562306a36Sopenharmony_ci * - (N - 1) mirror drives must be already faulty 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci if ((mddev->raid_disks - 1) != mddev->degraded) { 72862306a36Sopenharmony_ci pr_err("md/raid0:%s: (N - 1) mirrors drives must be already faulty!\n", 72962306a36Sopenharmony_ci mdname(mddev)); 73062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* 73462306a36Sopenharmony_ci * a raid1 doesn't have the notion of chunk size, so 73562306a36Sopenharmony_ci * figure out the largest suitable size we can use. 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_ci chunksect = 64 * 2; /* 64K by default */ 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* The array must be an exact multiple of chunksize */ 74062306a36Sopenharmony_ci while (chunksect && (mddev->array_sectors & (chunksect - 1))) 74162306a36Sopenharmony_ci chunksect >>= 1; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if ((chunksect << 9) < PAGE_SIZE) 74462306a36Sopenharmony_ci /* array size does not allow a suitable chunk size */ 74562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Set new parameters */ 74862306a36Sopenharmony_ci mddev->new_level = 0; 74962306a36Sopenharmony_ci mddev->new_layout = 0; 75062306a36Sopenharmony_ci mddev->new_chunk_sectors = chunksect; 75162306a36Sopenharmony_ci mddev->chunk_sectors = chunksect; 75262306a36Sopenharmony_ci mddev->delta_disks = 1 - mddev->raid_disks; 75362306a36Sopenharmony_ci mddev->raid_disks = 1; 75462306a36Sopenharmony_ci /* make sure it will be not marked as dirty */ 75562306a36Sopenharmony_ci mddev->recovery_cp = MaxSector; 75662306a36Sopenharmony_ci mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci create_strip_zones(mddev, &priv_conf); 75962306a36Sopenharmony_ci return priv_conf; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic void *raid0_takeover(struct mddev *mddev) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci /* raid0 can take over: 76562306a36Sopenharmony_ci * raid4 - if all data disks are active. 76662306a36Sopenharmony_ci * raid5 - providing it is Raid4 layout and one disk is faulty 76762306a36Sopenharmony_ci * raid10 - assuming we have all necessary active disks 76862306a36Sopenharmony_ci * raid1 - with (N -1) mirror drives faulty 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (mddev->bitmap) { 77262306a36Sopenharmony_ci pr_warn("md/raid0: %s: cannot takeover array with bitmap\n", 77362306a36Sopenharmony_ci mdname(mddev)); 77462306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci if (mddev->level == 4) 77762306a36Sopenharmony_ci return raid0_takeover_raid45(mddev); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (mddev->level == 5) { 78062306a36Sopenharmony_ci if (mddev->layout == ALGORITHM_PARITY_N) 78162306a36Sopenharmony_ci return raid0_takeover_raid45(mddev); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci pr_warn("md/raid0:%s: Raid can only takeover Raid5 with layout: %d\n", 78462306a36Sopenharmony_ci mdname(mddev), ALGORITHM_PARITY_N); 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (mddev->level == 10) 78862306a36Sopenharmony_ci return raid0_takeover_raid10(mddev); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (mddev->level == 1) 79162306a36Sopenharmony_ci return raid0_takeover_raid1(mddev); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci pr_warn("Takeover from raid%i to raid0 not supported\n", 79462306a36Sopenharmony_ci mddev->level); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic void raid0_quiesce(struct mddev *mddev, int quiesce) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic struct md_personality raid0_personality= 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci .name = "raid0", 80662306a36Sopenharmony_ci .level = 0, 80762306a36Sopenharmony_ci .owner = THIS_MODULE, 80862306a36Sopenharmony_ci .make_request = raid0_make_request, 80962306a36Sopenharmony_ci .run = raid0_run, 81062306a36Sopenharmony_ci .free = raid0_free, 81162306a36Sopenharmony_ci .status = raid0_status, 81262306a36Sopenharmony_ci .size = raid0_size, 81362306a36Sopenharmony_ci .takeover = raid0_takeover, 81462306a36Sopenharmony_ci .quiesce = raid0_quiesce, 81562306a36Sopenharmony_ci .error_handler = raid0_error, 81662306a36Sopenharmony_ci}; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int __init raid0_init (void) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci return register_md_personality (&raid0_personality); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void raid0_exit (void) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci unregister_md_personality (&raid0_personality); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cimodule_init(raid0_init); 82962306a36Sopenharmony_cimodule_exit(raid0_exit); 83062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 83162306a36Sopenharmony_ciMODULE_DESCRIPTION("RAID0 (striping) personality for MD"); 83262306a36Sopenharmony_ciMODULE_ALIAS("md-personality-2"); /* RAID0 */ 83362306a36Sopenharmony_ciMODULE_ALIAS("md-raid0"); 83462306a36Sopenharmony_ciMODULE_ALIAS("md-level-0"); 835