cregit-Linux how code gets into the kernel

Release 4.7 drivers/md/dm-switch.c

Directory: drivers/md
/*
 * Copyright (C) 2010-2012 by Dell Inc.  All rights reserved.
 * Copyright (C) 2011-2013 Red Hat, Inc.
 *
 * This file is released under the GPL.
 *
 * dm-switch is a device-mapper target that maps IO to underlying block
 * devices efficiently when there are a large number of fixed-sized
 * address regions but there is no simple pattern to allow for a compact
 * mapping representation such as dm-stripe.
 */

#include <linux/device-mapper.h>

#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>


#define DM_MSG_PREFIX "switch"

/*
 * One region_table_slot_t holds <region_entries_per_slot> region table
 * entries each of which is <region_table_entry_bits> in size.
 */

typedef unsigned long region_table_slot_t;

/*
 * A device with the offset to its start sector.
 */

struct switch_path {
	
struct dm_dev *dmdev;
	
sector_t start;
};

/*
 * Context block for a dm switch device.
 */

struct switch_ctx {
	
struct dm_target *ti;

	
unsigned nr_paths;		/* Number of paths in path_list. */

	
unsigned region_size;		/* Region size in 512-byte sectors */
	
unsigned long nr_regions;	/* Number of regions making up the device */
	
signed char region_size_bits;	/* log2 of region_size or -1 */

	
unsigned char region_table_entry_bits;	/* Number of bits in one region table entry */
	
unsigned char region_entries_per_slot;	/* Number of entries in one region table slot */
	
signed char region_entries_per_slot_bits;	/* log2 of region_entries_per_slot or -1 */

	
region_table_slot_t *region_table;	/* Region table */

	/*
         * Array of dm devices to switch between.
         */
	
struct switch_path path_list[0];
};


