xref: /openbmc/linux/drivers/md/dm-stripe.c (revision 4ee218cd)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Copyright (C) 2001-2003 Sistina Software (UK) Limited.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * This file is released under the GPL.
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include "dm.h"
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds #include <linux/init.h>
111da177e4SLinus Torvalds #include <linux/blkdev.h>
121da177e4SLinus Torvalds #include <linux/bio.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds struct stripe {
161da177e4SLinus Torvalds 	struct dm_dev *dev;
171da177e4SLinus Torvalds 	sector_t physical_start;
181da177e4SLinus Torvalds };
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds struct stripe_c {
211da177e4SLinus Torvalds 	uint32_t stripes;
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 	/* The size of this target / num. stripes */
241da177e4SLinus Torvalds 	sector_t stripe_width;
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds 	/* stripe chunk size */
271da177e4SLinus Torvalds 	uint32_t chunk_shift;
281da177e4SLinus Torvalds 	sector_t chunk_mask;
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds 	struct stripe stripe[0];
311da177e4SLinus Torvalds };
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds static inline struct stripe_c *alloc_context(unsigned int stripes)
341da177e4SLinus Torvalds {
351da177e4SLinus Torvalds 	size_t len;
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 	if (array_too_big(sizeof(struct stripe_c), sizeof(struct stripe),
381da177e4SLinus Torvalds 			  stripes))
391da177e4SLinus Torvalds 		return NULL;
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds 	len = sizeof(struct stripe_c) + (sizeof(struct stripe) * stripes);
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 	return kmalloc(len, GFP_KERNEL);
441da177e4SLinus Torvalds }
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds /*
471da177e4SLinus Torvalds  * Parse a single <dev> <sector> pair
481da177e4SLinus Torvalds  */
491da177e4SLinus Torvalds static int get_stripe(struct dm_target *ti, struct stripe_c *sc,
501da177e4SLinus Torvalds 		      unsigned int stripe, char **argv)
511da177e4SLinus Torvalds {
524ee218cdSAndrew Morton 	unsigned long long start;
531da177e4SLinus Torvalds 
544ee218cdSAndrew Morton 	if (sscanf(argv[1], "%llu", &start) != 1)
551da177e4SLinus Torvalds 		return -EINVAL;
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	if (dm_get_device(ti, argv[0], start, sc->stripe_width,
581da177e4SLinus Torvalds 			  dm_table_get_mode(ti->table),
591da177e4SLinus Torvalds 			  &sc->stripe[stripe].dev))
601da177e4SLinus Torvalds 		return -ENXIO;
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	sc->stripe[stripe].physical_start = start;
631da177e4SLinus Torvalds 	return 0;
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /*
671da177e4SLinus Torvalds  * Construct a striped mapping.
681da177e4SLinus Torvalds  * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
691da177e4SLinus Torvalds  */
701da177e4SLinus Torvalds static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	struct stripe_c *sc;
731da177e4SLinus Torvalds 	sector_t width;
741da177e4SLinus Torvalds 	uint32_t stripes;
751da177e4SLinus Torvalds 	uint32_t chunk_size;
761da177e4SLinus Torvalds 	char *end;
771da177e4SLinus Torvalds 	int r;
781da177e4SLinus Torvalds 	unsigned int i;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	if (argc < 2) {
811da177e4SLinus Torvalds 		ti->error = "dm-stripe: Not enough arguments";
821da177e4SLinus Torvalds 		return -EINVAL;
831da177e4SLinus Torvalds 	}
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 	stripes = simple_strtoul(argv[0], &end, 10);
861da177e4SLinus Torvalds 	if (*end) {
871da177e4SLinus Torvalds 		ti->error = "dm-stripe: Invalid stripe count";
881da177e4SLinus Torvalds 		return -EINVAL;
891da177e4SLinus Torvalds 	}
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	chunk_size = simple_strtoul(argv[1], &end, 10);
921da177e4SLinus Torvalds 	if (*end) {
931da177e4SLinus Torvalds 		ti->error = "dm-stripe: Invalid chunk_size";
941da177e4SLinus Torvalds 		return -EINVAL;
951da177e4SLinus Torvalds 	}
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds 	/*
981da177e4SLinus Torvalds 	 * chunk_size is a power of two
991da177e4SLinus Torvalds 	 */
1001da177e4SLinus Torvalds 	if (!chunk_size || (chunk_size & (chunk_size - 1)) ||
1011da177e4SLinus Torvalds 	    (chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) {
1021da177e4SLinus Torvalds 		ti->error = "dm-stripe: Invalid chunk size";
1031da177e4SLinus Torvalds 		return -EINVAL;
1041da177e4SLinus Torvalds 	}
1051da177e4SLinus Torvalds 
1068ba32fdeSKevin Corry 	if (((uint32_t)ti->len) & (chunk_size - 1)) {
1078ba32fdeSKevin Corry 		ti->error = "dm-stripe: Target length not divisible by "
1088ba32fdeSKevin Corry 		    "chunk size";
1098ba32fdeSKevin Corry 		return -EINVAL;
1108ba32fdeSKevin Corry 	}
1118ba32fdeSKevin Corry 
1121da177e4SLinus Torvalds 	width = ti->len;
1131da177e4SLinus Torvalds 	if (sector_div(width, stripes)) {
1148ba32fdeSKevin Corry 		ti->error = "dm-stripe: Target length not divisible by "
1151da177e4SLinus Torvalds 		    "number of stripes";
1161da177e4SLinus Torvalds 		return -EINVAL;
1171da177e4SLinus Torvalds 	}
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	/*
1201da177e4SLinus Torvalds 	 * Do we have enough arguments for that many stripes ?
1211da177e4SLinus Torvalds 	 */
1221da177e4SLinus Torvalds 	if (argc != (2 + 2 * stripes)) {
1231da177e4SLinus Torvalds 		ti->error = "dm-stripe: Not enough destinations "
1241da177e4SLinus Torvalds 			"specified";
1251da177e4SLinus Torvalds 		return -EINVAL;
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	sc = alloc_context(stripes);
1291da177e4SLinus Torvalds 	if (!sc) {
1301da177e4SLinus Torvalds 		ti->error = "dm-stripe: Memory allocation for striped context "
1311da177e4SLinus Torvalds 		    "failed";
1321da177e4SLinus Torvalds 		return -ENOMEM;
1331da177e4SLinus Torvalds 	}
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	sc->stripes = stripes;
1361da177e4SLinus Torvalds 	sc->stripe_width = width;
1371da177e4SLinus Torvalds 	ti->split_io = chunk_size;
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 	sc->chunk_mask = ((sector_t) chunk_size) - 1;
1401da177e4SLinus Torvalds 	for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
1411da177e4SLinus Torvalds 		chunk_size >>= 1;
1421da177e4SLinus Torvalds 	sc->chunk_shift--;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	/*
1451da177e4SLinus Torvalds 	 * Get the stripe destinations.
1461da177e4SLinus Torvalds 	 */
1471da177e4SLinus Torvalds 	for (i = 0; i < stripes; i++) {
1481da177e4SLinus Torvalds 		argv += 2;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 		r = get_stripe(ti, sc, i, argv);
1511da177e4SLinus Torvalds 		if (r < 0) {
1521da177e4SLinus Torvalds 			ti->error = "dm-stripe: Couldn't parse stripe "
1531da177e4SLinus Torvalds 				"destination";
1541da177e4SLinus Torvalds 			while (i--)
1551da177e4SLinus Torvalds 				dm_put_device(ti, sc->stripe[i].dev);
1561da177e4SLinus Torvalds 			kfree(sc);
1571da177e4SLinus Torvalds 			return r;
1581da177e4SLinus Torvalds 		}
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	ti->private = sc;
1621da177e4SLinus Torvalds 	return 0;
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds static void stripe_dtr(struct dm_target *ti)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds 	unsigned int i;
1681da177e4SLinus Torvalds 	struct stripe_c *sc = (struct stripe_c *) ti->private;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	for (i = 0; i < sc->stripes; i++)
1711da177e4SLinus Torvalds 		dm_put_device(ti, sc->stripe[i].dev);
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	kfree(sc);
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds static int stripe_map(struct dm_target *ti, struct bio *bio,
1771da177e4SLinus Torvalds 		      union map_info *map_context)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	struct stripe_c *sc = (struct stripe_c *) ti->private;
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	sector_t offset = bio->bi_sector - ti->begin;
1821da177e4SLinus Torvalds 	sector_t chunk = offset >> sc->chunk_shift;
1831da177e4SLinus Torvalds 	uint32_t stripe = sector_div(chunk, sc->stripes);
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	bio->bi_bdev = sc->stripe[stripe].dev->bdev;
1861da177e4SLinus Torvalds 	bio->bi_sector = sc->stripe[stripe].physical_start +
1871da177e4SLinus Torvalds 	    (chunk << sc->chunk_shift) + (offset & sc->chunk_mask);
1881da177e4SLinus Torvalds 	return 1;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds static int stripe_status(struct dm_target *ti,
1921da177e4SLinus Torvalds 			 status_type_t type, char *result, unsigned int maxlen)
1931da177e4SLinus Torvalds {
1941da177e4SLinus Torvalds 	struct stripe_c *sc = (struct stripe_c *) ti->private;
1951da177e4SLinus Torvalds 	unsigned int sz = 0;
1961da177e4SLinus Torvalds 	unsigned int i;
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 	switch (type) {
1991da177e4SLinus Torvalds 	case STATUSTYPE_INFO:
2001da177e4SLinus Torvalds 		result[0] = '\0';
2011da177e4SLinus Torvalds 		break;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	case STATUSTYPE_TABLE:
2044ee218cdSAndrew Morton 		DMEMIT("%d %llu", sc->stripes,
2054ee218cdSAndrew Morton 			(unsigned long long)sc->chunk_mask + 1);
2061da177e4SLinus Torvalds 		for (i = 0; i < sc->stripes; i++)
2074ee218cdSAndrew Morton 			DMEMIT(" %s %llu", sc->stripe[i].dev->name,
2084ee218cdSAndrew Morton 			    (unsigned long long)sc->stripe[i].physical_start);
2091da177e4SLinus Torvalds 		break;
2101da177e4SLinus Torvalds 	}
2111da177e4SLinus Torvalds 	return 0;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds static struct target_type stripe_target = {
2151da177e4SLinus Torvalds 	.name   = "striped",
2161da177e4SLinus Torvalds 	.version= {1, 0, 2},
2171da177e4SLinus Torvalds 	.module = THIS_MODULE,
2181da177e4SLinus Torvalds 	.ctr    = stripe_ctr,
2191da177e4SLinus Torvalds 	.dtr    = stripe_dtr,
2201da177e4SLinus Torvalds 	.map    = stripe_map,
2211da177e4SLinus Torvalds 	.status = stripe_status,
2221da177e4SLinus Torvalds };
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds int __init dm_stripe_init(void)
2251da177e4SLinus Torvalds {
2261da177e4SLinus Torvalds 	int r;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	r = dm_register_target(&stripe_target);
2291da177e4SLinus Torvalds 	if (r < 0)
2301da177e4SLinus Torvalds 		DMWARN("striped target registration failed");
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	return r;
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds void dm_stripe_exit(void)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	if (dm_unregister_target(&stripe_target))
2381da177e4SLinus Torvalds 		DMWARN("striped target unregistration failed");
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	return;
2411da177e4SLinus Torvalds }
242