xref: /openbmc/linux/drivers/md/raid0.c (revision cc22b540)
1af1a8899SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds    raid0.c : Multiple Devices driver for Linux
41da177e4SLinus Torvalds 	     Copyright (C) 1994-96 Marc ZYNGIER
51da177e4SLinus Torvalds 	     <zyngier@ufr-info-p7.ibp.fr> or
61da177e4SLinus Torvalds 	     <maz@gloups.fdn.fr>
71da177e4SLinus Torvalds 	     Copyright (C) 1999, 2000 Ingo Molnar, Red Hat
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds    RAID-0 management functions.
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds */
121da177e4SLinus Torvalds 
13bff61975SNeilBrown #include <linux/blkdev.h>
14bff61975SNeilBrown #include <linux/seq_file.h>
15056075c7SPaul Gortmaker #include <linux/module.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
17109e3765SNeilBrown #include <trace/events/block.h>
1843b2e5d8SNeilBrown #include "md.h"
19ef740c37SChristoph Hellwig #include "raid0.h"
209af204cfSTrela, Maciej #include "raid5.h"
211da177e4SLinus Torvalds 
22c84a1372SNeilBrown static int default_layout = 0;
23c84a1372SNeilBrown module_param(default_layout, int, 0644);
24c84a1372SNeilBrown 
25394ed8e4SShaohua Li #define UNSUPPORTED_MDDEV_FLAGS		\
26394ed8e4SShaohua Li 	((1L << MD_HAS_JOURNAL) |	\
27394ed8e4SShaohua Li 	 (1L << MD_JOURNAL_CLEAN) |	\
28ea0213e0SArtur Paszkiewicz 	 (1L << MD_FAILFAST_SUPPORTED) |\
29ddc08823SPawel Baldysiak 	 (1L << MD_HAS_PPL) |		\
30ddc08823SPawel Baldysiak 	 (1L << MD_HAS_MULTIPLE_PPLS))
31394ed8e4SShaohua Li 
3246994191Sraz ben yehuda /*
3346994191Sraz ben yehuda  * inform the user of the raid configuration
3446994191Sraz ben yehuda */
dump_zones(struct mddev * mddev)35fd01b88cSNeilBrown static void dump_zones(struct mddev *mddev)
3646994191Sraz ben yehuda {
3750de8df4SNeilBrown 	int j, k;
3846994191Sraz ben yehuda 	sector_t zone_size = 0;
3946994191Sraz ben yehuda 	sector_t zone_start = 0;
40e373ab10SNeilBrown 	struct r0conf *conf = mddev->private;
4184707f38SNeilBrown 	int raid_disks = conf->strip_zone[0].nb_dev;
4276603884SNeilBrown 	pr_debug("md: RAID0 configuration for %s - %d zone%s\n",
4350de8df4SNeilBrown 		 mdname(mddev),
4450de8df4SNeilBrown 		 conf->nr_strip_zones, conf->nr_strip_zones==1?"":"s");
4546994191Sraz ben yehuda 	for (j = 0; j < conf->nr_strip_zones; j++) {
4676603884SNeilBrown 		char line[200];
4776603884SNeilBrown 		int len = 0;
4876603884SNeilBrown 
4946994191Sraz ben yehuda 		for (k = 0; k < conf->strip_zone[j].nb_dev; k++)
501727fd50SSaurabh Sengar 			len += scnprintf(line+len, 200-len, "%s%pg", k?"/":"",
51913cce5aSChristoph Hellwig 				conf->devlist[j * raid_disks + k]->bdev);
5276603884SNeilBrown 		pr_debug("md: zone%d=[%s]\n", j, line);
5346994191Sraz ben yehuda 
5446994191Sraz ben yehuda 		zone_size  = conf->strip_zone[j].zone_end - zone_start;
5576603884SNeilBrown 		pr_debug("      zone-offset=%10lluKB, device-offset=%10lluKB, size=%10lluKB\n",
5646994191Sraz ben yehuda 			(unsigned long long)zone_start>>1,
5746994191Sraz ben yehuda 			(unsigned long long)conf->strip_zone[j].dev_start>>1,
5846994191Sraz ben yehuda 			(unsigned long long)zone_size>>1);
5946994191Sraz ben yehuda 		zone_start = conf->strip_zone[j].zone_end;
6046994191Sraz ben yehuda 	}
6146994191Sraz ben yehuda }
6246994191Sraz ben yehuda 
create_strip_zones(struct mddev * mddev,struct r0conf ** private_conf)63e373ab10SNeilBrown static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
641da177e4SLinus Torvalds {
65a9f326ebSNeilBrown 	int i, c, err;
6649f357a2SNeilBrown 	sector_t curr_zone_end, sectors;
673cb03002SNeilBrown 	struct md_rdev *smallest, *rdev1, *rdev2, *rdev, **dev;
681da177e4SLinus Torvalds 	struct strip_zone *zone;
691da177e4SLinus Torvalds 	int cnt;
70e373ab10SNeilBrown 	struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
71ad6bf88aSMikulas Patocka 	unsigned blksize = 512;
721da177e4SLinus Torvalds 
737dedd15dSDan Carpenter 	*private_conf = ERR_PTR(-ENOMEM);
74ed7b0038SAndre Noll 	if (!conf)
75ed7b0038SAndre Noll 		return -ENOMEM;
76dafb20faSNeilBrown 	rdev_for_each(rdev1, mddev) {
77913cce5aSChristoph Hellwig 		pr_debug("md/raid0:%s: looking at %pg\n",
78b5a20961SNeilBrown 			 mdname(mddev),
79913cce5aSChristoph Hellwig 			 rdev1->bdev);
801da177e4SLinus Torvalds 		c = 0;
8113f2682bSNeilBrown 
8213f2682bSNeilBrown 		/* round size to chunk_size */
8313f2682bSNeilBrown 		sectors = rdev1->sectors;
8413f2682bSNeilBrown 		sector_div(sectors, mddev->chunk_sectors);
8513f2682bSNeilBrown 		rdev1->sectors = sectors * mddev->chunk_sectors;
8613f2682bSNeilBrown 
87199dc6edSNeilBrown 		blksize = max(blksize, queue_logical_block_size(
88199dc6edSNeilBrown 				      rdev1->bdev->bd_disk->queue));
89199dc6edSNeilBrown 
90dafb20faSNeilBrown 		rdev_for_each(rdev2, mddev) {
91913cce5aSChristoph Hellwig 			pr_debug("md/raid0:%s:   comparing %pg(%llu)"
92913cce5aSChristoph Hellwig 				 " with %pg(%llu)\n",
93b5a20961SNeilBrown 				 mdname(mddev),
94913cce5aSChristoph Hellwig 				 rdev1->bdev,
9550de8df4SNeilBrown 				 (unsigned long long)rdev1->sectors,
96913cce5aSChristoph Hellwig 				 rdev2->bdev,
97dd8ac336SAndre Noll 				 (unsigned long long)rdev2->sectors);
981da177e4SLinus Torvalds 			if (rdev2 == rdev1) {
9950de8df4SNeilBrown 				pr_debug("md/raid0:%s:   END\n",
100b5a20961SNeilBrown 					 mdname(mddev));
1011da177e4SLinus Torvalds 				break;
1021da177e4SLinus Torvalds 			}
103dd8ac336SAndre Noll 			if (rdev2->sectors == rdev1->sectors) {
1041da177e4SLinus Torvalds 				/*
1051da177e4SLinus Torvalds 				 * Not unique, don't count it as a new
1061da177e4SLinus Torvalds 				 * group
1071da177e4SLinus Torvalds 				 */
10850de8df4SNeilBrown 				pr_debug("md/raid0:%s:   EQUAL\n",
109b5a20961SNeilBrown 					 mdname(mddev));
1101da177e4SLinus Torvalds 				c = 1;
1111da177e4SLinus Torvalds 				break;
1121da177e4SLinus Torvalds 			}
11350de8df4SNeilBrown 			pr_debug("md/raid0:%s:   NOT EQUAL\n",
114b5a20961SNeilBrown 				 mdname(mddev));
1151da177e4SLinus Torvalds 		}
1161da177e4SLinus Torvalds 		if (!c) {
11750de8df4SNeilBrown 			pr_debug("md/raid0:%s:   ==> UNIQUE\n",
118b5a20961SNeilBrown 				 mdname(mddev));
1191da177e4SLinus Torvalds 			conf->nr_strip_zones++;
12050de8df4SNeilBrown 			pr_debug("md/raid0:%s: %d zones\n",
121b5a20961SNeilBrown 				 mdname(mddev), conf->nr_strip_zones);
1221da177e4SLinus Torvalds 		}
1231da177e4SLinus Torvalds 	}
12450de8df4SNeilBrown 	pr_debug("md/raid0:%s: FINAL %d zones\n",
125b5a20961SNeilBrown 		 mdname(mddev), conf->nr_strip_zones);
126c84a1372SNeilBrown 
127199dc6edSNeilBrown 	/*
128199dc6edSNeilBrown 	 * now since we have the hard sector sizes, we can make sure
129199dc6edSNeilBrown 	 * chunk size is a multiple of that sector size
130199dc6edSNeilBrown 	 */
131199dc6edSNeilBrown 	if ((mddev->chunk_sectors << 9) % blksize) {
13276603884SNeilBrown 		pr_warn("md/raid0:%s: chunk_size of %d not multiple of block size %d\n",
133199dc6edSNeilBrown 			mdname(mddev),
134199dc6edSNeilBrown 			mddev->chunk_sectors << 9, blksize);
135199dc6edSNeilBrown 		err = -EINVAL;
136199dc6edSNeilBrown 		goto abort;
137199dc6edSNeilBrown 	}
138199dc6edSNeilBrown 
139ed7b0038SAndre Noll 	err = -ENOMEM;
1406396bb22SKees Cook 	conf->strip_zone = kcalloc(conf->nr_strip_zones,
1416396bb22SKees Cook 				   sizeof(struct strip_zone),
1426396bb22SKees Cook 				   GFP_KERNEL);
1431da177e4SLinus Torvalds 	if (!conf->strip_zone)
144ed7b0038SAndre Noll 		goto abort;
1456396bb22SKees Cook 	conf->devlist = kzalloc(array3_size(sizeof(struct md_rdev *),
1466396bb22SKees Cook 					    conf->nr_strip_zones,
1476396bb22SKees Cook 					    mddev->raid_disks),
1481da177e4SLinus Torvalds 				GFP_KERNEL);
1491da177e4SLinus Torvalds 	if (!conf->devlist)
150ed7b0038SAndre Noll 		goto abort;
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	/* The first zone must contain all devices, so here we check that
1531da177e4SLinus Torvalds 	 * there is a proper alignment of slots to devices and find them all
1541da177e4SLinus Torvalds 	 */
1551da177e4SLinus Torvalds 	zone = &conf->strip_zone[0];
1561da177e4SLinus Torvalds 	cnt = 0;
1571da177e4SLinus Torvalds 	smallest = NULL;
158b414579fSNeilBrown 	dev = conf->devlist;
159ed7b0038SAndre Noll 	err = -EINVAL;
160dafb20faSNeilBrown 	rdev_for_each(rdev1, mddev) {
1611da177e4SLinus Torvalds 		int j = rdev1->raid_disk;
1621da177e4SLinus Torvalds 
163e93f68a1SNeilBrown 		if (mddev->level == 10) {
1649af204cfSTrela, Maciej 			/* taking over a raid10-n2 array */
1659af204cfSTrela, Maciej 			j /= 2;
166e93f68a1SNeilBrown 			rdev1->new_raid_disk = j;
167e93f68a1SNeilBrown 		}
1689af204cfSTrela, Maciej 
169fc3a08b8SKrzysztof Wojcik 		if (mddev->level == 1) {
170fc3a08b8SKrzysztof Wojcik 			/* taiking over a raid1 array-
171fc3a08b8SKrzysztof Wojcik 			 * we have only one active disk
172fc3a08b8SKrzysztof Wojcik 			 */
173fc3a08b8SKrzysztof Wojcik 			j = 0;
174fc3a08b8SKrzysztof Wojcik 			rdev1->new_raid_disk = j;
175fc3a08b8SKrzysztof Wojcik 		}
176fc3a08b8SKrzysztof Wojcik 
177f96c9f30SNeilBrown 		if (j < 0) {
17876603884SNeilBrown 			pr_warn("md/raid0:%s: remove inactive devices before converting to RAID0\n",
179f96c9f30SNeilBrown 				mdname(mddev));
180f96c9f30SNeilBrown 			goto abort;
181f96c9f30SNeilBrown 		}
182f96c9f30SNeilBrown 		if (j >= mddev->raid_disks) {
18376603884SNeilBrown 			pr_warn("md/raid0:%s: bad disk number %d - aborting!\n",
18476603884SNeilBrown 				mdname(mddev), j);
1851da177e4SLinus Torvalds 			goto abort;
1861da177e4SLinus Torvalds 		}
187b414579fSNeilBrown 		if (dev[j]) {
18876603884SNeilBrown 			pr_warn("md/raid0:%s: multiple devices for %d - aborting!\n",
18976603884SNeilBrown 				mdname(mddev), j);
1901da177e4SLinus Torvalds 			goto abort;
1911da177e4SLinus Torvalds 		}
192b414579fSNeilBrown 		dev[j] = rdev1;
1931da177e4SLinus Torvalds 
194dd8ac336SAndre Noll 		if (!smallest || (rdev1->sectors < smallest->sectors))
1951da177e4SLinus Torvalds 			smallest = rdev1;
1961da177e4SLinus Torvalds 		cnt++;
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 	if (cnt != mddev->raid_disks) {
19976603884SNeilBrown 		pr_warn("md/raid0:%s: too few disks (%d of %d) - aborting!\n",
20076603884SNeilBrown 			mdname(mddev), cnt, mddev->raid_disks);
2011da177e4SLinus Torvalds 		goto abort;
2021da177e4SLinus Torvalds 	}
2031da177e4SLinus Torvalds 	zone->nb_dev = cnt;
20449f357a2SNeilBrown 	zone->zone_end = smallest->sectors * cnt;
2051da177e4SLinus Torvalds 
20649f357a2SNeilBrown 	curr_zone_end = zone->zone_end;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	/* now do the other zones */
2091da177e4SLinus Torvalds 	for (i = 1; i < conf->nr_strip_zones; i++)
2101da177e4SLinus Torvalds 	{
211a9f326ebSNeilBrown 		int j;
212a9f326ebSNeilBrown 
2131da177e4SLinus Torvalds 		zone = conf->strip_zone + i;
214b414579fSNeilBrown 		dev = conf->devlist + i * mddev->raid_disks;
2151da177e4SLinus Torvalds 
21650de8df4SNeilBrown 		pr_debug("md/raid0:%s: zone %d\n", mdname(mddev), i);
217d27a43abSNeilBrown 		zone->dev_start = smallest->sectors;
2181da177e4SLinus Torvalds 		smallest = NULL;
2191da177e4SLinus Torvalds 		c = 0;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 		for (j=0; j<cnt; j++) {
222b414579fSNeilBrown 			rdev = conf->devlist[j];
22350de8df4SNeilBrown 			if (rdev->sectors <= zone->dev_start) {
224913cce5aSChristoph Hellwig 				pr_debug("md/raid0:%s: checking %pg ... nope\n",
225b5a20961SNeilBrown 					 mdname(mddev),
226913cce5aSChristoph Hellwig 					 rdev->bdev);
227dd8ac336SAndre Noll 				continue;
228dd8ac336SAndre Noll 			}
229913cce5aSChristoph Hellwig 			pr_debug("md/raid0:%s: checking %pg ..."
23050de8df4SNeilBrown 				 " contained as device %d\n",
23150de8df4SNeilBrown 				 mdname(mddev),
232913cce5aSChristoph Hellwig 				 rdev->bdev, c);
233b414579fSNeilBrown 			dev[c] = rdev;
2341da177e4SLinus Torvalds 			c++;
235dd8ac336SAndre Noll 			if (!smallest || rdev->sectors < smallest->sectors) {
2361da177e4SLinus Torvalds 				smallest = rdev;
23750de8df4SNeilBrown 				pr_debug("md/raid0:%s:  (%llu) is smallest!.\n",
238b5a20961SNeilBrown 					 mdname(mddev),
239dd8ac336SAndre Noll 					 (unsigned long long)rdev->sectors);
2401da177e4SLinus Torvalds 			}
2411da177e4SLinus Torvalds 		}
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 		zone->nb_dev = c;
24449f357a2SNeilBrown 		sectors = (smallest->sectors - zone->dev_start) * c;
24550de8df4SNeilBrown 		pr_debug("md/raid0:%s: zone->nb_dev: %d, sectors: %llu\n",
246b5a20961SNeilBrown 			 mdname(mddev),
24749f357a2SNeilBrown 			 zone->nb_dev, (unsigned long long)sectors);
2481da177e4SLinus Torvalds 
24949f357a2SNeilBrown 		curr_zone_end += sectors;
250d27a43abSNeilBrown 		zone->zone_end = curr_zone_end;
2511da177e4SLinus Torvalds 
25250de8df4SNeilBrown 		pr_debug("md/raid0:%s: current zone start: %llu\n",
253b5a20961SNeilBrown 			 mdname(mddev),
254d27a43abSNeilBrown 			 (unsigned long long)smallest->sectors);
2551da177e4SLinus Torvalds 	}
2561da177e4SLinus Torvalds 
257ea23994eSPascal Hambourg 	if (conf->nr_strip_zones == 1 || conf->strip_zone[1].nb_dev == 1) {
258ea23994eSPascal Hambourg 		conf->layout = RAID0_ORIG_LAYOUT;
259ea23994eSPascal Hambourg 	} else if (mddev->layout == RAID0_ORIG_LAYOUT ||
260ea23994eSPascal Hambourg 		   mddev->layout == RAID0_ALT_MULTIZONE_LAYOUT) {
261ea23994eSPascal Hambourg 		conf->layout = mddev->layout;
262ea23994eSPascal Hambourg 	} else if (default_layout == RAID0_ORIG_LAYOUT ||
263ea23994eSPascal Hambourg 		   default_layout == RAID0_ALT_MULTIZONE_LAYOUT) {
264ea23994eSPascal Hambourg 		conf->layout = default_layout;
265ea23994eSPascal Hambourg 	} else {
266ea23994eSPascal Hambourg 		pr_err("md/raid0:%s: cannot assemble multi-zone RAID0 with default_layout setting\n",
267ea23994eSPascal Hambourg 		       mdname(mddev));
268ea23994eSPascal Hambourg 		pr_err("md/raid0: please set raid0.default_layout to 1 or 2\n");
269ea23994eSPascal Hambourg 		err = -EOPNOTSUPP;
270ea23994eSPascal Hambourg 		goto abort;
271ea23994eSPascal Hambourg 	}
272ea23994eSPascal Hambourg 
273e8360070SJason Baron 	if (conf->layout == RAID0_ORIG_LAYOUT) {
274e8360070SJason Baron 		for (i = 1; i < conf->nr_strip_zones; i++) {
275e8360070SJason Baron 			sector_t first_sector = conf->strip_zone[i-1].zone_end;
276e8360070SJason Baron 
277e8360070SJason Baron 			sector_div(first_sector, mddev->chunk_sectors);
278e8360070SJason Baron 			zone = conf->strip_zone + i;
279e8360070SJason Baron 			/* disk_shift is first disk index used in the zone */
280e8360070SJason Baron 			zone->disk_shift = sector_div(first_sector,
281e8360070SJason Baron 						      zone->nb_dev);
282e8360070SJason Baron 		}
283e8360070SJason Baron 	}
284e8360070SJason Baron 
28550de8df4SNeilBrown 	pr_debug("md/raid0:%s: done.\n", mdname(mddev));
2869af204cfSTrela, Maciej 	*private_conf = conf;
2879af204cfSTrela, Maciej 
2881da177e4SLinus Torvalds 	return 0;
2891da177e4SLinus Torvalds abort:
290ed7b0038SAndre Noll 	kfree(conf->strip_zone);
291ed7b0038SAndre Noll 	kfree(conf->devlist);
292ed7b0038SAndre Noll 	kfree(conf);
29358ebb34cSNeilBrown 	*private_conf = ERR_PTR(err);
294ed7b0038SAndre Noll 	return err;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds 
297ba13da47SNeilBrown /* Find the zone which holds a particular offset
298ba13da47SNeilBrown  * Update *sectorp to be an offset in that zone
299ba13da47SNeilBrown  */
find_zone(struct r0conf * conf,sector_t * sectorp)300ba13da47SNeilBrown static struct strip_zone *find_zone(struct r0conf *conf,
301ba13da47SNeilBrown 				    sector_t *sectorp)
302ba13da47SNeilBrown {
303ba13da47SNeilBrown 	int i;
304ba13da47SNeilBrown 	struct strip_zone *z = conf->strip_zone;
305ba13da47SNeilBrown 	sector_t sector = *sectorp;
306ba13da47SNeilBrown 
307ba13da47SNeilBrown 	for (i = 0; i < conf->nr_strip_zones; i++)
308ba13da47SNeilBrown 		if (sector < z[i].zone_end) {
309ba13da47SNeilBrown 			if (i)
310ba13da47SNeilBrown 				*sectorp = sector - z[i-1].zone_end;
311ba13da47SNeilBrown 			return z + i;
312ba13da47SNeilBrown 		}
313ba13da47SNeilBrown 	BUG();
314ba13da47SNeilBrown }
315ba13da47SNeilBrown 
316ba13da47SNeilBrown /*
317ba13da47SNeilBrown  * remaps the bio to the target device. we separate two flows.
31847d68979SNeilBrown  * power 2 flow and a general flow for the sake of performance
319ba13da47SNeilBrown */
map_sector(struct mddev * mddev,struct strip_zone * zone,sector_t sector,sector_t * sector_offset)320ba13da47SNeilBrown static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone,
321ba13da47SNeilBrown 				sector_t sector, sector_t *sector_offset)
322ba13da47SNeilBrown {
323ba13da47SNeilBrown 	unsigned int sect_in_chunk;
324ba13da47SNeilBrown 	sector_t chunk;
325ba13da47SNeilBrown 	struct r0conf *conf = mddev->private;
326ba13da47SNeilBrown 	int raid_disks = conf->strip_zone[0].nb_dev;
327ba13da47SNeilBrown 	unsigned int chunk_sects = mddev->chunk_sectors;
328ba13da47SNeilBrown 
329ba13da47SNeilBrown 	if (is_power_of_2(chunk_sects)) {
330ba13da47SNeilBrown 		int chunksect_bits = ffz(~chunk_sects);
331ba13da47SNeilBrown 		/* find the sector offset inside the chunk */
332ba13da47SNeilBrown 		sect_in_chunk  = sector & (chunk_sects - 1);
333ba13da47SNeilBrown 		sector >>= chunksect_bits;
334ba13da47SNeilBrown 		/* chunk in zone */
335ba13da47SNeilBrown 		chunk = *sector_offset;
336ba13da47SNeilBrown 		/* quotient is the chunk in real device*/
337ba13da47SNeilBrown 		sector_div(chunk, zone->nb_dev << chunksect_bits);
338ba13da47SNeilBrown 	} else{
339ba13da47SNeilBrown 		sect_in_chunk = sector_div(sector, chunk_sects);
340ba13da47SNeilBrown 		chunk = *sector_offset;
341ba13da47SNeilBrown 		sector_div(chunk, chunk_sects * zone->nb_dev);
342ba13da47SNeilBrown 	}
343ba13da47SNeilBrown 	/*
344ba13da47SNeilBrown 	*  position the bio over the real device
345ba13da47SNeilBrown 	*  real sector = chunk in device + starting of zone
346ba13da47SNeilBrown 	*	+ the position in the chunk
347ba13da47SNeilBrown 	*/
348ba13da47SNeilBrown 	*sector_offset = (chunk * chunk_sects) + sect_in_chunk;
349ba13da47SNeilBrown 	return conf->devlist[(zone - conf->strip_zone)*raid_disks
350ba13da47SNeilBrown 			     + sector_div(sector, zone->nb_dev)];
351ba13da47SNeilBrown }
352ba13da47SNeilBrown 
raid0_size(struct mddev * mddev,sector_t sectors,int raid_disks)353fd01b88cSNeilBrown static sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks)
35480c3a6ceSDan Williams {
35580c3a6ceSDan Williams 	sector_t array_sectors = 0;
3563cb03002SNeilBrown 	struct md_rdev *rdev;
35780c3a6ceSDan Williams 
35880c3a6ceSDan Williams 	WARN_ONCE(sectors || raid_disks,
35980c3a6ceSDan Williams 		  "%s does not support generic reshape\n", __func__);
36080c3a6ceSDan Williams 
361dafb20faSNeilBrown 	rdev_for_each(rdev, mddev)
362a6468539SNeilBrown 		array_sectors += (rdev->sectors &
363a6468539SNeilBrown 				  ~(sector_t)(mddev->chunk_sectors-1));
36480c3a6ceSDan Williams 
36580c3a6ceSDan Williams 	return array_sectors;
36680c3a6ceSDan Williams }
36780c3a6ceSDan Williams 
free_conf(struct mddev * mddev,struct r0conf * conf)3680c031fd3SXiao Ni static void free_conf(struct mddev *mddev, struct r0conf *conf)
3690c031fd3SXiao Ni {
3700c031fd3SXiao Ni 	kfree(conf->strip_zone);
3710c031fd3SXiao Ni 	kfree(conf->devlist);
3720c031fd3SXiao Ni 	kfree(conf);
3730c031fd3SXiao Ni }
3740c031fd3SXiao Ni 
raid0_free(struct mddev * mddev,void * priv)3750c031fd3SXiao Ni static void raid0_free(struct mddev *mddev, void *priv)
3760c031fd3SXiao Ni {
3770c031fd3SXiao Ni 	struct r0conf *conf = priv;
3780c031fd3SXiao Ni 
3790c031fd3SXiao Ni 	free_conf(mddev, conf);
3800c031fd3SXiao Ni }
3810366ef84Smajianpeng 
raid0_run(struct mddev * mddev)382fd01b88cSNeilBrown static int raid0_run(struct mddev *mddev)
3831da177e4SLinus Torvalds {
384e373ab10SNeilBrown 	struct r0conf *conf;
3855568a603SAndre Noll 	int ret;
3861da177e4SLinus Torvalds 
3879d8f0363SAndre Noll 	if (mddev->chunk_sectors == 0) {
38876603884SNeilBrown 		pr_warn("md/raid0:%s: chunk size must be set.\n", mdname(mddev));
3892604b703SNeilBrown 		return -EINVAL;
3902604b703SNeilBrown 	}
3910894cc30SAndre Noll 	if (md_check_no_bitmap(mddev))
3920894cc30SAndre Noll 		return -EINVAL;
393753f2856SHeinz Mauelshagen 
3949af204cfSTrela, Maciej 	/* if private is not null, we are here after takeover */
3959af204cfSTrela, Maciej 	if (mddev->private == NULL) {
3969af204cfSTrela, Maciej 		ret = create_strip_zones(mddev, &conf);
3975568a603SAndre Noll 		if (ret < 0)
398c567c86bSYu Kuai 			return ret;
3999af204cfSTrela, Maciej 		mddev->private = conf;
4009af204cfSTrela, Maciej 	}
4019af204cfSTrela, Maciej 	conf = mddev->private;
402199dc6edSNeilBrown 	if (mddev->queue) {
403199dc6edSNeilBrown 		struct md_rdev *rdev;
404199dc6edSNeilBrown 
405199dc6edSNeilBrown 		blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors);
4063deff1a7SChristoph Hellwig 		blk_queue_max_write_zeroes_sectors(mddev->queue, mddev->chunk_sectors);
407199dc6edSNeilBrown 
408199dc6edSNeilBrown 		blk_queue_io_min(mddev->queue, mddev->chunk_sectors << 9);
409199dc6edSNeilBrown 		blk_queue_io_opt(mddev->queue,
410199dc6edSNeilBrown 				 (mddev->chunk_sectors << 9) * mddev->raid_disks);
411199dc6edSNeilBrown 
41266eefe5dSNeilBrown 		rdev_for_each(rdev, mddev) {
41366eefe5dSNeilBrown 			disk_stack_limits(mddev->gendisk, rdev->bdev,
41466eefe5dSNeilBrown 					  rdev->data_offset << 9);
41566eefe5dSNeilBrown 		}
416199dc6edSNeilBrown 	}
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	/* calculate array device size */
4191f403624SDan Williams 	md_set_array_sectors(mddev, raid0_size(mddev, 0, 0));
4201da177e4SLinus Torvalds 
42176603884SNeilBrown 	pr_debug("md/raid0:%s: md_size is %llu sectors.\n",
422b5a20961SNeilBrown 		 mdname(mddev),
423ccacc7d2SAndre Noll 		 (unsigned long long)mddev->array_sectors);
424753f2856SHeinz Mauelshagen 
42546994191Sraz ben yehuda 	dump_zones(mddev);
4260366ef84Smajianpeng 
4270366ef84Smajianpeng 	ret = md_integrity_register(mddev);
4280c031fd3SXiao Ni 	if (ret)
4290c031fd3SXiao Ni 		free_conf(mddev, conf);
430c567c86bSYu Kuai 
4310c031fd3SXiao Ni 	return ret;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds 
434e8360070SJason Baron /*
435e8360070SJason Baron  * Convert disk_index to the disk order in which it is read/written.
436e8360070SJason Baron  *  For example, if we have 4 disks, they are numbered 0,1,2,3. If we
437e8360070SJason Baron  *  write the disks starting at disk 3, then the read/write order would
438e8360070SJason Baron  *  be disk 3, then 0, then 1, and then disk 2 and we want map_disk_shift()
439e8360070SJason Baron  *  to map the disks as follows 0,1,2,3 => 1,2,3,0. So disk 0 would map
440e8360070SJason Baron  *  to 1, 1 to 2, 2 to 3, and 3 to 0. That way we can compare disks in
441e8360070SJason Baron  *  that 'output' space to understand the read/write disk ordering.
442e8360070SJason Baron  */
map_disk_shift(int disk_index,int num_disks,int disk_shift)443e8360070SJason Baron static int map_disk_shift(int disk_index, int num_disks, int disk_shift)
444e8360070SJason Baron {
445e8360070SJason Baron 	return ((disk_index + num_disks - disk_shift) % num_disks);
446e8360070SJason Baron }
447e8360070SJason Baron 
raid0_handle_discard(struct mddev * mddev,struct bio * bio)44829efc390SShaohua Li static void raid0_handle_discard(struct mddev *mddev, struct bio *bio)
44929efc390SShaohua Li {
45029efc390SShaohua Li 	struct r0conf *conf = mddev->private;
45129efc390SShaohua Li 	struct strip_zone *zone;
45229efc390SShaohua Li 	sector_t start = bio->bi_iter.bi_sector;
45329efc390SShaohua Li 	sector_t end;
45429efc390SShaohua Li 	unsigned int stripe_size;
45529efc390SShaohua Li 	sector_t first_stripe_index, last_stripe_index;
45629efc390SShaohua Li 	sector_t start_disk_offset;
45729efc390SShaohua Li 	unsigned int start_disk_index;
45829efc390SShaohua Li 	sector_t end_disk_offset;
45929efc390SShaohua Li 	unsigned int end_disk_index;
46029efc390SShaohua Li 	unsigned int disk;
461e8360070SJason Baron 	sector_t orig_start, orig_end;
46229efc390SShaohua Li 
463e8360070SJason Baron 	orig_start = start;
46429efc390SShaohua Li 	zone = find_zone(conf, &start);
46529efc390SShaohua Li 
46629efc390SShaohua Li 	if (bio_end_sector(bio) > zone->zone_end) {
46729efc390SShaohua Li 		struct bio *split = bio_split(bio,
46829efc390SShaohua Li 			zone->zone_end - bio->bi_iter.bi_sector, GFP_NOIO,
469afeee514SKent Overstreet 			&mddev->bio_set);
47029efc390SShaohua Li 		bio_chain(split, bio);
471ed00aabdSChristoph Hellwig 		submit_bio_noacct(bio);
47229efc390SShaohua Li 		bio = split;
47329efc390SShaohua Li 		end = zone->zone_end;
47429efc390SShaohua Li 	} else
47529efc390SShaohua Li 		end = bio_end_sector(bio);
47629efc390SShaohua Li 
477e8360070SJason Baron 	orig_end = end;
47829efc390SShaohua Li 	if (zone != conf->strip_zone)
47929efc390SShaohua Li 		end = end - zone[-1].zone_end;
48029efc390SShaohua Li 
48129efc390SShaohua Li 	/* Now start and end is the offset in zone */
48229efc390SShaohua Li 	stripe_size = zone->nb_dev * mddev->chunk_sectors;
48329efc390SShaohua Li 
48429efc390SShaohua Li 	first_stripe_index = start;
48529efc390SShaohua Li 	sector_div(first_stripe_index, stripe_size);
48629efc390SShaohua Li 	last_stripe_index = end;
48729efc390SShaohua Li 	sector_div(last_stripe_index, stripe_size);
48829efc390SShaohua Li 
489e8360070SJason Baron 	/* In the first zone the original and alternate layouts are the same */
490e8360070SJason Baron 	if ((conf->layout == RAID0_ORIG_LAYOUT) && (zone != conf->strip_zone)) {
491e8360070SJason Baron 		sector_div(orig_start, mddev->chunk_sectors);
492e8360070SJason Baron 		start_disk_index = sector_div(orig_start, zone->nb_dev);
493e8360070SJason Baron 		start_disk_index = map_disk_shift(start_disk_index,
494e8360070SJason Baron 						  zone->nb_dev,
495e8360070SJason Baron 						  zone->disk_shift);
496e8360070SJason Baron 		sector_div(orig_end, mddev->chunk_sectors);
497e8360070SJason Baron 		end_disk_index = sector_div(orig_end, zone->nb_dev);
498e8360070SJason Baron 		end_disk_index = map_disk_shift(end_disk_index,
499e8360070SJason Baron 						zone->nb_dev, zone->disk_shift);
500e8360070SJason Baron 	} else {
50129efc390SShaohua Li 		start_disk_index = (int)(start - first_stripe_index * stripe_size) /
50229efc390SShaohua Li 			mddev->chunk_sectors;
503e8360070SJason Baron 		end_disk_index = (int)(end - last_stripe_index * stripe_size) /
504e8360070SJason Baron 			mddev->chunk_sectors;
505e8360070SJason Baron 	}
50629efc390SShaohua Li 	start_disk_offset = ((int)(start - first_stripe_index * stripe_size) %
50729efc390SShaohua Li 		mddev->chunk_sectors) +
50829efc390SShaohua Li 		first_stripe_index * mddev->chunk_sectors;
50929efc390SShaohua Li 	end_disk_offset = ((int)(end - last_stripe_index * stripe_size) %
51029efc390SShaohua Li 		mddev->chunk_sectors) +
51129efc390SShaohua Li 		last_stripe_index * mddev->chunk_sectors;
51229efc390SShaohua Li 
51329efc390SShaohua Li 	for (disk = 0; disk < zone->nb_dev; disk++) {
51429efc390SShaohua Li 		sector_t dev_start, dev_end;
51529efc390SShaohua Li 		struct md_rdev *rdev;
516e8360070SJason Baron 		int compare_disk;
51729efc390SShaohua Li 
518e8360070SJason Baron 		compare_disk = map_disk_shift(disk, zone->nb_dev,
519e8360070SJason Baron 					      zone->disk_shift);
520e8360070SJason Baron 
521e8360070SJason Baron 		if (compare_disk < start_disk_index)
52229efc390SShaohua Li 			dev_start = (first_stripe_index + 1) *
52329efc390SShaohua Li 				mddev->chunk_sectors;
524e8360070SJason Baron 		else if (compare_disk > start_disk_index)
52529efc390SShaohua Li 			dev_start = first_stripe_index * mddev->chunk_sectors;
52629efc390SShaohua Li 		else
52729efc390SShaohua Li 			dev_start = start_disk_offset;
52829efc390SShaohua Li 
529e8360070SJason Baron 		if (compare_disk < end_disk_index)
53029efc390SShaohua Li 			dev_end = (last_stripe_index + 1) * mddev->chunk_sectors;
531e8360070SJason Baron 		else if (compare_disk > end_disk_index)
53229efc390SShaohua Li 			dev_end = last_stripe_index * mddev->chunk_sectors;
53329efc390SShaohua Li 		else
53429efc390SShaohua Li 			dev_end = end_disk_offset;
53529efc390SShaohua Li 
53629efc390SShaohua Li 		if (dev_end <= dev_start)
53729efc390SShaohua Li 			continue;
53829efc390SShaohua Li 
53929efc390SShaohua Li 		rdev = conf->devlist[(zone - conf->strip_zone) *
54029efc390SShaohua Li 			conf->strip_zone[0].nb_dev + disk];
541cf78408fSXiao Ni 		md_submit_discard_bio(mddev, rdev, bio,
54229efc390SShaohua Li 			dev_start + zone->dev_start + rdev->data_offset,
543cf78408fSXiao Ni 			dev_end - dev_start);
54429efc390SShaohua Li 	}
54529efc390SShaohua Li 	bio_endio(bio);
54629efc390SShaohua Li }
54729efc390SShaohua Li 
raid0_map_submit_bio(struct mddev * mddev,struct bio * bio)548af50e20aSJan Kara static void raid0_map_submit_bio(struct mddev *mddev, struct bio *bio)
5491da177e4SLinus Torvalds {
550c84a1372SNeilBrown 	struct r0conf *conf = mddev->private;
5511da177e4SLinus Torvalds 	struct strip_zone *zone;
5523cb03002SNeilBrown 	struct md_rdev *tmp_dev;
553af50e20aSJan Kara 	sector_t bio_sector = bio->bi_iter.bi_sector;
554af50e20aSJan Kara 	sector_t sector = bio_sector;
555af50e20aSJan Kara 
556af50e20aSJan Kara 	md_account_bio(mddev, &bio);
557af50e20aSJan Kara 
558af50e20aSJan Kara 	zone = find_zone(mddev->private, &sector);
559af50e20aSJan Kara 	switch (conf->layout) {
560af50e20aSJan Kara 	case RAID0_ORIG_LAYOUT:
561af50e20aSJan Kara 		tmp_dev = map_sector(mddev, zone, bio_sector, &sector);
562af50e20aSJan Kara 		break;
563af50e20aSJan Kara 	case RAID0_ALT_MULTIZONE_LAYOUT:
564af50e20aSJan Kara 		tmp_dev = map_sector(mddev, zone, sector, &sector);
565af50e20aSJan Kara 		break;
566af50e20aSJan Kara 	default:
567af50e20aSJan Kara 		WARN(1, "md/raid0:%s: Invalid layout\n", mdname(mddev));
568af50e20aSJan Kara 		bio_io_error(bio);
569af50e20aSJan Kara 		return;
570af50e20aSJan Kara 	}
571af50e20aSJan Kara 
572af50e20aSJan Kara 	if (unlikely(is_rdev_broken(tmp_dev))) {
573af50e20aSJan Kara 		bio_io_error(bio);
574af50e20aSJan Kara 		md_error(mddev, tmp_dev);
575af50e20aSJan Kara 		return;
576af50e20aSJan Kara 	}
577af50e20aSJan Kara 
578af50e20aSJan Kara 	bio_set_dev(bio, tmp_dev->bdev);
579af50e20aSJan Kara 	bio->bi_iter.bi_sector = sector + zone->dev_start +
580af50e20aSJan Kara 		tmp_dev->data_offset;
581af50e20aSJan Kara 
582af50e20aSJan Kara 	if (mddev->gendisk)
583af50e20aSJan Kara 		trace_block_bio_remap(bio, disk_devt(mddev->gendisk),
584af50e20aSJan Kara 				      bio_sector);
585af50e20aSJan Kara 	mddev_check_write_zeroes(mddev, bio);
586af50e20aSJan Kara 	submit_bio_noacct(bio);
587af50e20aSJan Kara }
588af50e20aSJan Kara 
raid0_make_request(struct mddev * mddev,struct bio * bio)589af50e20aSJan Kara static bool raid0_make_request(struct mddev *mddev, struct bio *bio)
590af50e20aSJan Kara {
591f00d7c85SNeilBrown 	sector_t sector;
592f00d7c85SNeilBrown 	unsigned chunk_sects;
593f00d7c85SNeilBrown 	unsigned sectors;
5941da177e4SLinus Torvalds 
595775d7831SDavid Jeffery 	if (unlikely(bio->bi_opf & REQ_PREFLUSH)
596775d7831SDavid Jeffery 	    && md_flush_request(mddev, bio))
597cc27b0c7SNeilBrown 		return true;
598e5dcdd80SNeilBrown 
59929efc390SShaohua Li 	if (unlikely((bio_op(bio) == REQ_OP_DISCARD))) {
60029efc390SShaohua Li 		raid0_handle_discard(mddev, bio);
601cc27b0c7SNeilBrown 		return true;
60229efc390SShaohua Li 	}
60329efc390SShaohua Li 
604af50e20aSJan Kara 	sector = bio->bi_iter.bi_sector;
605f00d7c85SNeilBrown 	chunk_sects = mddev->chunk_sectors;
60620d0189bSKent Overstreet 
607f00d7c85SNeilBrown 	sectors = chunk_sects -
60820d0189bSKent Overstreet 		(likely(is_power_of_2(chunk_sects))
60920d0189bSKent Overstreet 		 ? (sector & (chunk_sects-1))
61020d0189bSKent Overstreet 		 : sector_div(sector, chunk_sects));
61120d0189bSKent Overstreet 
61220d0189bSKent Overstreet 	if (sectors < bio_sectors(bio)) {
613afeee514SKent Overstreet 		struct bio *split = bio_split(bio, sectors, GFP_NOIO,
614afeee514SKent Overstreet 					      &mddev->bio_set);
61520d0189bSKent Overstreet 		bio_chain(split, bio);
616*319ff40aSJan Kara 		raid0_map_submit_bio(mddev, bio);
617f00d7c85SNeilBrown 		bio = split;
6181da177e4SLinus Torvalds 	}
6191da177e4SLinus Torvalds 
620af50e20aSJan Kara 	raid0_map_submit_bio(mddev, bio);
621cc27b0c7SNeilBrown 	return true;
622109e3765SNeilBrown }
6231da177e4SLinus Torvalds 
raid0_status(struct seq_file * seq,struct mddev * mddev)624fd01b88cSNeilBrown static void raid0_status(struct seq_file *seq, struct mddev *mddev)
6251da177e4SLinus Torvalds {
6269d8f0363SAndre Noll 	seq_printf(seq, " %dk chunks", mddev->chunk_sectors / 2);
6271da177e4SLinus Torvalds 	return;
6281da177e4SLinus Torvalds }
6291da177e4SLinus Torvalds 
raid0_error(struct mddev * mddev,struct md_rdev * rdev)630c31fea2fSMariusz Tkaczyk static void raid0_error(struct mddev *mddev, struct md_rdev *rdev)
631c31fea2fSMariusz Tkaczyk {
632c31fea2fSMariusz Tkaczyk 	if (!test_and_set_bit(MD_BROKEN, &mddev->flags)) {
633c31fea2fSMariusz Tkaczyk 		char *md_name = mdname(mddev);
634c31fea2fSMariusz Tkaczyk 
635c31fea2fSMariusz Tkaczyk 		pr_crit("md/raid0%s: Disk failure on %pg detected, failing array.\n",
636c31fea2fSMariusz Tkaczyk 			md_name, rdev->bdev);
637c31fea2fSMariusz Tkaczyk 	}
638c31fea2fSMariusz Tkaczyk }
639c31fea2fSMariusz Tkaczyk 
raid0_takeover_raid45(struct mddev * mddev)640fd01b88cSNeilBrown static void *raid0_takeover_raid45(struct mddev *mddev)
6419af204cfSTrela, Maciej {
6423cb03002SNeilBrown 	struct md_rdev *rdev;
643e373ab10SNeilBrown 	struct r0conf *priv_conf;
6449af204cfSTrela, Maciej 
6459af204cfSTrela, Maciej 	if (mddev->degraded != 1) {
64676603884SNeilBrown 		pr_warn("md/raid0:%s: raid5 must be degraded! Degraded disks: %d\n",
647b5a20961SNeilBrown 			mdname(mddev),
6489af204cfSTrela, Maciej 			mddev->degraded);
6499af204cfSTrela, Maciej 		return ERR_PTR(-EINVAL);
6509af204cfSTrela, Maciej 	}
6519af204cfSTrela, Maciej 
652dafb20faSNeilBrown 	rdev_for_each(rdev, mddev) {
6539af204cfSTrela, Maciej 		/* check slot number for a disk */
6549af204cfSTrela, Maciej 		if (rdev->raid_disk == mddev->raid_disks-1) {
65576603884SNeilBrown 			pr_warn("md/raid0:%s: raid5 must have missing parity disk!\n",
656b5a20961SNeilBrown 				mdname(mddev));
6579af204cfSTrela, Maciej 			return ERR_PTR(-EINVAL);
6589af204cfSTrela, Maciej 		}
659eea136d6SNeilBrown 		rdev->sectors = mddev->dev_sectors;
6609af204cfSTrela, Maciej 	}
6619af204cfSTrela, Maciej 
6629af204cfSTrela, Maciej 	/* Set new parameters */
6639af204cfSTrela, Maciej 	mddev->new_level = 0;
664001048a3SMaciej Trela 	mddev->new_layout = 0;
6659af204cfSTrela, Maciej 	mddev->new_chunk_sectors = mddev->chunk_sectors;
6669af204cfSTrela, Maciej 	mddev->raid_disks--;
6679af204cfSTrela, Maciej 	mddev->delta_disks = -1;
6689af204cfSTrela, Maciej 	/* make sure it will be not marked as dirty */
6699af204cfSTrela, Maciej 	mddev->recovery_cp = MaxSector;
670394ed8e4SShaohua Li 	mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
6719af204cfSTrela, Maciej 
6729af204cfSTrela, Maciej 	create_strip_zones(mddev, &priv_conf);
6736995f0b2SShaohua Li 
6749af204cfSTrela, Maciej 	return priv_conf;
6759af204cfSTrela, Maciej }
6769af204cfSTrela, Maciej 
raid0_takeover_raid10(struct mddev * mddev)677fd01b88cSNeilBrown static void *raid0_takeover_raid10(struct mddev *mddev)
6789af204cfSTrela, Maciej {
679e373ab10SNeilBrown 	struct r0conf *priv_conf;
6809af204cfSTrela, Maciej 
6819af204cfSTrela, Maciej 	/* Check layout:
6829af204cfSTrela, Maciej 	 *  - far_copies must be 1
6839af204cfSTrela, Maciej 	 *  - near_copies must be 2
6849af204cfSTrela, Maciej 	 *  - disks number must be even
6859af204cfSTrela, Maciej 	 *  - all mirrors must be already degraded
6869af204cfSTrela, Maciej 	 */
6879af204cfSTrela, Maciej 	if (mddev->layout != ((1 << 8) + 2)) {
68876603884SNeilBrown 		pr_warn("md/raid0:%s:: Raid0 cannot takeover layout: 0x%x\n",
689b5a20961SNeilBrown 			mdname(mddev),
6909af204cfSTrela, Maciej 			mddev->layout);
6919af204cfSTrela, Maciej 		return ERR_PTR(-EINVAL);
6929af204cfSTrela, Maciej 	}
6939af204cfSTrela, Maciej 	if (mddev->raid_disks & 1) {
69476603884SNeilBrown 		pr_warn("md/raid0:%s: Raid0 cannot takeover Raid10 with odd disk number.\n",
695b5a20961SNeilBrown 			mdname(mddev));
6969af204cfSTrela, Maciej 		return ERR_PTR(-EINVAL);
6979af204cfSTrela, Maciej 	}
6989af204cfSTrela, Maciej 	if (mddev->degraded != (mddev->raid_disks>>1)) {
69976603884SNeilBrown 		pr_warn("md/raid0:%s: All mirrors must be already degraded!\n",
700b5a20961SNeilBrown 			mdname(mddev));
7019af204cfSTrela, Maciej 		return ERR_PTR(-EINVAL);
7029af204cfSTrela, Maciej 	}
7039af204cfSTrela, Maciej 
7049af204cfSTrela, Maciej 	/* Set new parameters */
7059af204cfSTrela, Maciej 	mddev->new_level = 0;
706001048a3SMaciej Trela 	mddev->new_layout = 0;
7079af204cfSTrela, Maciej 	mddev->new_chunk_sectors = mddev->chunk_sectors;
7089af204cfSTrela, Maciej 	mddev->delta_disks = - mddev->raid_disks / 2;
7099af204cfSTrela, Maciej 	mddev->raid_disks += mddev->delta_disks;
7109af204cfSTrela, Maciej 	mddev->degraded = 0;
7119af204cfSTrela, Maciej 	/* make sure it will be not marked as dirty */
7129af204cfSTrela, Maciej 	mddev->recovery_cp = MaxSector;
713394ed8e4SShaohua Li 	mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
7149af204cfSTrela, Maciej 
7159af204cfSTrela, Maciej 	create_strip_zones(mddev, &priv_conf);
7169af204cfSTrela, Maciej 	return priv_conf;
7179af204cfSTrela, Maciej }
7189af204cfSTrela, Maciej 
raid0_takeover_raid1(struct mddev * mddev)719fd01b88cSNeilBrown static void *raid0_takeover_raid1(struct mddev *mddev)
720fc3a08b8SKrzysztof Wojcik {
721e373ab10SNeilBrown 	struct r0conf *priv_conf;
72224b961f8SJes Sorensen 	int chunksect;
723fc3a08b8SKrzysztof Wojcik 
724fc3a08b8SKrzysztof Wojcik 	/* Check layout:
725fc3a08b8SKrzysztof Wojcik 	 *  - (N - 1) mirror drives must be already faulty
726fc3a08b8SKrzysztof Wojcik 	 */
727fc3a08b8SKrzysztof Wojcik 	if ((mddev->raid_disks - 1) != mddev->degraded) {
72876603884SNeilBrown 		pr_err("md/raid0:%s: (N - 1) mirrors drives must be already faulty!\n",
729fc3a08b8SKrzysztof Wojcik 		       mdname(mddev));
730fc3a08b8SKrzysztof Wojcik 		return ERR_PTR(-EINVAL);
731fc3a08b8SKrzysztof Wojcik 	}
732fc3a08b8SKrzysztof Wojcik 
73324b961f8SJes Sorensen 	/*
73424b961f8SJes Sorensen 	 * a raid1 doesn't have the notion of chunk size, so
73524b961f8SJes Sorensen 	 * figure out the largest suitable size we can use.
73624b961f8SJes Sorensen 	 */
73724b961f8SJes Sorensen 	chunksect = 64 * 2; /* 64K by default */
73824b961f8SJes Sorensen 
73924b961f8SJes Sorensen 	/* The array must be an exact multiple of chunksize */
74024b961f8SJes Sorensen 	while (chunksect && (mddev->array_sectors & (chunksect - 1)))
74124b961f8SJes Sorensen 		chunksect >>= 1;
74224b961f8SJes Sorensen 
74324b961f8SJes Sorensen 	if ((chunksect << 9) < PAGE_SIZE)
74424b961f8SJes Sorensen 		/* array size does not allow a suitable chunk size */
74524b961f8SJes Sorensen 		return ERR_PTR(-EINVAL);
74624b961f8SJes Sorensen 
747fc3a08b8SKrzysztof Wojcik 	/* Set new parameters */
748fc3a08b8SKrzysztof Wojcik 	mddev->new_level = 0;
749fc3a08b8SKrzysztof Wojcik 	mddev->new_layout = 0;
75024b961f8SJes Sorensen 	mddev->new_chunk_sectors = chunksect;
75124b961f8SJes Sorensen 	mddev->chunk_sectors = chunksect;
752fc3a08b8SKrzysztof Wojcik 	mddev->delta_disks = 1 - mddev->raid_disks;
753f7bee809SKrzysztof Wojcik 	mddev->raid_disks = 1;
754fc3a08b8SKrzysztof Wojcik 	/* make sure it will be not marked as dirty */
755fc3a08b8SKrzysztof Wojcik 	mddev->recovery_cp = MaxSector;
756394ed8e4SShaohua Li 	mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
757fc3a08b8SKrzysztof Wojcik 
758fc3a08b8SKrzysztof Wojcik 	create_strip_zones(mddev, &priv_conf);
759fc3a08b8SKrzysztof Wojcik 	return priv_conf;
760fc3a08b8SKrzysztof Wojcik }
761fc3a08b8SKrzysztof Wojcik 
raid0_takeover(struct mddev * mddev)762fd01b88cSNeilBrown static void *raid0_takeover(struct mddev *mddev)
7639af204cfSTrela, Maciej {
7649af204cfSTrela, Maciej 	/* raid0 can take over:
765049d6c1eSMaciej Trela 	 *  raid4 - if all data disks are active.
7669af204cfSTrela, Maciej 	 *  raid5 - providing it is Raid4 layout and one disk is faulty
7679af204cfSTrela, Maciej 	 *  raid10 - assuming we have all necessary active disks
768fc3a08b8SKrzysztof Wojcik 	 *  raid1 - with (N -1) mirror drives faulty
7699af204cfSTrela, Maciej 	 */
770a8461a61SNeilBrown 
771a8461a61SNeilBrown 	if (mddev->bitmap) {
77276603884SNeilBrown 		pr_warn("md/raid0: %s: cannot takeover array with bitmap\n",
773a8461a61SNeilBrown 			mdname(mddev));
774a8461a61SNeilBrown 		return ERR_PTR(-EBUSY);
775a8461a61SNeilBrown 	}
776049d6c1eSMaciej Trela 	if (mddev->level == 4)
777049d6c1eSMaciej Trela 		return raid0_takeover_raid45(mddev);
778049d6c1eSMaciej Trela 
7799af204cfSTrela, Maciej 	if (mddev->level == 5) {
7809af204cfSTrela, Maciej 		if (mddev->layout == ALGORITHM_PARITY_N)
781049d6c1eSMaciej Trela 			return raid0_takeover_raid45(mddev);
7829af204cfSTrela, Maciej 
78376603884SNeilBrown 		pr_warn("md/raid0:%s: Raid can only takeover Raid5 with layout: %d\n",
784b5a20961SNeilBrown 			mdname(mddev), ALGORITHM_PARITY_N);
7859af204cfSTrela, Maciej 	}
7869af204cfSTrela, Maciej 
7879af204cfSTrela, Maciej 	if (mddev->level == 10)
7889af204cfSTrela, Maciej 		return raid0_takeover_raid10(mddev);
7899af204cfSTrela, Maciej 
790fc3a08b8SKrzysztof Wojcik 	if (mddev->level == 1)
791fc3a08b8SKrzysztof Wojcik 		return raid0_takeover_raid1(mddev);
792fc3a08b8SKrzysztof Wojcik 
79376603884SNeilBrown 	pr_warn("Takeover from raid%i to raid0 not supported\n",
794fc3a08b8SKrzysztof Wojcik 		mddev->level);
795fc3a08b8SKrzysztof Wojcik 
7969af204cfSTrela, Maciej 	return ERR_PTR(-EINVAL);
7979af204cfSTrela, Maciej }
7989af204cfSTrela, Maciej 
raid0_quiesce(struct mddev * mddev,int quiesce)799b03e0ccbSNeilBrown static void raid0_quiesce(struct mddev *mddev, int quiesce)
8009af204cfSTrela, Maciej {
8019af204cfSTrela, Maciej }
8029af204cfSTrela, Maciej 
80384fc4b56SNeilBrown static struct md_personality raid0_personality=
8041da177e4SLinus Torvalds {
8051da177e4SLinus Torvalds 	.name		= "raid0",
8062604b703SNeilBrown 	.level		= 0,
8071da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
8081da177e4SLinus Torvalds 	.make_request	= raid0_make_request,
8091da177e4SLinus Torvalds 	.run		= raid0_run,
810afa0f557SNeilBrown 	.free		= raid0_free,
8111da177e4SLinus Torvalds 	.status		= raid0_status,
81280c3a6ceSDan Williams 	.size		= raid0_size,
8139af204cfSTrela, Maciej 	.takeover	= raid0_takeover,
8149af204cfSTrela, Maciej 	.quiesce	= raid0_quiesce,
815c31fea2fSMariusz Tkaczyk 	.error_handler	= raid0_error,
8161da177e4SLinus Torvalds };
8171da177e4SLinus Torvalds 
raid0_init(void)8181da177e4SLinus Torvalds static int __init raid0_init (void)
8191da177e4SLinus Torvalds {
8202604b703SNeilBrown 	return register_md_personality (&raid0_personality);
8211da177e4SLinus Torvalds }
8221da177e4SLinus Torvalds 
raid0_exit(void)8231da177e4SLinus Torvalds static void raid0_exit (void)
8241da177e4SLinus Torvalds {
8252604b703SNeilBrown 	unregister_md_personality (&raid0_personality);
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds module_init(raid0_init);
8291da177e4SLinus Torvalds module_exit(raid0_exit);
8301da177e4SLinus Torvalds MODULE_LICENSE("GPL");
8310efb9e61SNeilBrown MODULE_DESCRIPTION("RAID0 (striping) personality for MD");
8321da177e4SLinus Torvalds MODULE_ALIAS("md-personality-2"); /* RAID0 */
833d9d166c2SNeilBrown MODULE_ALIAS("md-raid0");
8342604b703SNeilBrown MODULE_ALIAS("md-level-0");
835