dm-ps-io-affinity.c (5379B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2020 Oracle Corporation 4 * 5 * Module Author: Mike Christie 6 */ 7#include "dm-path-selector.h" 8 9#include <linux/device-mapper.h> 10#include <linux/module.h> 11 12#define DM_MSG_PREFIX "multipath io-affinity" 13 14struct path_info { 15 struct dm_path *path; 16 cpumask_var_t cpumask; 17 refcount_t refcount; 18 bool failed; 19}; 20 21struct selector { 22 struct path_info **path_map; 23 cpumask_var_t path_mask; 24 atomic_t map_misses; 25}; 26 27static void ioa_free_path(struct selector *s, unsigned int cpu) 28{ 29 struct path_info *pi = s->path_map[cpu]; 30 31 if (!pi) 32 return; 33 34 if (refcount_dec_and_test(&pi->refcount)) { 35 cpumask_clear_cpu(cpu, s->path_mask); 36 free_cpumask_var(pi->cpumask); 37 kfree(pi); 38 39 s->path_map[cpu] = NULL; 40 } 41} 42 43static int ioa_add_path(struct path_selector *ps, struct dm_path *path, 44 int argc, char **argv, char **error) 45{ 46 struct selector *s = ps->context; 47 struct path_info *pi = NULL; 48 unsigned int cpu; 49 int ret; 50 51 if (argc != 1) { 52 *error = "io-affinity ps: invalid number of arguments"; 53 return -EINVAL; 54 } 55 56 pi = kzalloc(sizeof(*pi), GFP_KERNEL); 57 if (!pi) { 58 *error = "io-affinity ps: Error allocating path context"; 59 return -ENOMEM; 60 } 61 62 pi->path = path; 63 path->pscontext = pi; 64 refcount_set(&pi->refcount, 1); 65 66 if (!zalloc_cpumask_var(&pi->cpumask, GFP_KERNEL)) { 67 *error = "io-affinity ps: Error allocating cpumask context"; 68 ret = -ENOMEM; 69 goto free_pi; 70 } 71 72 ret = cpumask_parse(argv[0], pi->cpumask); 73 if (ret) { 74 *error = "io-affinity ps: invalid cpumask"; 75 ret = -EINVAL; 76 goto free_mask; 77 } 78 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", 82 cpu, nr_cpu_ids); 83 break; 84 } 85 86 if (s->path_map[cpu]) { 87 DMWARN("CPU mapping for %u exists. Ignoring.", cpu); 88 continue; 89 } 90 91 cpumask_set_cpu(cpu, s->path_mask); 92 s->path_map[cpu] = pi; 93 refcount_inc(&pi->refcount); 94 } 95 96 if (refcount_dec_and_test(&pi->refcount)) { 97 *error = "io-affinity ps: No new/valid CPU mapping found"; 98 ret = -EINVAL; 99 goto free_mask; 100 } 101 102 return 0; 103 104free_mask: 105 free_cpumask_var(pi->cpumask); 106free_pi: 107 kfree(pi); 108 return ret; 109} 110 111static int ioa_create(struct path_selector *ps, unsigned argc, char **argv) 112{ 113 struct selector *s; 114 115 s = kmalloc(sizeof(*s), GFP_KERNEL); 116 if (!s) 117 return -ENOMEM; 118 119 s->path_map = kzalloc(nr_cpu_ids * sizeof(struct path_info *), 120 GFP_KERNEL); 121 if (!s->path_map) 122 goto free_selector; 123 124 if (!zalloc_cpumask_var(&s->path_mask, GFP_KERNEL)) 125 goto free_map; 126 127 atomic_set(&s->map_misses, 0); 128 ps->context = s; 129 return 0; 130 131free_map: 132 kfree(s->path_map); 133free_selector: 134 kfree(s); 135 return -ENOMEM; 136} 137 138static void ioa_destroy(struct path_selector *ps) 139{ 140 struct selector *s = ps->context; 141 unsigned cpu; 142 143 for_each_cpu(cpu, s->path_mask) 144 ioa_free_path(s, cpu); 145 146 free_cpumask_var(s->path_mask); 147 kfree(s->path_map); 148 kfree(s); 149 150 ps->context = NULL; 151} 152 153static int ioa_status(struct path_selector *ps, struct dm_path *path, 154 status_type_t type, char *result, unsigned int maxlen) 155{ 156 struct selector *s = ps->context; 157 struct path_info *pi; 158 int sz = 0; 159 160 if (!path) { 161 DMEMIT("0 "); 162 return sz; 163 } 164 165 switch(type) { 166 case STATUSTYPE_INFO: 167 DMEMIT("%d ", atomic_read(&s->map_misses)); 168 break; 169 case STATUSTYPE_TABLE: 170 pi = path->pscontext; 171 DMEMIT("%*pb ", cpumask_pr_args(pi->cpumask)); 172 break; 173 case STATUSTYPE_IMA: 174 *result = '\0'; 175 break; 176 } 177 178 return sz; 179} 180 181static void ioa_fail_path(struct path_selector *ps, struct dm_path *p) 182{ 183 struct path_info *pi = p->pscontext; 184 185 pi->failed = true; 186} 187 188static int ioa_reinstate_path(struct path_selector *ps, struct dm_path *p) 189{ 190 struct path_info *pi = p->pscontext; 191 192 pi->failed = false; 193 return 0; 194} 195 196static struct dm_path *ioa_select_path(struct path_selector *ps, 197 size_t nr_bytes) 198{ 199 unsigned int cpu, node; 200 struct selector *s = ps->context; 201 const struct cpumask *cpumask; 202 struct path_info *pi; 203 int i; 204 205 cpu = get_cpu(); 206 207 pi = s->path_map[cpu]; 208 if (pi && !pi->failed) 209 goto done; 210 211 /* 212 * Perf is not optimal, but we at least try the local node then just 213 * try not to fail. 214 */ 215 if (!pi) 216 atomic_inc(&s->map_misses); 217 218 node = cpu_to_node(cpu); 219 cpumask = cpumask_of_node(node); 220 for_each_cpu(i, cpumask) { 221 pi = s->path_map[i]; 222 if (pi && !pi->failed) 223 goto done; 224 } 225 226 for_each_cpu(i, s->path_mask) { 227 pi = s->path_map[i]; 228 if (pi && !pi->failed) 229 goto done; 230 } 231 pi = NULL; 232 233done: 234 put_cpu(); 235 return pi ? pi->path : NULL; 236} 237 238static struct path_selector_type ioa_ps = { 239 .name = "io-affinity", 240 .module = THIS_MODULE, 241 .table_args = 1, 242 .info_args = 1, 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, 250}; 251 252static int __init dm_ioa_init(void) 253{ 254 int ret = dm_register_path_selector(&ioa_ps); 255 256 if (ret < 0) 257 DMERR("register failed %d", ret); 258 return ret; 259} 260 261static void __exit dm_ioa_exit(void) 262{ 263 int ret = dm_unregister_path_selector(&ioa_ps); 264 265 if (ret < 0) 266 DMERR("unregister failed %d", ret); 267} 268 269module_init(dm_ioa_init); 270module_exit(dm_ioa_exit); 271 272MODULE_DESCRIPTION(DM_NAME " multipath path selector that selects paths based on the CPU IO is being executed on"); 273MODULE_AUTHOR("Mike Christie <michael.christie@oracle.com>"); 274MODULE_LICENSE("GPL");