18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2010-2012 by Dell Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (C) 2011-2013 Red Hat, Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file is released under the GPL. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * dm-switch is a device-mapper target that maps IO to underlying block 88c2ecf20Sopenharmony_ci * devices efficiently when there are a large number of fixed-sized 98c2ecf20Sopenharmony_ci * address regions but there is no simple pattern to allow for a compact 108c2ecf20Sopenharmony_ci * mapping representation such as dm-stripe. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/device-mapper.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "switch" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * One region_table_slot_t holds <region_entries_per_slot> region table 238c2ecf20Sopenharmony_ci * entries each of which is <region_table_entry_bits> in size. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_citypedef unsigned long region_table_slot_t; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * A device with the offset to its start sector. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistruct switch_path { 318c2ecf20Sopenharmony_ci struct dm_dev *dmdev; 328c2ecf20Sopenharmony_ci sector_t start; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Context block for a dm switch device. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistruct switch_ctx { 398c2ecf20Sopenharmony_ci struct dm_target *ti; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci unsigned nr_paths; /* Number of paths in path_list. */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci unsigned region_size; /* Region size in 512-byte sectors */ 448c2ecf20Sopenharmony_ci unsigned long nr_regions; /* Number of regions making up the device */ 458c2ecf20Sopenharmony_ci signed char region_size_bits; /* log2 of region_size or -1 */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci unsigned char region_table_entry_bits; /* Number of bits in one region table entry */ 488c2ecf20Sopenharmony_ci unsigned char region_entries_per_slot; /* Number of entries in one region table slot */ 498c2ecf20Sopenharmony_ci signed char region_entries_per_slot_bits; /* log2 of region_entries_per_slot or -1 */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci region_table_slot_t *region_table; /* Region table */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Array of dm devices to switch between. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci struct switch_path path_list[]; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct switch_ctx *alloc_switch_ctx(struct dm_target *ti, unsigned nr_paths, 608c2ecf20Sopenharmony_ci unsigned region_size) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct switch_ctx *sctx; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci sctx = kzalloc(struct_size(sctx, path_list, nr_paths), GFP_KERNEL); 658c2ecf20Sopenharmony_ci if (!sctx) 668c2ecf20Sopenharmony_ci return NULL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci sctx->ti = ti; 698c2ecf20Sopenharmony_ci sctx->region_size = region_size; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ti->private = sctx; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return sctx; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int alloc_region_table(struct dm_target *ti, unsigned nr_paths) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 798c2ecf20Sopenharmony_ci sector_t nr_regions = ti->len; 808c2ecf20Sopenharmony_ci sector_t nr_slots; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (!(sctx->region_size & (sctx->region_size - 1))) 838c2ecf20Sopenharmony_ci sctx->region_size_bits = __ffs(sctx->region_size); 848c2ecf20Sopenharmony_ci else 858c2ecf20Sopenharmony_ci sctx->region_size_bits = -1; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci sctx->region_table_entry_bits = 1; 888c2ecf20Sopenharmony_ci while (sctx->region_table_entry_bits < sizeof(region_table_slot_t) * 8 && 898c2ecf20Sopenharmony_ci (region_table_slot_t)1 << sctx->region_table_entry_bits < nr_paths) 908c2ecf20Sopenharmony_ci sctx->region_table_entry_bits++; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci sctx->region_entries_per_slot = (sizeof(region_table_slot_t) * 8) / sctx->region_table_entry_bits; 938c2ecf20Sopenharmony_ci if (!(sctx->region_entries_per_slot & (sctx->region_entries_per_slot - 1))) 948c2ecf20Sopenharmony_ci sctx->region_entries_per_slot_bits = __ffs(sctx->region_entries_per_slot); 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci sctx->region_entries_per_slot_bits = -1; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (sector_div(nr_regions, sctx->region_size)) 998c2ecf20Sopenharmony_ci nr_regions++; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (nr_regions >= ULONG_MAX) { 1028c2ecf20Sopenharmony_ci ti->error = "Region table too large"; 1038c2ecf20Sopenharmony_ci return -EINVAL; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci sctx->nr_regions = nr_regions; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci nr_slots = nr_regions; 1088c2ecf20Sopenharmony_ci if (sector_div(nr_slots, sctx->region_entries_per_slot)) 1098c2ecf20Sopenharmony_ci nr_slots++; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (nr_slots > ULONG_MAX / sizeof(region_table_slot_t)) { 1128c2ecf20Sopenharmony_ci ti->error = "Region table too large"; 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci sctx->region_table = vmalloc(array_size(nr_slots, 1178c2ecf20Sopenharmony_ci sizeof(region_table_slot_t))); 1188c2ecf20Sopenharmony_ci if (!sctx->region_table) { 1198c2ecf20Sopenharmony_ci ti->error = "Cannot allocate region table"; 1208c2ecf20Sopenharmony_ci return -ENOMEM; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void switch_get_position(struct switch_ctx *sctx, unsigned long region_nr, 1278c2ecf20Sopenharmony_ci unsigned long *region_index, unsigned *bit) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (sctx->region_entries_per_slot_bits >= 0) { 1308c2ecf20Sopenharmony_ci *region_index = region_nr >> sctx->region_entries_per_slot_bits; 1318c2ecf20Sopenharmony_ci *bit = region_nr & (sctx->region_entries_per_slot - 1); 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci *region_index = region_nr / sctx->region_entries_per_slot; 1348c2ecf20Sopenharmony_ci *bit = region_nr % sctx->region_entries_per_slot; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci *bit *= sctx->region_table_entry_bits; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic unsigned switch_region_table_read(struct switch_ctx *sctx, unsigned long region_nr) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci unsigned long region_index; 1438c2ecf20Sopenharmony_ci unsigned bit; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci switch_get_position(sctx, region_nr, ®ion_index, &bit); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return (READ_ONCE(sctx->region_table[region_index]) >> bit) & 1488c2ecf20Sopenharmony_ci ((1 << sctx->region_table_entry_bits) - 1); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Find which path to use at given offset. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic unsigned switch_get_path_nr(struct switch_ctx *sctx, sector_t offset) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned path_nr; 1578c2ecf20Sopenharmony_ci sector_t p; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci p = offset; 1608c2ecf20Sopenharmony_ci if (sctx->region_size_bits >= 0) 1618c2ecf20Sopenharmony_ci p >>= sctx->region_size_bits; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci sector_div(p, sctx->region_size); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci path_nr = switch_region_table_read(sctx, p); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* This can only happen if the processor uses non-atomic stores. */ 1688c2ecf20Sopenharmony_ci if (unlikely(path_nr >= sctx->nr_paths)) 1698c2ecf20Sopenharmony_ci path_nr = 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return path_nr; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void switch_region_table_write(struct switch_ctx *sctx, unsigned long region_nr, 1758c2ecf20Sopenharmony_ci unsigned value) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci unsigned long region_index; 1788c2ecf20Sopenharmony_ci unsigned bit; 1798c2ecf20Sopenharmony_ci region_table_slot_t pte; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci switch_get_position(sctx, region_nr, ®ion_index, &bit); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci pte = sctx->region_table[region_index]; 1848c2ecf20Sopenharmony_ci pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit); 1858c2ecf20Sopenharmony_ci pte |= (region_table_slot_t)value << bit; 1868c2ecf20Sopenharmony_ci sctx->region_table[region_index] = pte; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Fill the region table with an initial round robin pattern. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic void initialise_region_table(struct switch_ctx *sctx) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci unsigned path_nr = 0; 1958c2ecf20Sopenharmony_ci unsigned long region_nr; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci for (region_nr = 0; region_nr < sctx->nr_regions; region_nr++) { 1988c2ecf20Sopenharmony_ci switch_region_table_write(sctx, region_nr, path_nr); 1998c2ecf20Sopenharmony_ci if (++path_nr >= sctx->nr_paths) 2008c2ecf20Sopenharmony_ci path_nr = 0; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int parse_path(struct dm_arg_set *as, struct dm_target *ti) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 2078c2ecf20Sopenharmony_ci unsigned long long start; 2088c2ecf20Sopenharmony_ci int r; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table), 2118c2ecf20Sopenharmony_ci &sctx->path_list[sctx->nr_paths].dmdev); 2128c2ecf20Sopenharmony_ci if (r) { 2138c2ecf20Sopenharmony_ci ti->error = "Device lookup failed"; 2148c2ecf20Sopenharmony_ci return r; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (kstrtoull(dm_shift_arg(as), 10, &start) || start != (sector_t)start) { 2188c2ecf20Sopenharmony_ci ti->error = "Invalid device starting offset"; 2198c2ecf20Sopenharmony_ci dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev); 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci sctx->path_list[sctx->nr_paths].start = start; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci sctx->nr_paths++; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * Destructor: Don't free the dm_target, just the ti->private data (if any). 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic void switch_dtr(struct dm_target *ti) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci while (sctx->nr_paths--) 2388c2ecf20Sopenharmony_ci dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci vfree(sctx->region_table); 2418c2ecf20Sopenharmony_ci kfree(sctx); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * Constructor arguments: 2468c2ecf20Sopenharmony_ci * <num_paths> <region_size> <num_optional_args> [<optional_args>...] 2478c2ecf20Sopenharmony_ci * [<dev_path> <offset>]+ 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * Optional args are to allow for future extension: currently this 2508c2ecf20Sopenharmony_ci * parameter must be 0. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic int switch_ctr(struct dm_target *ti, unsigned argc, char **argv) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci static const struct dm_arg _args[] = { 2558c2ecf20Sopenharmony_ci {1, (KMALLOC_MAX_SIZE - sizeof(struct switch_ctx)) / sizeof(struct switch_path), "Invalid number of paths"}, 2568c2ecf20Sopenharmony_ci {1, UINT_MAX, "Invalid region size"}, 2578c2ecf20Sopenharmony_ci {0, 0, "Invalid number of optional args"}, 2588c2ecf20Sopenharmony_ci }; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci struct switch_ctx *sctx; 2618c2ecf20Sopenharmony_ci struct dm_arg_set as; 2628c2ecf20Sopenharmony_ci unsigned nr_paths, region_size, nr_optional_args; 2638c2ecf20Sopenharmony_ci int r; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci as.argc = argc; 2668c2ecf20Sopenharmony_ci as.argv = argv; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci r = dm_read_arg(_args, &as, &nr_paths, &ti->error); 2698c2ecf20Sopenharmony_ci if (r) 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci r = dm_read_arg(_args + 1, &as, ®ion_size, &ti->error); 2738c2ecf20Sopenharmony_ci if (r) 2748c2ecf20Sopenharmony_ci return r; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci r = dm_read_arg_group(_args + 2, &as, &nr_optional_args, &ti->error); 2778c2ecf20Sopenharmony_ci if (r) 2788c2ecf20Sopenharmony_ci return r; 2798c2ecf20Sopenharmony_ci /* parse optional arguments here, if we add any */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (as.argc != nr_paths * 2) { 2828c2ecf20Sopenharmony_ci ti->error = "Incorrect number of path arguments"; 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci sctx = alloc_switch_ctx(ti, nr_paths, region_size); 2878c2ecf20Sopenharmony_ci if (!sctx) { 2888c2ecf20Sopenharmony_ci ti->error = "Cannot allocate redirection context"; 2898c2ecf20Sopenharmony_ci return -ENOMEM; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci r = dm_set_target_max_io_len(ti, region_size); 2938c2ecf20Sopenharmony_ci if (r) 2948c2ecf20Sopenharmony_ci goto error; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci while (as.argc) { 2978c2ecf20Sopenharmony_ci r = parse_path(&as, ti); 2988c2ecf20Sopenharmony_ci if (r) 2998c2ecf20Sopenharmony_ci goto error; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci r = alloc_region_table(ti, nr_paths); 3038c2ecf20Sopenharmony_ci if (r) 3048c2ecf20Sopenharmony_ci goto error; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci initialise_region_table(sctx); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* For UNMAP, sending the request down any path is sufficient */ 3098c2ecf20Sopenharmony_ci ti->num_discard_bios = 1; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cierror: 3148c2ecf20Sopenharmony_ci switch_dtr(ti); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return r; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int switch_map(struct dm_target *ti, struct bio *bio) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 3228c2ecf20Sopenharmony_ci sector_t offset = dm_target_offset(ti, bio->bi_iter.bi_sector); 3238c2ecf20Sopenharmony_ci unsigned path_nr = switch_get_path_nr(sctx, offset); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci bio_set_dev(bio, sctx->path_list[path_nr].dmdev->bdev); 3268c2ecf20Sopenharmony_ci bio->bi_iter.bi_sector = sctx->path_list[path_nr].start + offset; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return DM_MAPIO_REMAPPED; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* 3328c2ecf20Sopenharmony_ci * We need to parse hex numbers in the message as quickly as possible. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * This table-based hex parser improves performance. 3358c2ecf20Sopenharmony_ci * It improves a time to load 1000000 entries compared to the condition-based 3368c2ecf20Sopenharmony_ci * parser. 3378c2ecf20Sopenharmony_ci * table-based parser condition-based parser 3388c2ecf20Sopenharmony_ci * PA-RISC 0.29s 0.31s 3398c2ecf20Sopenharmony_ci * Opteron 0.0495s 0.0498s 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_cistatic const unsigned char hex_table[256] = { 3428c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3438c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3448c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3458c2ecf20Sopenharmony_ci0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 3468c2ecf20Sopenharmony_ci255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3478c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3488c2ecf20Sopenharmony_ci255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3498c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3508c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3518c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3528c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3538c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3548c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3558c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3568c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3578c2ecf20Sopenharmony_ci255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic __always_inline unsigned long parse_hex(const char **string) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci unsigned char d; 3638c2ecf20Sopenharmony_ci unsigned long r = 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci while ((d = hex_table[(unsigned char)**string]) < 16) { 3668c2ecf20Sopenharmony_ci r = (r << 4) | d; 3678c2ecf20Sopenharmony_ci (*string)++; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return r; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int process_set_region_mappings(struct switch_ctx *sctx, 3748c2ecf20Sopenharmony_ci unsigned argc, char **argv) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci unsigned i; 3778c2ecf20Sopenharmony_ci unsigned long region_index = 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci for (i = 1; i < argc; i++) { 3808c2ecf20Sopenharmony_ci unsigned long path_nr; 3818c2ecf20Sopenharmony_ci const char *string = argv[i]; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if ((*string & 0xdf) == 'R') { 3848c2ecf20Sopenharmony_ci unsigned long cycle_length, num_write; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci string++; 3878c2ecf20Sopenharmony_ci if (unlikely(*string == ',')) { 3888c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 3898c2ecf20Sopenharmony_ci return -EINVAL; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci cycle_length = parse_hex(&string); 3928c2ecf20Sopenharmony_ci if (unlikely(*string != ',')) { 3938c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci string++; 3978c2ecf20Sopenharmony_ci if (unlikely(!*string)) { 3988c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci num_write = parse_hex(&string); 4028c2ecf20Sopenharmony_ci if (unlikely(*string)) { 4038c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 4048c2ecf20Sopenharmony_ci return -EINVAL; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (unlikely(!cycle_length) || unlikely(cycle_length - 1 > region_index)) { 4088c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings cycle length: %lu > %lu", 4098c2ecf20Sopenharmony_ci cycle_length - 1, region_index); 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci if (unlikely(region_index + num_write < region_index) || 4138c2ecf20Sopenharmony_ci unlikely(region_index + num_write >= sctx->nr_regions)) { 4148c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings region number: %lu + %lu >= %lu", 4158c2ecf20Sopenharmony_ci region_index, num_write, sctx->nr_regions); 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci while (num_write--) { 4208c2ecf20Sopenharmony_ci region_index++; 4218c2ecf20Sopenharmony_ci path_nr = switch_region_table_read(sctx, region_index - cycle_length); 4228c2ecf20Sopenharmony_ci switch_region_table_write(sctx, region_index, path_nr); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci continue; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (*string == ':') 4298c2ecf20Sopenharmony_ci region_index++; 4308c2ecf20Sopenharmony_ci else { 4318c2ecf20Sopenharmony_ci region_index = parse_hex(&string); 4328c2ecf20Sopenharmony_ci if (unlikely(*string != ':')) { 4338c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci string++; 4398c2ecf20Sopenharmony_ci if (unlikely(!*string)) { 4408c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 4418c2ecf20Sopenharmony_ci return -EINVAL; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci path_nr = parse_hex(&string); 4458c2ecf20Sopenharmony_ci if (unlikely(*string)) { 4468c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci if (unlikely(region_index >= sctx->nr_regions)) { 4508c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings region number: %lu >= %lu", region_index, sctx->nr_regions); 4518c2ecf20Sopenharmony_ci return -EINVAL; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci if (unlikely(path_nr >= sctx->nr_paths)) { 4548c2ecf20Sopenharmony_ci DMWARN("invalid set_region_mappings device: %lu >= %u", path_nr, sctx->nr_paths); 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci switch_region_table_write(sctx, region_index, path_nr); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci/* 4658c2ecf20Sopenharmony_ci * Messages are processed one-at-a-time. 4668c2ecf20Sopenharmony_ci * 4678c2ecf20Sopenharmony_ci * Only set_region_mappings is supported. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_cistatic int switch_message(struct dm_target *ti, unsigned argc, char **argv, 4708c2ecf20Sopenharmony_ci char *result, unsigned maxlen) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci static DEFINE_MUTEX(message_mutex); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 4758c2ecf20Sopenharmony_ci int r = -EINVAL; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mutex_lock(&message_mutex); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!strcasecmp(argv[0], "set_region_mappings")) 4808c2ecf20Sopenharmony_ci r = process_set_region_mappings(sctx, argc, argv); 4818c2ecf20Sopenharmony_ci else 4828c2ecf20Sopenharmony_ci DMWARN("Unrecognised message received."); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci mutex_unlock(&message_mutex); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return r; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void switch_status(struct dm_target *ti, status_type_t type, 4908c2ecf20Sopenharmony_ci unsigned status_flags, char *result, unsigned maxlen) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 4938c2ecf20Sopenharmony_ci unsigned sz = 0; 4948c2ecf20Sopenharmony_ci int path_nr; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci switch (type) { 4978c2ecf20Sopenharmony_ci case STATUSTYPE_INFO: 4988c2ecf20Sopenharmony_ci result[0] = '\0'; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci case STATUSTYPE_TABLE: 5028c2ecf20Sopenharmony_ci DMEMIT("%u %u 0", sctx->nr_paths, sctx->region_size); 5038c2ecf20Sopenharmony_ci for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++) 5048c2ecf20Sopenharmony_ci DMEMIT(" %s %llu", sctx->path_list[path_nr].dmdev->name, 5058c2ecf20Sopenharmony_ci (unsigned long long)sctx->path_list[path_nr].start); 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* 5118c2ecf20Sopenharmony_ci * Switch ioctl: 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * Passthrough all ioctls to the path for sector 0 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_cistatic int switch_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 5188c2ecf20Sopenharmony_ci unsigned path_nr; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci path_nr = switch_get_path_nr(sctx, 0); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci *bdev = sctx->path_list[path_nr].dmdev->bdev; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* 5258c2ecf20Sopenharmony_ci * Only pass ioctls through if the device sizes match exactly. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci if (ti->len + sctx->path_list[path_nr].start != 5288c2ecf20Sopenharmony_ci i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT) 5298c2ecf20Sopenharmony_ci return 1; 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int switch_iterate_devices(struct dm_target *ti, 5348c2ecf20Sopenharmony_ci iterate_devices_callout_fn fn, void *data) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct switch_ctx *sctx = ti->private; 5378c2ecf20Sopenharmony_ci int path_nr; 5388c2ecf20Sopenharmony_ci int r; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++) { 5418c2ecf20Sopenharmony_ci r = fn(ti, sctx->path_list[path_nr].dmdev, 5428c2ecf20Sopenharmony_ci sctx->path_list[path_nr].start, ti->len, data); 5438c2ecf20Sopenharmony_ci if (r) 5448c2ecf20Sopenharmony_ci return r; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic struct target_type switch_target = { 5518c2ecf20Sopenharmony_ci .name = "switch", 5528c2ecf20Sopenharmony_ci .version = {1, 1, 0}, 5538c2ecf20Sopenharmony_ci .module = THIS_MODULE, 5548c2ecf20Sopenharmony_ci .ctr = switch_ctr, 5558c2ecf20Sopenharmony_ci .dtr = switch_dtr, 5568c2ecf20Sopenharmony_ci .map = switch_map, 5578c2ecf20Sopenharmony_ci .message = switch_message, 5588c2ecf20Sopenharmony_ci .status = switch_status, 5598c2ecf20Sopenharmony_ci .prepare_ioctl = switch_prepare_ioctl, 5608c2ecf20Sopenharmony_ci .iterate_devices = switch_iterate_devices, 5618c2ecf20Sopenharmony_ci}; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int __init dm_switch_init(void) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci int r; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci r = dm_register_target(&switch_target); 5688c2ecf20Sopenharmony_ci if (r < 0) 5698c2ecf20Sopenharmony_ci DMERR("dm_register_target() failed %d", r); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return r; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void __exit dm_switch_exit(void) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci dm_unregister_target(&switch_target); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cimodule_init(dm_switch_init); 5808c2ecf20Sopenharmony_cimodule_exit(dm_switch_exit); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DM_NAME " dynamic path switching target"); 5838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin D. O'Kelley <Kevin_OKelley@dell.com>"); 5848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Narendran Ganapathy <Narendran_Ganapathy@dell.com>"); 5858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jim Ramsay <Jim_Ramsay@dell.com>"); 5868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mikulas Patocka <mpatocka@redhat.com>"); 5878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 588