1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2004-2005 IBM Corp. All Rights Reserved. 4 * Copyright (C) 2006-2009 NEC Corporation. 5 * 6 * dm-queue-length.c 7 * 8 * Module Author: Stefan Bader, IBM 9 * Modified by: Kiyoshi Ueda, NEC 10 * 11 * This file is released under the GPL. 12 * 13 * queue-length path selector - choose a path with the least number of 14 * in-flight I/Os. 15 */ 16 17 #include "dm.h" 18 #include "dm-path-selector.h" 19 20 #include <linux/slab.h> 21 #include <linux/ctype.h> 22 #include <linux/errno.h> 23 #include <linux/module.h> 24 #include <linux/atomic.h> 25 26 #define DM_MSG_PREFIX "multipath queue-length" 27 #define QL_MIN_IO 1 28 #define QL_VERSION "0.2.0" 29 30 struct selector { 31 struct list_head valid_paths; 32 struct list_head failed_paths; 33 spinlock_t lock; 34 }; 35 36 struct path_info { 37 struct list_head list; 38 struct dm_path *path; 39 unsigned int repeat_count; 40 atomic_t qlen; /* the number of in-flight I/Os */ 41 }; 42 43 static struct selector *alloc_selector(void) 44 { 45 struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); 46 47 if (s) { 48 INIT_LIST_HEAD(&s->valid_paths); 49 INIT_LIST_HEAD(&s->failed_paths); 50 spin_lock_init(&s->lock); 51 } 52 53 return s; 54 } 55 56 static int ql_create(struct path_selector *ps, unsigned int argc, char **argv) 57 { 58 struct selector *s = alloc_selector(); 59 60 if (!s) 61 return -ENOMEM; 62 63 ps->context = s; 64 return 0; 65 } 66 67 static void ql_free_paths(struct list_head *paths) 68 { 69 struct path_info *pi, *next; 70 71 list_for_each_entry_safe(pi, next, paths, list) { 72 list_del(&pi->list); 73 kfree(pi); 74 } 75 } 76 77 static void ql_destroy(struct path_selector *ps) 78 { 79 struct selector *s = ps->context; 80 81 ql_free_paths(&s->valid_paths); 82 ql_free_paths(&s->failed_paths); 83 kfree(s); 84 ps->context = NULL; 85 } 86 87 static int ql_status(struct path_selector *ps, struct dm_path *path, 88 status_type_t type, char *result, unsigned int maxlen) 89 { 90 unsigned int sz = 0; 91 struct path_info *pi; 92 93 /* When called with NULL path, return selector status/args. */ 94 if (!path) 95 DMEMIT("0 "); 96 else { 97 pi = path->pscontext; 98 99 switch (type) { 100 case STATUSTYPE_INFO: 101 DMEMIT("%d ", atomic_read(&pi->qlen)); 102 break; 103 case STATUSTYPE_TABLE: 104 DMEMIT("%u ", pi->repeat_count); 105 break; 106 case STATUSTYPE_IMA: 107 *result = '\0'; 108 break; 109 } 110 } 111 112 return sz; 113 } 114 115 static int ql_add_path(struct path_selector *ps, struct dm_path *path, 116 int argc, char **argv, char **error) 117 { 118 struct selector *s = ps->context; 119 struct path_info *pi; 120 unsigned int repeat_count = QL_MIN_IO; 121 char dummy; 122 unsigned long flags; 123 124 /* 125 * Arguments: [<repeat_count>] 126 * <repeat_count>: The number of I/Os before switching path. 127 * If not given, default (QL_MIN_IO) is used. 128 */ 129 if (argc > 1) { 130 *error = "queue-length ps: incorrect number of arguments"; 131 return -EINVAL; 132 } 133 134 if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 135 *error = "queue-length ps: invalid repeat count"; 136 return -EINVAL; 137 } 138 139 if (repeat_count > 1) { 140 DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead"); 141 repeat_count = 1; 142 } 143 144 /* Allocate the path information structure */ 145 pi = kmalloc(sizeof(*pi), GFP_KERNEL); 146 if (!pi) { 147 *error = "queue-length ps: Error allocating path information"; 148 return -ENOMEM; 149 } 150 151 pi->path = path; 152 pi->repeat_count = repeat_count; 153 atomic_set(&pi->qlen, 0); 154 155 path->pscontext = pi; 156 157 spin_lock_irqsave(&s->lock, flags); 158 list_add_tail(&pi->list, &s->valid_paths); 159 spin_unlock_irqrestore(&s->lock, flags); 160 161 return 0; 162 } 163 164 static void ql_fail_path(struct path_selector *ps, struct dm_path *path) 165 { 166 struct selector *s = ps->context; 167 struct path_info *pi = path->pscontext; 168 unsigned long flags; 169 170 spin_lock_irqsave(&s->lock, flags); 171 list_move(&pi->list, &s->failed_paths); 172 spin_unlock_irqrestore(&s->lock, flags); 173 } 174 175 static int ql_reinstate_path(struct path_selector *ps, struct dm_path *path) 176 { 177 struct selector *s = ps->context; 178 struct path_info *pi = path->pscontext; 179 unsigned long flags; 180 181 spin_lock_irqsave(&s->lock, flags); 182 list_move_tail(&pi->list, &s->valid_paths); 183 spin_unlock_irqrestore(&s->lock, flags); 184 185 return 0; 186 } 187 188 /* 189 * Select a path having the minimum number of in-flight I/Os 190 */ 191 static struct dm_path *ql_select_path(struct path_selector *ps, size_t nr_bytes) 192 { 193 struct selector *s = ps->context; 194 struct path_info *pi = NULL, *best = NULL; 195 struct dm_path *ret = NULL; 196 unsigned long flags; 197 198 spin_lock_irqsave(&s->lock, flags); 199 if (list_empty(&s->valid_paths)) 200 goto out; 201 202 list_for_each_entry(pi, &s->valid_paths, list) { 203 if (!best || 204 (atomic_read(&pi->qlen) < atomic_read(&best->qlen))) 205 best = pi; 206 207 if (!atomic_read(&best->qlen)) 208 break; 209 } 210 211 if (!best) 212 goto out; 213 214 /* Move most recently used to least preferred to evenly balance. */ 215 list_move_tail(&best->list, &s->valid_paths); 216 217 ret = best->path; 218 out: 219 spin_unlock_irqrestore(&s->lock, flags); 220 return ret; 221 } 222 223 static int ql_start_io(struct path_selector *ps, struct dm_path *path, 224 size_t nr_bytes) 225 { 226 struct path_info *pi = path->pscontext; 227 228 atomic_inc(&pi->qlen); 229 230 return 0; 231 } 232 233 static int ql_end_io(struct path_selector *ps, struct dm_path *path, 234 size_t nr_bytes, u64 start_time) 235 { 236 struct path_info *pi = path->pscontext; 237 238 atomic_dec(&pi->qlen); 239 240 return 0; 241 } 242 243 static struct path_selector_type ql_ps = { 244 .name = "queue-length", 245 .module = THIS_MODULE, 246 .table_args = 1, 247 .info_args = 1, 248 .create = ql_create, 249 .destroy = ql_destroy, 250 .status = ql_status, 251 .add_path = ql_add_path, 252 .fail_path = ql_fail_path, 253 .reinstate_path = ql_reinstate_path, 254 .select_path = ql_select_path, 255 .start_io = ql_start_io, 256 .end_io = ql_end_io, 257 }; 258 259 static int __init dm_ql_init(void) 260 { 261 int r = dm_register_path_selector(&ql_ps); 262 263 if (r < 0) 264 DMERR("register failed %d", r); 265 266 DMINFO("version " QL_VERSION " loaded"); 267 268 return r; 269 } 270 271 static void __exit dm_ql_exit(void) 272 { 273 int r = dm_unregister_path_selector(&ql_ps); 274 275 if (r < 0) 276 DMERR("unregister failed %d", r); 277 } 278 279 module_init(dm_ql_init); 280 module_exit(dm_ql_exit); 281 282 MODULE_AUTHOR("Stefan Bader <Stefan.Bader at de.ibm.com>"); 283 MODULE_DESCRIPTION( 284 "(C) Copyright IBM Corp. 2004,2005 All Rights Reserved.\n" 285 DM_NAME " path selector to balance the number of in-flight I/Os" 286 ); 287 MODULE_LICENSE("GPL"); 288