static struct switch_ctx *alloc_switch_ctx(struct dm_target *ti, unsigned nr_paths, unsigned region_size) { struct switch_ctx *sctx; sctx = kzalloc(sizeof(struct switch_ctx) + nr_paths * sizeof(struct switch_path), GFP_KERNEL); if (!sctx) return NULL; sctx->ti = ti; sctx->region_size = region_size; ti->private = sctx; return sctx; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay74100.00%1100.00%
Total74100.00%1100.00%


static int alloc_region_table(struct dm_target *ti, unsigned nr_paths) { struct switch_ctx *sctx = ti->private; sector_t nr_regions = ti->len; sector_t nr_slots; if (!(sctx->region_size & (sctx->region_size - 1))) sctx->region_size_bits = __ffs(sctx->region_size); else sctx->region_size_bits = -1; sctx->region_table_entry_bits = 1; while (sctx->region_table_entry_bits < sizeof(region_table_slot_t) * 8 && (region_table_slot_t)1 << sctx->region_table_entry_bits < nr_paths) sctx->region_table_entry_bits++; sctx->region_entries_per_slot = (sizeof(region_table_slot_t) * 8) / sctx->region_table_entry_bits; if (!(sctx->region_entries_per_slot & (sctx->region_entries_per_slot - 1))) sctx->region_entries_per_slot_bits = __ffs(sctx->region_entries_per_slot); else sctx->region_entries_per_slot_bits = -1; if (sector_div(nr_regions, sctx->region_size)) nr_regions++; if (nr_regions >= ULONG_MAX) { ti->error = "Region table too large"; return -EINVAL; } sctx->nr_regions = nr_regions; nr_slots = nr_regions; if (sector_div(nr_slots, sctx->region_entries_per_slot)) nr_slots++; if (nr_slots > ULONG_MAX / sizeof(region_table_slot_t)) { ti->error = "Region table too large"; return -EINVAL; } sctx->region_table = vmalloc(nr_slots * sizeof(region_table_slot_t)); if (!sctx->region_table) { ti->error = "Cannot allocate region table"; return -ENOMEM; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay26697.79%150.00%
tomohiro kusumitomohiro kusumi62.21%150.00%
Total272100.00%2100.00%


static void switch_get_position(struct switch_ctx *sctx, unsigned long region_nr, unsigned long *region_index, unsigned *bit) { if (sctx->region_entries_per_slot_bits >= 0) { *region_index = region_nr >> sctx->region_entries_per_slot_bits; *bit = region_nr & (sctx->region_entries_per_slot - 1); } else { *region_index = region_nr / sctx->region_entries_per_slot; *bit = region_nr % sctx->region_entries_per_slot; } *bit *= sctx->region_table_entry_bits; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay84100.00%1100.00%
Total84100.00%1100.00%


static unsigned switch_region_table_read(struct switch_ctx *sctx, unsigned long region_nr) { unsigned long region_index; unsigned bit; switch_get_position(sctx, region_nr, &region_index, &bit); return (ACCESS_ONCE(sctx->region_table[region_index]) >> bit) & ((1 << sctx->region_table_entry_bits) - 1); }

Contributors

PersonTokensPropCommitsCommitProp
mikulas patockamikulas patocka62100.00%1100.00%
Total62100.00%1100.00%

/* * Find which path to use at given offset. */
static unsigned switch_get_path_nr(struct switch_ctx *sctx, sector_t offset) { unsigned path_nr; sector_t p; p = offset; if (sctx->region_size_bits >= 0) p >>= sctx->region_size_bits; else sector_div(p, sctx->region_size); path_nr = switch_region_table_read(sctx, p); /* This can only happen if the processor uses non-atomic stores. */ if (unlikely(path_nr >= sctx->nr_paths)) path_nr = 0; return path_nr; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay7396.05%150.00%
mikulas patockamikulas patocka33.95%150.00%
Total76100.00%2100.00%


static void switch_region_table_write(struct switch_ctx *sctx, unsigned long region_nr, unsigned value) { unsigned long region_index; unsigned bit; region_table_slot_t pte; switch_get_position(sctx, region_nr, &region_index, &bit); pte = sctx->region_table[region_index]; pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit); pte |= (region_table_slot_t)value << bit; sctx->region_table[region_index] = pte; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay90100.00%1100.00%
Total90100.00%1100.00%

/* * Fill the region table with an initial round robin pattern. */
static void initialise_region_table(struct switch_ctx *sctx) { unsigned path_nr = 0; unsigned long region_nr; for (region_nr = 0; region_nr < sctx->nr_regions; region_nr++) { switch_region_table_write(sctx, region_nr, path_nr); if (++path_nr >= sctx->nr_paths) path_nr = 0; } }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay59100.00%1100.00%
Total59100.00%1100.00%


static int parse_path(struct dm_arg_set *as, struct dm_target *ti) { struct switch_ctx *sctx = ti->private; unsigned long long start; int r; r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table), &sctx->path_list[sctx->nr_paths].dmdev); if (r) { ti->error = "Device lookup failed"; return r; } if (kstrtoull(dm_shift_arg(as), 10, &start) || start != (sector_t)start) { ti->error = "Invalid device starting offset"; dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev); return -EINVAL; } sctx->path_list[sctx->nr_paths].start = start; sctx->nr_paths++; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay150100.00%1100.00%
Total150100.00%1100.00%

/* * Destructor: Don't free the dm_target, just the ti->private data (if any). */
static void switch_dtr(struct dm_target *ti) { struct switch_ctx *sctx = ti->private; while (sctx->nr_paths--) dm_put_device(ti, sctx->path_list[sctx->nr_paths].dmdev); vfree(sctx->region_table); kfree(sctx); }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay55100.00%1100.00%
Total55100.00%1100.00%

/* * Constructor arguments: * <num_paths> <region_size> <num_optional_args> [<optional_args>...] * [<dev_path> <offset>]+ * * Optional args are to allow for future extension: currently this * parameter must be 0. */
static int switch_ctr(struct dm_target *ti, unsigned argc, char **argv) { static struct dm_arg _args[] = { {1, (KMALLOC_MAX_SIZE - sizeof(struct switch_ctx)) / sizeof(struct switch_path), "Invalid number of paths"}, {1, UINT_MAX, "Invalid region size"}, {0, 0, "Invalid number of optional args"}, }; struct switch_ctx *sctx; struct dm_arg_set as; unsigned nr_paths, region_size, nr_optional_args; int r; as.argc = argc; as.argv = argv; r = dm_read_arg(_args, &as, &nr_paths, &ti->error); if (r) return -EINVAL; r = dm_read_arg(_args + 1, &as, &region_size, &ti->error); if (r) return r; r = dm_read_arg_group(_args + 2, &as, &nr_optional_args, &ti->error); if (r) return r; /* parse optional arguments here, if we add any */ if (as.argc != nr_paths * 2) { ti->error = "Incorrect number of path arguments"; return -EINVAL; } sctx = alloc_switch_ctx(ti, nr_paths, region_size); if (!sctx) { ti->error = "Cannot allocate redirection context"; return -ENOMEM; } r = dm_set_target_max_io_len(ti, region_size); if (r) goto error; while (as.argc) { r = parse_path(&as, ti); if (r) goto error; } r = alloc_region_table(ti, nr_paths); if (r) goto error; initialise_region_table(sctx); /* For UNMAP, sending the request down any path is sufficient */ ti->num_discard_bios = 1; return 0; error: switch_dtr(ti); return r; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay309100.00%1100.00%
Total309100.00%1100.00%


static int switch_map(struct dm_target *ti, struct bio *bio) { struct switch_ctx *sctx = ti->private; sector_t offset = dm_target_offset(ti, bio->bi_iter.bi_sector); unsigned path_nr = switch_get_path_nr(sctx, offset); bio->bi_bdev = sctx->path_list[path_nr].dmdev->bdev; bio->bi_iter.bi_sector = sctx->path_list[path_nr].start + offset; return DM_MAPIO_REMAPPED; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay8095.24%150.00%
kent overstreetkent overstreet44.76%150.00%
Total84100.00%2100.00%

/* * We need to parse hex numbers in the message as quickly as possible. * * This table-based hex parser improves performance. * It improves a time to load 1000000 entries compared to the condition-based * parser. * table-based parser condition-based parser * PA-RISC 0.29s 0.31s * Opteron 0.0495s 0.0498s */ static const unsigned char hex_table[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
static __always_inline unsigned long parse_hex(const char **string) { unsigned char d; unsigned long r = 0; while ((d = hex_table[(unsigned char)**string]) < 16) { r = (r << 4) | d; (*string)++; } return r; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay64100.00%1100.00%
Total64100.00%1100.00%


static int process_set_region_mappings(struct switch_ctx *sctx, unsigned argc, char **argv) { unsigned i; unsigned long region_index = 0; for (i = 1; i < argc; i++) { unsigned long path_nr; const char *string = argv[i]; if ((*string & 0xdf) == 'R') { unsigned long cycle_length, num_write; string++; if (unlikely(*string == ',')) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } cycle_length = parse_hex(&string); if (unlikely(*string != ',')) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } string++; if (unlikely(!*string)) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } num_write = parse_hex(&string); if (unlikely(*string)) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } if (unlikely(!cycle_length) || unlikely(cycle_length - 1 > region_index)) { DMWARN("invalid set_region_mappings cycle length: %lu > %lu", cycle_length - 1, region_index); return -EINVAL; } if (unlikely(region_index + num_write < region_index) || unlikely(region_index + num_write >= sctx->nr_regions)) { DMWARN("invalid set_region_mappings region number: %lu + %lu >= %lu", region_index, num_write, sctx->nr_regions); return -EINVAL; } while (num_write--) { region_index++; path_nr = switch_region_table_read(sctx, region_index - cycle_length); switch_region_table_write(sctx, region_index, path_nr); } continue; } if (*string == ':') region_index++; else { region_index = parse_hex(&string); if (unlikely(*string != ':')) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } } string++; if (unlikely(!*string)) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } path_nr = parse_hex(&string); if (unlikely(*string)) { DMWARN("invalid set_region_mappings argument: '%s'", argv[i]); return -EINVAL; } if (unlikely(region_index >= sctx->nr_regions)) { DMWARN("invalid set_region_mappings region number: %lu >= %lu", region_index, sctx->nr_regions); return -EINVAL; } if (unlikely(path_nr >= sctx->nr_paths)) { DMWARN("invalid set_region_mappings device: %lu >= %u", path_nr, sctx->nr_paths); return -EINVAL; } switch_region_table_write(sctx, region_index, path_nr); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mikulas patockamikulas patocka24851.67%150.00%
jim ramsayjim ramsay23248.33%150.00%
Total480100.00%2100.00%

/* * Messages are processed one-at-a-time. * * Only set_region_mappings is supported. */
static int switch_message(struct dm_target *ti, unsigned argc, char **argv) { static DEFINE_MUTEX(message_mutex); struct switch_ctx *sctx = ti->private; int r = -EINVAL; mutex_lock(&message_mutex); if (!strcasecmp(argv[0], "set_region_mappings")) r = process_set_region_mappings(sctx, argc, argv); else DMWARN("Unrecognised message received."); mutex_unlock(&message_mutex); return r; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay85100.00%1100.00%
Total85100.00%1100.00%


static void switch_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) { struct switch_ctx *sctx = ti->private; unsigned sz = 0; int path_nr; switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; case STATUSTYPE_TABLE: DMEMIT("%u %u 0", sctx->nr_paths, sctx->region_size); for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++) DMEMIT(" %s %llu", sctx->path_list[path_nr].dmdev->name, (unsigned long long)sctx->path_list[path_nr].start); break; } }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay120100.00%1100.00%
Total120100.00%1100.00%

/* * Switch ioctl: * * Passthrough all ioctls to the path for sector 0 */
static int switch_prepare_ioctl(struct dm_target *ti, struct block_device **bdev, fmode_t *mode) { struct switch_ctx *sctx = ti->private; unsigned path_nr; path_nr = switch_get_path_nr(sctx, 0); *bdev = sctx->path_list[path_nr].dmdev->bdev; *mode = sctx->path_list[path_nr].dmdev->mode; /* * Only pass ioctls through if the device sizes match exactly. */ if (ti->len + sctx->path_list[path_nr].start != i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay8682.69%150.00%
christoph hellwigchristoph hellwig1817.31%150.00%
Total104100.00%2100.00%


static int switch_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { struct switch_ctx *sctx = ti->private; int path_nr; int r; for (path_nr = 0; path_nr < sctx->nr_paths; path_nr++) { r = fn(ti, sctx->path_list[path_nr].dmdev, sctx->path_list[path_nr].start, ti->len, data); if (r) return r; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay91100.00%1100.00%
Total91100.00%1100.00%

static struct target_type switch_target = { .name = "switch", .version = {1, 1, 0}, .module = THIS_MODULE, .ctr = switch_ctr, .dtr = switch_dtr, .map = switch_map, .message = switch_message, .status = switch_status, .prepare_ioctl = switch_prepare_ioctl, .iterate_devices = switch_iterate_devices, };
static int __init dm_switch_init(void) { int r; r = dm_register_target(&switch_target); if (r < 0) DMERR("dm_register_target() failed %d", r); return r; }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay36100.00%1100.00%
Total36100.00%1100.00%


static void __exit dm_switch_exit(void) { dm_unregister_target(&switch_target); }

Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay15100.00%1100.00%
Total15100.00%1100.00%

module_init(dm_switch_init); module_exit(dm_switch_exit); MODULE_DESCRIPTION(DM_NAME " dynamic path switching target"); MODULE_AUTHOR("Kevin D. O'Kelley <Kevin_OKelley@dell.com>"); MODULE_AUTHOR("Narendran Ganapathy <Narendran_Ganapathy@dell.com>"); MODULE_AUTHOR("Jim Ramsay <Jim_Ramsay@dell.com>"); MODULE_AUTHOR("Mikulas Patocka <mpatocka@redhat.com>"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
jim ramsayjim ramsay269488.68%116.67%
mikulas patockamikulas patocka31410.34%233.33%
christoph hellwigchristoph hellwig200.66%116.67%
tomohiro kusumitomohiro kusumi60.20%116.67%
kent overstreetkent overstreet40.13%116.67%
Total3038100.00%6100.00%
Directory: drivers/md
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}