xref: /openbmc/linux/drivers/md/dm-unstripe.c (revision 7b453719)
1 /*
2  * Copyright (C) 2017 Intel Corporation.
3  *
4  * This file is released under the GPL.
5  */
6 
7 #include "dm.h"
8 
9 #include <linux/module.h>
10 
11 struct unstripe_c {
12 	struct dm_dev *dev;
13 	sector_t physical_start;
14 
15 	uint32_t stripes;
16 
17 	uint32_t unstripe;
18 	sector_t unstripe_width;
19 	sector_t unstripe_offset;
20 
21 	uint32_t chunk_size;
22 	u8 chunk_shift;
23 };
24 
25 #define DM_MSG_PREFIX "unstriped"
26 
27 static void cleanup_unstripe(struct unstripe_c *uc, struct dm_target *ti)
28 {
29 	if (uc->dev)
30 		dm_put_device(ti, uc->dev);
31 	kfree(uc);
32 }
33 
34 /*
35  * Contruct an unstriped mapping.
36  * <number of stripes> <chunk size> <stripe #> <dev_path> <offset>
37  */
38 static int unstripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
39 {
40 	struct unstripe_c *uc;
41 	sector_t tmp_len;
42 	unsigned long long start;
43 	char dummy;
44 
45 	if (argc != 5) {
46 		ti->error = "Invalid number of arguments";
47 		return -EINVAL;
48 	}
49 
50 	uc = kzalloc(sizeof(*uc), GFP_KERNEL);
51 	if (!uc) {
52 		ti->error = "Memory allocation for unstriped context failed";
53 		return -ENOMEM;
54 	}
55 
56 	if (kstrtouint(argv[0], 10, &uc->stripes) || !uc->stripes) {
57 		ti->error = "Invalid stripe count";
58 		goto err;
59 	}
60 
61 	if (kstrtouint(argv[1], 10, &uc->chunk_size) || !uc->chunk_size) {
62 		ti->error = "Invalid chunk_size";
63 		goto err;
64 	}
65 
66 	if (kstrtouint(argv[2], 10, &uc->unstripe)) {
67 		ti->error = "Invalid stripe number";
68 		goto err;
69 	}
70 
71 	if (uc->unstripe > uc->stripes && uc->stripes > 1) {
72 		ti->error = "Please provide stripe between [0, # of stripes]";
73 		goto err;
74 	}
75 
76 	if (dm_get_device(ti, argv[3], dm_table_get_mode(ti->table), &uc->dev)) {
77 		ti->error = "Couldn't get striped device";
78 		goto err;
79 	}
80 
81 	if (sscanf(argv[4], "%llu%c", &start, &dummy) != 1 || start != (sector_t)start) {
82 		ti->error = "Invalid striped device offset";
83 		goto err;
84 	}
85 	uc->physical_start = start;
86 
87 	uc->unstripe_offset = uc->unstripe * uc->chunk_size;
88 	uc->unstripe_width = (uc->stripes - 1) * uc->chunk_size;
89 	uc->chunk_shift = is_power_of_2(uc->chunk_size) ? fls(uc->chunk_size) - 1 : 0;
90 
91 	tmp_len = ti->len;
92 	if (sector_div(tmp_len, uc->chunk_size)) {
93 		ti->error = "Target length not divisible by chunk size";
94 		goto err;
95 	}
96 
97 	if (dm_set_target_max_io_len(ti, uc->chunk_size)) {
98 		ti->error = "Failed to set max io len";
99 		goto err;
100 	}
101 
102 	ti->private = uc;
103 	return 0;
104 err:
105 	cleanup_unstripe(uc, ti);
106 	return -EINVAL;
107 }
108 
109 static void unstripe_dtr(struct dm_target *ti)
110 {
111 	struct unstripe_c *uc = ti->private;
112 
113 	cleanup_unstripe(uc, ti);
114 }
115 
116 static sector_t map_to_core(struct dm_target *ti, struct bio *bio)
117 {
118 	struct unstripe_c *uc = ti->private;
119 	sector_t sector = bio->bi_iter.bi_sector;
120 	sector_t tmp_sector = sector;
121 
122 	/* Shift us up to the right "row" on the stripe */
123 	if (uc->chunk_shift)
124 		tmp_sector >>= uc->chunk_shift;
125 	else
126 		sector_div(tmp_sector, uc->chunk_size);
127 
128 	sector += uc->unstripe_width * tmp_sector;
129 
130 	/* Account for what stripe we're operating on */
131 	return sector + uc->unstripe_offset;
132 }
133 
134 static int unstripe_map(struct dm_target *ti, struct bio *bio)
135 {
136 	struct unstripe_c *uc = ti->private;
137 
138 	bio_set_dev(bio, uc->dev->bdev);
139 	bio->bi_iter.bi_sector = map_to_core(ti, bio) + uc->physical_start;
140 
141 	return DM_MAPIO_REMAPPED;
142 }
143 
144 static void unstripe_status(struct dm_target *ti, status_type_t type,
145 			    unsigned int status_flags, char *result, unsigned int maxlen)
146 {
147 	struct unstripe_c *uc = ti->private;
148 	unsigned int sz = 0;
149 
150 	switch (type) {
151 	case STATUSTYPE_INFO:
152 		break;
153 
154 	case STATUSTYPE_TABLE:
155 		DMEMIT("%d %llu %d %s %llu",
156 		       uc->stripes, (unsigned long long)uc->chunk_size, uc->unstripe,
157 		       uc->dev->name, (unsigned long long)uc->physical_start);
158 		break;
159 
160 	case STATUSTYPE_IMA:
161 		*result = '\0';
162 		break;
163 	}
164 }
165 
166 static int unstripe_iterate_devices(struct dm_target *ti,
167 				    iterate_devices_callout_fn fn, void *data)
168 {
169 	struct unstripe_c *uc = ti->private;
170 
171 	return fn(ti, uc->dev, uc->physical_start, ti->len, data);
172 }
173 
174 static void unstripe_io_hints(struct dm_target *ti,
175 			       struct queue_limits *limits)
176 {
177 	struct unstripe_c *uc = ti->private;
178 
179 	limits->chunk_sectors = uc->chunk_size;
180 }
181 
182 static struct target_type unstripe_target = {
183 	.name = "unstriped",
184 	.version = {1, 1, 0},
185 	.features = DM_TARGET_NOWAIT,
186 	.module = THIS_MODULE,
187 	.ctr = unstripe_ctr,
188 	.dtr = unstripe_dtr,
189 	.map = unstripe_map,
190 	.status = unstripe_status,
191 	.iterate_devices = unstripe_iterate_devices,
192 	.io_hints = unstripe_io_hints,
193 };
194 
195 static int __init dm_unstripe_init(void)
196 {
197 	return dm_register_target(&unstripe_target);
198 }
199 
200 static void __exit dm_unstripe_exit(void)
201 {
202 	dm_unregister_target(&unstripe_target);
203 }
204 
205 module_init(dm_unstripe_init);
206 module_exit(dm_unstripe_exit);
207 
208 MODULE_DESCRIPTION(DM_NAME " unstriped target");
209 MODULE_ALIAS("dm-unstriped");
210 MODULE_AUTHOR("Scott Bauer <scott.bauer@intel.com>");
211 MODULE_LICENSE("GPL");
212