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
);
97 if (refcount_dec_and_test(&pi
->refcount
)) {
98 *error
= "io-affinity ps: No new/valid CPU mapping found";
106 free_cpumask_var(pi
->cpumask
);
112 static int ioa_create(struct path_selector
*ps
, unsigned argc
, char **argv
)
116 s
= kmalloc(sizeof(*s
), GFP_KERNEL
);
120 s
->path_map
= kzalloc(nr_cpu_ids
* sizeof(struct path_info
*),
125 if (!zalloc_cpumask_var(&s
->path_mask
, GFP_KERNEL
))
128 atomic_set(&s
->map_misses
, 0);
139 static void ioa_destroy(struct path_selector
*ps
)
141 struct selector
*s
= ps
->context
;
144 for_each_cpu(cpu
, s
->path_mask
)
145 ioa_free_path(s
, cpu
);
147 free_cpumask_var(s
->path_mask
);
154 static int ioa_status(struct path_selector
*ps
, struct dm_path
*path
,
155 status_type_t type
, char *result
, unsigned int maxlen
)
157 struct selector
*s
= ps
->context
;
158 struct path_info
*pi
;
167 case STATUSTYPE_INFO
:
168 DMEMIT("%d ", atomic_read(&s
->map_misses
));
170 case STATUSTYPE_TABLE
:
171 pi
= path
->pscontext
;
172 DMEMIT("%*pb ", cpumask_pr_args(pi
->cpumask
));
179 static void ioa_fail_path(struct path_selector
*ps
, struct dm_path
*p
)
181 struct path_info
*pi
= p
->pscontext
;
186 static int ioa_reinstate_path(struct path_selector
*ps
, struct dm_path
*p
)
188 struct path_info
*pi
= p
->pscontext
;
194 static struct dm_path
*ioa_select_path(struct path_selector
*ps
,
197 unsigned int cpu
, node
;
198 struct selector
*s
= ps
->context
;
199 const struct cpumask
*cpumask
;
200 struct path_info
*pi
;
205 pi
= s
->path_map
[cpu
];
206 if (pi
&& !pi
->failed
)
210 * Perf is not optimal, but we at least try the local node then just
214 atomic_inc(&s
->map_misses
);
216 node
= cpu_to_node(cpu
);
217 cpumask
= cpumask_of_node(node
);
218 for_each_cpu(i
, cpumask
) {
220 if (pi
&& !pi
->failed
)
224 for_each_cpu(i
, s
->path_mask
) {
226 if (pi
&& !pi
->failed
)
233 return pi
? pi
->path
: NULL
;
236 static struct path_selector_type ioa_ps
= {
237 .name
= "io-affinity",
238 .module
= THIS_MODULE
,
241 .create
= ioa_create
,
242 .destroy
= ioa_destroy
,
243 .status
= ioa_status
,
244 .add_path
= ioa_add_path
,
245 .fail_path
= ioa_fail_path
,
246 .reinstate_path
= ioa_reinstate_path
,
247 .select_path
= ioa_select_path
,
250 static int __init
dm_ioa_init(void)
252 int ret
= dm_register_path_selector(&ioa_ps
);
255 DMERR("register failed %d", ret
);
259 static void __exit
dm_ioa_exit(void)
261 int ret
= dm_unregister_path_selector(&ioa_ps
);
264 DMERR("unregister failed %d", ret
);
267 module_init(dm_ioa_init
);
268 module_exit(dm_ioa_exit
);
270 MODULE_DESCRIPTION(DM_NAME
" multipath path selector that selects paths based on the CPU IO is being executed on");
271 MODULE_AUTHOR("Mike Christie <michael.christie@oracle.com>");
272 MODULE_LICENSE("GPL");