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