1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2020 Oracle Corporation
5 * Module Author: Mike Christie
7 #include "dm-path-selector.h"
9 #include <linux/device-mapper.h>
10 #include <linux/module.h>
12 #define DM_MSG_PREFIX "multipath io-affinity"
16 cpumask_var_t cpumask;
22 struct path_info **path_map;
23 cpumask_var_t path_mask;
27 static void ioa_free_path(struct selector *s, unsigned int cpu)
29 struct path_info *pi = s->path_map[cpu];
34 if (refcount_dec_and_test(&pi->refcount)) {
35 cpumask_clear_cpu(cpu, s->path_mask);
36 free_cpumask_var(pi->cpumask);
39 s->path_map[cpu] = NULL;
43 static int ioa_add_path(struct path_selector *ps, struct dm_path *path,
44 int argc, char **argv, char **error)
46 struct selector *s = ps->context;
47 struct path_info *pi = NULL;
52 *error = "io-affinity ps: invalid number of arguments";
56 pi = kzalloc(sizeof(*pi), GFP_KERNEL);
58 *error = "io-affinity ps: Error allocating path context";
64 refcount_set(&pi->refcount, 1);
66 if (!zalloc_cpumask_var(&pi->cpumask, GFP_KERNEL)) {
67 *error = "io-affinity ps: Error allocating cpumask context";
72 ret = cpumask_parse(argv[0], pi->cpumask);
74 *error = "io-affinity ps: invalid cpumask";
79 for_each_cpu(cpu, pi->cpumask) {
80 if (cpu >= nr_cpu_ids) {
81 DMWARN_LIMIT("Ignoring mapping for CPU %u. Max CPU is %u",
86 if (s->path_map[cpu]) {
87 DMWARN("CPU mapping for %u exists. Ignoring.", cpu);
91 cpumask_set_cpu(cpu, s->path_mask);
92 s->path_map[cpu] = pi;
93 refcount_inc(&pi->refcount);
96 if (refcount_dec_and_test(&pi->refcount)) {
97 *error = "io-affinity ps: No new/valid CPU mapping found";
105 free_cpumask_var(pi->cpumask);
111 static int ioa_create(struct path_selector *ps, unsigned argc, char **argv)
115 s = kmalloc(sizeof(*s), GFP_KERNEL);
119 s->path_map = kzalloc(nr_cpu_ids * sizeof(struct path_info *),
124 if (!zalloc_cpumask_var(&s->path_mask, GFP_KERNEL))
127 atomic_set(&s->map_misses, 0);
138 static void ioa_destroy(struct path_selector *ps)
140 struct selector *s = ps->context;
143 for_each_cpu(cpu, s->path_mask)
144 ioa_free_path(s, cpu);
146 free_cpumask_var(s->path_mask);
153 static int ioa_status(struct path_selector *ps, struct dm_path *path,
154 status_type_t type, char *result, unsigned int maxlen)
156 struct selector *s = ps->context;
157 struct path_info *pi;
166 case STATUSTYPE_INFO:
167 DMEMIT("%d ", atomic_read(&s->map_misses));
169 case STATUSTYPE_TABLE:
170 pi = path->pscontext;
171 DMEMIT("%*pb ", cpumask_pr_args(pi->cpumask));
181 static void ioa_fail_path(struct path_selector *ps, struct dm_path *p)
183 struct path_info *pi = p->pscontext;
188 static int ioa_reinstate_path(struct path_selector *ps, struct dm_path *p)
190 struct path_info *pi = p->pscontext;
196 static struct dm_path *ioa_select_path(struct path_selector *ps,
199 unsigned int cpu, node;
200 struct selector *s = ps->context;
201 const struct cpumask *cpumask;
202 struct path_info *pi;
207 pi = s->path_map[cpu];
208 if (pi && !pi->failed)
212 * Perf is not optimal, but we at least try the local node then just
216 atomic_inc(&s->map_misses);
218 node = cpu_to_node(cpu);
219 cpumask = cpumask_of_node(node);
220 for_each_cpu(i, cpumask) {
222 if (pi && !pi->failed)
226 for_each_cpu(i, s->path_mask) {
228 if (pi && !pi->failed)
235 return pi ? pi->path : NULL;
238 static struct path_selector_type ioa_ps = {
239 .name = "io-affinity",
240 .module = THIS_MODULE,
243 .create = ioa_create,
244 .destroy = ioa_destroy,
245 .status = ioa_status,
246 .add_path = ioa_add_path,
247 .fail_path = ioa_fail_path,
248 .reinstate_path = ioa_reinstate_path,
249 .select_path = ioa_select_path,
252 static int __init dm_ioa_init(void)
254 int ret = dm_register_path_selector(&ioa_ps);
257 DMERR("register failed %d", ret);
261 static void __exit dm_ioa_exit(void)
263 int ret = dm_unregister_path_selector(&ioa_ps);
266 DMERR("unregister failed %d", ret);
269 module_init(dm_ioa_init);
270 module_exit(dm_ioa_exit);
272 MODULE_DESCRIPTION(DM_NAME " multipath path selector that selects paths based on the CPU IO is being executed on");
273 MODULE_AUTHOR("Mike Christie <michael.christie@oracle.com>");
274 MODULE_LICENSE("GPL");