123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2020 Oracle Corporation
- *
- * Module Author: Mike Christie
- */
- #include "dm-path-selector.h"
- #include <linux/device-mapper.h>
- #include <linux/module.h>
- #define DM_MSG_PREFIX "multipath io-affinity"
- struct path_info {
- struct dm_path *path;
- cpumask_var_t cpumask;
- refcount_t refcount;
- bool failed;
- };
- struct selector {
- struct path_info **path_map;
- cpumask_var_t path_mask;
- atomic_t map_misses;
- };
- static void ioa_free_path(struct selector *s, unsigned int cpu)
- {
- struct path_info *pi = s->path_map[cpu];
- if (!pi)
- return;
- if (refcount_dec_and_test(&pi->refcount)) {
- cpumask_clear_cpu(cpu, s->path_mask);
- free_cpumask_var(pi->cpumask);
- kfree(pi);
- s->path_map[cpu] = NULL;
- }
- }
- static int ioa_add_path(struct path_selector *ps, struct dm_path *path,
- int argc, char **argv, char **error)
- {
- struct selector *s = ps->context;
- struct path_info *pi = NULL;
- unsigned int cpu;
- int ret;
- if (argc != 1) {
- *error = "io-affinity ps: invalid number of arguments";
- return -EINVAL;
- }
- pi = kzalloc(sizeof(*pi), GFP_KERNEL);
- if (!pi) {
- *error = "io-affinity ps: Error allocating path context";
- return -ENOMEM;
- }
- pi->path = path;
- path->pscontext = pi;
- refcount_set(&pi->refcount, 1);
- if (!zalloc_cpumask_var(&pi->cpumask, GFP_KERNEL)) {
- *error = "io-affinity ps: Error allocating cpumask context";
- ret = -ENOMEM;
- goto free_pi;
- }
- ret = cpumask_parse(argv[0], pi->cpumask);
- if (ret) {
- *error = "io-affinity ps: invalid cpumask";
- ret = -EINVAL;
- goto free_mask;
- }
- for_each_cpu(cpu, pi->cpumask) {
- if (cpu >= nr_cpu_ids) {
- DMWARN_LIMIT("Ignoring mapping for CPU %u. Max CPU is %u",
- cpu, nr_cpu_ids);
- break;
- }
- if (s->path_map[cpu]) {
- DMWARN("CPU mapping for %u exists. Ignoring.", cpu);
- continue;
- }
- cpumask_set_cpu(cpu, s->path_mask);
- s->path_map[cpu] = pi;
- refcount_inc(&pi->refcount);
- }
- if (refcount_dec_and_test(&pi->refcount)) {
- *error = "io-affinity ps: No new/valid CPU mapping found";
- ret = -EINVAL;
- goto free_mask;
- }
- return 0;
- free_mask:
- free_cpumask_var(pi->cpumask);
- free_pi:
- kfree(pi);
- return ret;
- }
- static int ioa_create(struct path_selector *ps, unsigned int argc, char **argv)
- {
- struct selector *s;
- s = kmalloc(sizeof(*s), GFP_KERNEL);
- if (!s)
- return -ENOMEM;
- s->path_map = kzalloc(nr_cpu_ids * sizeof(struct path_info *),
- GFP_KERNEL);
- if (!s->path_map)
- goto free_selector;
- if (!zalloc_cpumask_var(&s->path_mask, GFP_KERNEL))
- goto free_map;
- atomic_set(&s->map_misses, 0);
- ps->context = s;
- return 0;
- free_map:
- kfree(s->path_map);
- free_selector:
- kfree(s);
- return -ENOMEM;
- }
- static void ioa_destroy(struct path_selector *ps)
- {
- struct selector *s = ps->context;
- unsigned int cpu;
- for_each_cpu(cpu, s->path_mask)
- ioa_free_path(s, cpu);
- free_cpumask_var(s->path_mask);
- kfree(s->path_map);
- kfree(s);
- ps->context = NULL;
- }
- static int ioa_status(struct path_selector *ps, struct dm_path *path,
- status_type_t type, char *result, unsigned int maxlen)
- {
- struct selector *s = ps->context;
- struct path_info *pi;
- int sz = 0;
- if (!path) {
- DMEMIT("0 ");
- return sz;
- }
- switch(type) {
- case STATUSTYPE_INFO:
- DMEMIT("%d ", atomic_read(&s->map_misses));
- break;
- case STATUSTYPE_TABLE:
- pi = path->pscontext;
- DMEMIT("%*pb ", cpumask_pr_args(pi->cpumask));
- break;
- case STATUSTYPE_IMA:
- *result = '\0';
- break;
- }
- return sz;
- }
- static void ioa_fail_path(struct path_selector *ps, struct dm_path *p)
- {
- struct path_info *pi = p->pscontext;
- pi->failed = true;
- }
- static int ioa_reinstate_path(struct path_selector *ps, struct dm_path *p)
- {
- struct path_info *pi = p->pscontext;
- pi->failed = false;
- return 0;
- }
- static struct dm_path *ioa_select_path(struct path_selector *ps,
- size_t nr_bytes)
- {
- unsigned int cpu, node;
- struct selector *s = ps->context;
- const struct cpumask *cpumask;
- struct path_info *pi;
- int i;
- cpu = get_cpu();
- pi = s->path_map[cpu];
- if (pi && !pi->failed)
- goto done;
- /*
- * Perf is not optimal, but we at least try the local node then just
- * try not to fail.
- */
- if (!pi)
- atomic_inc(&s->map_misses);
- node = cpu_to_node(cpu);
- cpumask = cpumask_of_node(node);
- for_each_cpu(i, cpumask) {
- pi = s->path_map[i];
- if (pi && !pi->failed)
- goto done;
- }
- for_each_cpu(i, s->path_mask) {
- pi = s->path_map[i];
- if (pi && !pi->failed)
- goto done;
- }
- pi = NULL;
- done:
- put_cpu();
- return pi ? pi->path : NULL;
- }
- static struct path_selector_type ioa_ps = {
- .name = "io-affinity",
- .module = THIS_MODULE,
- .table_args = 1,
- .info_args = 1,
- .create = ioa_create,
- .destroy = ioa_destroy,
- .status = ioa_status,
- .add_path = ioa_add_path,
- .fail_path = ioa_fail_path,
- .reinstate_path = ioa_reinstate_path,
- .select_path = ioa_select_path,
- };
- static int __init dm_ioa_init(void)
- {
- int ret = dm_register_path_selector(&ioa_ps);
- if (ret < 0)
- DMERR("register failed %d", ret);
- return ret;
- }
- static void __exit dm_ioa_exit(void)
- {
- int ret = dm_unregister_path_selector(&ioa_ps);
- if (ret < 0)
- DMERR("unregister failed %d", ret);
- }
- module_init(dm_ioa_init);
- module_exit(dm_ioa_exit);
- MODULE_DESCRIPTION(DM_NAME " multipath path selector that selects paths based on the CPU IO is being executed on");
- MODULE_AUTHOR("Mike Christie <[email protected]>");
- MODULE_LICENSE("GPL");
|