xref: /openbmc/linux/drivers/md/dm-switch.c (revision 3664ff82)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2010-2012 by Dell Inc.  All rights reserved.
4  * Copyright (C) 2011-2013 Red Hat, Inc.
5  *
6  * This file is released under the GPL.
7  *
8  * dm-switch is a device-mapper target that maps IO to underlying block
9  * devices efficiently when there are a large number of fixed-sized
10  * address regions but there is no simple pattern to allow for a compact
11  * mapping representation such as dm-stripe.
12  */
13 
14 #include <linux/device-mapper.h>
15 
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/vmalloc.h>
19 
20 #define DM_MSG_PREFIX "switch"
21 
22 /*
23  * One region_table_slot_t holds <region_entries_per_slot> region table
24  * entries each of which is <region_table_entry_bits> in size.
25  */
26 typedef unsigned long region_table_slot_t;
27 
28 /*
29  * A device with the offset to its start sector.
30  */
31 struct switch_path {
32 	struct dm_dev *dmdev;
33 	sector_t start;
34 };
35 
36 /*
37  * Context block for a dm switch device.
38  */
39 struct switch_ctx {
40 	struct dm_target *ti;
41 
42 	unsigned int nr_paths;		/* Number of paths in path_list. */
43 
44 	unsigned int region_size;		/* Region size in 512-byte sectors */
45 	unsigned long nr_regions;	/* Number of regions making up the device */
46 	signed char region_size_bits;	/* log2 of region_size or -1 */
47 
48 	unsigned char region_table_entry_bits;	/* Number of bits in one region table entry */
49 	unsigned char region_entries_per_slot;	/* Number of entries in one region table slot */
50 	signed char region_entries_per_slot_bits;	/* log2 of region_entries_per_slot or -1 */
51 
52 	region_table_slot_t *region_table;	/* Region table */
53 
54 	/*
55 	 * Array of dm devices to switch between.
56 	 */
57 	struct switch_path path_list[];
58 };
59 
alloc_switch_ctx(struct dm_target * ti,unsigned int nr_paths,unsigned int region_size)60 static struct switch_ctx *alloc_switch_ctx(struct dm_target *ti, unsigned int nr_paths,
61 					   unsigned int region_size)
62 {
63 	struct switch_ctx *sctx;
64 
65 	sctx = kzalloc(struct_size(sctx, path_list, nr_paths), GFP_KERNEL);
66 	if (!sctx)
67 		return NULL;
68 
69 	sctx->ti = ti;
70 	sctx->region_size = region_size;
71 
72 	ti->private = sctx;
73 
74 	return sctx;
75 }
76 
alloc_region_table(struct dm_target * ti,unsigned int nr_paths)77 static int alloc_region_table(struct dm_target *ti, unsigned int nr_paths)
78 {
79 	struct switch_ctx *sctx = ti->private;
80 	sector_t nr_regions = ti->len;
81 	sector_t nr_slots;
82 
83 	if (!(sctx->region_size & (sctx->region_size - 1)))
84 		sctx->region_size_bits = __ffs(sctx->region_size);
85 	else
86 		sctx->region_size_bits = -1;
87 
88 	sctx->region_table_entry_bits = 1;
89 	while (sctx->region_table_entry_bits < sizeof(region_table_slot_t) * 8 &&
90 	       (region_table_slot_t)1 << sctx->region_table_entry_bits < nr_paths)
91 		sctx->region_table_entry_bits++;
92 
93 	sctx->region_entries_per_slot = (sizeof(region_table_slot_t) * 8) / sctx->region_table_entry_bits;
94 	if (!(sctx->region_entries_per_slot & (sctx->region_entries_per_slot - 1)))
95 		sctx->region_entries_per_slot_bits = __ffs(sctx->region_entries_per_slot);
96 	else
97 		sctx->region_entries_per_slot_bits = -1;
98 
99 	if (sector_div(nr_regions, sctx->region_size))
100 		nr_regions++;
101 
102 	if (nr_regions >= ULONG_MAX) {
103 		ti->error = "Region table too large";
104 		return -EINVAL;
105 	}
106 	sctx->nr_regions = nr_regions;
107 
108 	nr_slots = nr_regions;
109 	if (sector_div(nr_slots, sctx->region_entries_per_slot))
110 		nr_slots++;
111 
112 	if (nr_slots > ULONG_MAX / sizeof(region_table_slot_t)) {
113 		ti->error = "Region table too large";
114 		return -EINVAL;
115 	}
116 
117 	sctx->region_table = vmalloc(array_size(nr_slots,
118 						sizeof(region_table_slot_t)));
119 	if (!sctx->region_table) {
120 		ti->error = "Cannot allocate region table";
121 		return -ENOMEM;
122 	}
123 
124 	return 0;
125 }
126 
switch_get_position(struct switch_ctx * sctx,unsigned long region_nr,unsigned long * region_index,unsigned int * bit)127 static void switch_get_position(struct switch_ctx *sctx, unsigned long region_nr,
128 				unsigned long *region_index, unsigned int *bit)
129 {
130 	if (sctx->region_entries_per_slot_bits >= 0) {
131 		*region_index = region_nr >> sctx->region_entries_per_slot_bits;
132 		*bit = region_nr & (sctx->region_entries_per_slot - 1);
133 	} else {
134 		*region_index = region_nr / sctx->region_entries_per_slot;
135 		*bit = region_nr % sctx->region_entries_per_slot;
136 	}
137 
138 	*bit *= sctx->region_table_entry_bits;
139 }
140 
switch_region_table_read(struct switch_ctx * sctx,unsigned long region_nr)141 static unsigned int switch_region_table_read(struct switch_ctx *sctx, unsigned long region_nr)
142 {
143 	unsigned long region_index;
144 	unsigned int bit;
145 
146 	switch_get_position(sctx, region_nr, &region_index, &bit);
147 
148 	return (READ_ONCE(sctx->region_table[region_index]) >> bit) &
149 		((1 << sctx->region_table_entry_bits) - 1);
150 }
151 
152 /*
153  * Find which path to use at given offset.
154  */
switch_get_path_nr(struct switch_ctx * sctx,sector_t offset)155 static unsigned int switch_get_path_nr(struct switch_ctx *sctx, sector_t offset)
156 {
157 	unsigned int path_nr;
158 	sector_t p;
159 
160 	p = offset;
161 	if (sctx->region_size_bits >= 0)
162 		p >>= sctx->region_size_bits;
163 	else
164 		sector_div(p, sctx->region_size);
165 
166 	path_nr = switch_region_table_read(sctx, p);
167 
168 	/* This can only happen if the processor uses non-atomic stores. */
169 	if (unlikely(path_nr >= sctx->nr_paths))
170 		path_nr = 0;
171 
172 	return path_nr;
173 }
174 
switch_region_table_write(struct switch_ctx * sctx,unsigned long region_nr,unsigned int value)175 static void switch_region_table_write(struct switch_ctx *sctx, unsigned long region_nr,
176 				      unsigned int value)
177 {
178 	unsigned long region_index;
179 	unsigned int bit;
180 	region_table_slot_t pte;
181 
182 	switch_get_position(sctx, region_nr, &region_index, &bit);
183 
184 	pte = sctx->region_table[region_index];
185 	pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit);
186 	pte |= (region_table_slot_t)value << bit;
187 	sctx->region_table[region_index] = pte;
188 }
189 
190 /*
191  * Fill the region table with an initial round robin pattern.
192  */
initialise_region_table(struct switch_ctx * sctx)193 static void initialise_region_table(struct switch_ctx *sctx)
194 {
195 	unsigned int path_nr = 0;
196 	unsigned long region_nr;
197 
198 	for (region_nr = 0; region_nr < sctx->nr_regions; region_nr++) {
199 		switch_region_table_write(sctx, region_nr, path_nr);
200 		if (++path_nr >= sctx->nr_paths)
201 			path_nr = 0;
202 	}
203 }
204 
parse_path(struct dm_arg_set * as,struct dm_target * ti)205 static int parse_path(struct dm_arg_set *as, struct dm_target *ti)
206 {
207 	struct switch_ctx *sctx = ti->private;
208 	unsigned long long start;
209 	int r;
210 
211 	r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
212 			  &sctx->path_list[sctx->nr_paths].dmdev);
213 	if (r) {
214 		ti->error = "Device lookup failed";
215 		return r;
216 	}
217 
218 	if (kstrtoull(dm_shift_arg(as), 10, &start) || start != (sector_t)start) {
219 		ti->error = "Invalid device starting offset";
220 		dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev);
221 		return -EINVAL;
222 	}
223 
224 	sctx->path_list[sctx->nr_paths].start = start;
225 
226 	sctx->nr_paths++;
227 
228 	return 0;
229 }
230 
231 /*
232  * Destructor: Don't free the dm_target, just the ti->private data (if any).
233  */
switch_dtr(struct dm_target * ti)234 static void switch_dtr(struct dm_target *ti)
235 {
236 	struct switch_ctx *sctx = ti->private;
237 
238 	while (sctx->nr_paths--)
239 		dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev);
240 
241 	vfree(sctx->region_table);
242 	kfree(sctx);
243 }
244 
245 /*
246  * Constructor arguments:
247  *   <num_paths> <region_size> <num_optional_args> [<optional_args>...]
248  *   [<dev_path> <offset>]+
249  *
250  * Optional args are to allow for future extension: currently this
251  * parameter must be 0.
252  */
switch_ctr(struct dm_target * ti,unsigned int argc,char ** argv)253 static int switch_ctr(struct dm_target *ti, unsigned int argc, char **argv)
254 {
255 	static const struct dm_arg _args[] = {
256 		{1, (KMALLOC_MAX_SIZE - sizeof(struct switch_ctx)) / sizeof(struct switch_path), "Invalid number of paths"},
257 		{1, UINT_MAX, "Invalid region size"},
258 		{0, 0, "Invalid number of optional args"},
259 	};
260 
261 	struct switch_ctx *sctx;
262 	struct dm_arg_set as;
263 	unsigned int nr_paths, region_size, nr_optional_args;
264 	int r;
265 
266 	as.argc = argc;
267 	as.argv = argv;
268 
269 	r = dm_read_arg(_args, &as, &nr_paths, &ti->error);
270 	if (r)
271 		return -EINVAL;
272 
273 	r = dm_read_arg(_args + 1, &as, &region_size, &ti->error);
274 	if (r)
275 		return r;
276 
277 	r = dm_read_arg_group(_args + 2, &as, &nr_optional_args, &ti->error);
278 	if (r)
279 		return r;
280 	/* parse optional arguments here, if we add any */
281 
282 	if (as.argc != nr_paths * 2) {
283 		ti->error = "Incorrect number of path arguments";
284 		return -EINVAL;
285 	}
286 
287 	sctx = alloc_switch_ctx(ti, nr_paths, region_size);
288 	if (!sctx) {
289 		ti->error = "Cannot allocate redirection context";
290 		return -ENOMEM;
291 	}
292 
293 	r = dm_set_target_max_io_len(ti, region_size);
294 	if (r)
295 		goto error;
296 
297 	while (as.argc) {
298 		r = parse_path(&as, ti);
299 		if (r)
300 			goto error;
301 	}
302 
303 	r = alloc_region_table(ti, nr_paths);
304 	if (r)
305 		goto error;
306 
307 	initialise_region_table(sctx);
308 
309 	/* For UNMAP, sending the request down any path is sufficient */
310 	ti->num_discard_bios = 1;
311 
312 	return 0;
313 
314 error:
315 	switch_dtr(ti);
316 
317 	return r;
318 }
319 
switch_map(struct dm_target * ti,struct bio * bio)320 static int switch_map(struct dm_target *ti, struct bio *bio)
321 {
322 	struct switch_ctx *sctx = ti->private;
323 	sector_t offset = dm_target_offset(ti, bio->bi_iter.bi_sector);
324 	unsigned int path_nr = switch_get_path_nr(sctx, offset);
325 
326 	bio_set_dev(bio, sctx->path_list[path_nr].dmdev->bdev);
327 	bio->bi_iter.bi_sector = sctx->path_list[path_nr].start + offset;
328 
329 	return DM_MAPIO_REMAPPED;
330 }
331 
332 /*
333  * We need to parse hex numbers in the message as quickly as possible.
334  *
335  * This table-based hex parser improves performance.
336  * It improves a time to load 1000000 entries compared to the condition-based
337  * parser.
338  *		table-based parser	condition-based parser
339  * PA-RISC	0.29s			0.31s
340  * Opteron	0.0495s			0.0498s
341  */
342 static const unsigned char hex_table[256] = {
343 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
344 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
345 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
346 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
347 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
348 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
349 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
350 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
351 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
352 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
353 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
354 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
355 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
356 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
357 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
358 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
359 };
360 
parse_hex(const char ** string)361 static __always_inline unsigned long parse_hex(const char **string)
362 {
363 	unsigned char d;
364 	unsigned long r = 0;
365 
366 	while ((d = hex_table[(unsigned char)**string]) < 16) {
367 		r = (r << 4) | d;
368 		(*string)++;
369 	}
370 
371 	return r;
372 }
373 
process_set_region_mappings(struct switch_ctx * sctx,unsigned int argc,char ** argv)374 static int process_set_region_mappings(struct switch_ctx *sctx,
375 				       unsigned int argc, char **argv)
376 {
377 	unsigned int i;
378 	unsigned long region_index = 0;
379 
380 	for (i = 1; i < argc; i++) {
381 		unsigned long path_nr;
382 		const char *string = argv[i];
383 
384 		if ((*string & 0xdf) == 'R') {
385 			unsigned long cycle_length, num_write;
386 
387 			string++;
388 			if (unlikely(*string == ',')) {
389 				DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
390 				return -EINVAL;
391 			}
392 			cycle_length = parse_hex(&string);
393 			if (unlikely(*string != ',')) {
394 				DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
395 				return -EINVAL;
396 			}
397 			string++;
398 			if (unlikely(!*string)) {
399 				DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
400 				return -EINVAL;
401 			}
402 			num_write = parse_hex(&string);
403 			if (unlikely(*string)) {
404 				DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
405 				return -EINVAL;
406 			}
407 
408 			if (unlikely(!cycle_length) || unlikely(cycle_length - 1 > region_index)) {
409 				DMWARN("invalid set_region_mappings cycle length: %lu > %lu",
410 				       cycle_length - 1, region_index);
411 				return -EINVAL;
412 			}
413 			if (unlikely(region_index + num_write < region_index) ||
414 			    unlikely(region_index + num_write >= sctx->nr_regions)) {
415 				DMWARN("invalid set_region_mappings region number: %lu + %lu >= %lu",
416 				       region_index, num_write, sctx->nr_regions);
417 				return -EINVAL;
418 			}
419 
420 			while (num_write--) {
421 				region_index++;
422 				path_nr = switch_region_table_read(sctx, region_index - cycle_length);
423 				switch_region_table_write(sctx, region_index, path_nr);
424 			}
425 
426 			continue;
427 		}
428 
429 		if (*string == ':')
430 			region_index++;
431 		else {
432 			region_index = parse_hex(&string);
433 			if (unlikely(*string != ':')) {
434 				DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
435 				return -EINVAL;
436 			}
437 		}
438 
439 		string++;
440 		if (unlikely(!*string)) {
441 			DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
442 			return -EINVAL;
443 		}
444 
445 		path_nr = parse_hex(&string);
446 		if (unlikely(*string)) {
447 			DMWARN("invalid set_region_mappings argument: '%s'", argv[i]);
448 			return -EINVAL;
449 		}
450 		if (unlikely(region_index >= sctx->nr_regions)) {
451 			DMWARN("invalid set_region_mappings region number: %lu >= %lu", region_index, sctx->nr_regions);
452 			return -EINVAL;
453 		}
454 		if (unlikely(path_nr >= sctx->nr_paths)) {
455 			DMWARN("invalid set_region_mappings device: %lu >= %u", path_nr, sctx->nr_paths);
456 			return -EINVAL;
457 		}
458 
459 		switch_region_table_write(sctx, region_index, path_nr);
460 	}
461 
462 	return 0;
463 }
464 
465 /*
466  * Messages are processed one-at-a-time.
467  *
468  * Only set_region_mappings is supported.
469  */
switch_message(struct dm_target * ti,unsigned int argc,char ** argv,char * result,unsigned int maxlen)470 static int switch_message(struct dm_target *ti, unsigned int argc, char **argv,
471 			  char *result, unsigned int maxlen)
472 {
473 	static DEFINE_MUTEX(message_mutex);
474 
475 	struct switch_ctx *sctx = ti->private;
476 	int r = -EINVAL;
477 
478 	mutex_lock(&message_mutex);
479 
480 	if (!strcasecmp(argv[0], "set_region_mappings"))
481 		r = process_set_region_mappings(sctx, argc, argv);
482 	else
483 		DMWARN("Unrecognised message received.");
484 
485 	mutex_unlock(&message_mutex);
486 
487 	return r;
488 }
489 
switch_status(struct dm_target * ti,status_type_t type,unsigned int status_flags,char * result,unsigned int maxlen)490 static void switch_status(struct dm_target *ti, status_type_t type,
491 			  unsigned int status_flags, char *result, unsigned int maxlen)
492 {
493 	struct switch_ctx *sctx = ti->private;
494 	unsigned int sz = 0;
495 	int path_nr;
496 
497 	switch (type) {
498 	case STATUSTYPE_INFO:
499 		result[0] = '\0';
500 		break;
501 
502 	case STATUSTYPE_TABLE:
503 		DMEMIT("%u %u 0", sctx->nr_paths, sctx->region_size);
504 		for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++)
505 			DMEMIT(" %s %llu", sctx->path_list[path_nr].dmdev->name,
506 			       (unsigned long long)sctx->path_list[path_nr].start);
507 		break;
508 
509 	case STATUSTYPE_IMA:
510 		result[0] = '\0';
511 		break;
512 	}
513 }
514 
515 /*
516  * Switch ioctl:
517  *
518  * Passthrough all ioctls to the path for sector 0
519  */
switch_prepare_ioctl(struct dm_target * ti,struct block_device ** bdev)520 static int switch_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
521 {
522 	struct switch_ctx *sctx = ti->private;
523 	unsigned int path_nr;
524 
525 	path_nr = switch_get_path_nr(sctx, 0);
526 
527 	*bdev = sctx->path_list[path_nr].dmdev->bdev;
528 
529 	/*
530 	 * Only pass ioctls through if the device sizes match exactly.
531 	 */
532 	if (ti->len + sctx->path_list[path_nr].start !=
533 	    bdev_nr_sectors((*bdev)))
534 		return 1;
535 	return 0;
536 }
537 
switch_iterate_devices(struct dm_target * ti,iterate_devices_callout_fn fn,void * data)538 static int switch_iterate_devices(struct dm_target *ti,
539 				  iterate_devices_callout_fn fn, void *data)
540 {
541 	struct switch_ctx *sctx = ti->private;
542 	int path_nr;
543 	int r;
544 
545 	for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++) {
546 		r = fn(ti, sctx->path_list[path_nr].dmdev,
547 			 sctx->path_list[path_nr].start, ti->len, data);
548 		if (r)
549 			return r;
550 	}
551 
552 	return 0;
553 }
554 
555 static struct target_type switch_target = {
556 	.name = "switch",
557 	.version = {1, 1, 0},
558 	.features = DM_TARGET_NOWAIT,
559 	.module = THIS_MODULE,
560 	.ctr = switch_ctr,
561 	.dtr = switch_dtr,
562 	.map = switch_map,
563 	.message = switch_message,
564 	.status = switch_status,
565 	.prepare_ioctl = switch_prepare_ioctl,
566 	.iterate_devices = switch_iterate_devices,
567 };
568 module_dm(switch);
569 
570 MODULE_DESCRIPTION(DM_NAME " dynamic path switching target");
571 MODULE_AUTHOR("Kevin D. O'Kelley <Kevin_OKelley@dell.com>");
572 MODULE_AUTHOR("Narendran Ganapathy <Narendran_Ganapathy@dell.com>");
573 MODULE_AUTHOR("Jim Ramsay <Jim_Ramsay@dell.com>");
574 MODULE_AUTHOR("Mikulas Patocka <mpatocka@redhat.com>");
575 MODULE_LICENSE("GPL");
576