1*3bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only 2298fb372SMike Snitzer /* 3298fb372SMike Snitzer * Copyright (C) 2007-2009 NEC Corporation. All Rights Reserved. 4298fb372SMike Snitzer * 5298fb372SMike Snitzer * Module Author: Kiyoshi Ueda 6298fb372SMike Snitzer * 7298fb372SMike Snitzer * This file is released under the GPL. 8298fb372SMike Snitzer * 9298fb372SMike Snitzer * Throughput oriented path selector. 10298fb372SMike Snitzer */ 11298fb372SMike Snitzer 12298fb372SMike Snitzer #include "dm.h" 13298fb372SMike Snitzer #include "dm-path-selector.h" 14298fb372SMike Snitzer 15298fb372SMike Snitzer #include <linux/slab.h> 16298fb372SMike Snitzer #include <linux/module.h> 17298fb372SMike Snitzer 18298fb372SMike Snitzer #define DM_MSG_PREFIX "multipath service-time" 19298fb372SMike Snitzer #define ST_MIN_IO 1 20298fb372SMike Snitzer #define ST_MAX_RELATIVE_THROUGHPUT 100 21298fb372SMike Snitzer #define ST_MAX_RELATIVE_THROUGHPUT_SHIFT 7 22298fb372SMike Snitzer #define ST_MAX_INFLIGHT_SIZE ((size_t)-1 >> ST_MAX_RELATIVE_THROUGHPUT_SHIFT) 23298fb372SMike Snitzer #define ST_VERSION "0.3.0" 24298fb372SMike Snitzer 25298fb372SMike Snitzer struct selector { 26298fb372SMike Snitzer struct list_head valid_paths; 27298fb372SMike Snitzer struct list_head failed_paths; 28298fb372SMike Snitzer spinlock_t lock; 29298fb372SMike Snitzer }; 30298fb372SMike Snitzer 31298fb372SMike Snitzer struct path_info { 32298fb372SMike Snitzer struct list_head list; 33298fb372SMike Snitzer struct dm_path *path; 34298fb372SMike Snitzer unsigned repeat_count; 35298fb372SMike Snitzer unsigned relative_throughput; 36298fb372SMike Snitzer atomic_t in_flight_size; /* Total size of in-flight I/Os */ 37298fb372SMike Snitzer }; 38298fb372SMike Snitzer 39298fb372SMike Snitzer static struct selector *alloc_selector(void) 40298fb372SMike Snitzer { 41298fb372SMike Snitzer struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); 42298fb372SMike Snitzer 43298fb372SMike Snitzer if (s) { 44298fb372SMike Snitzer INIT_LIST_HEAD(&s->valid_paths); 45298fb372SMike Snitzer INIT_LIST_HEAD(&s->failed_paths); 46298fb372SMike Snitzer spin_lock_init(&s->lock); 47298fb372SMike Snitzer } 48298fb372SMike Snitzer 49298fb372SMike Snitzer return s; 50298fb372SMike Snitzer } 51298fb372SMike Snitzer 52298fb372SMike Snitzer static int st_create(struct path_selector *ps, unsigned argc, char **argv) 53298fb372SMike Snitzer { 54298fb372SMike Snitzer struct selector *s = alloc_selector(); 55298fb372SMike Snitzer 56298fb372SMike Snitzer if (!s) 57298fb372SMike Snitzer return -ENOMEM; 58298fb372SMike Snitzer 59298fb372SMike Snitzer ps->context = s; 60298fb372SMike Snitzer return 0; 61298fb372SMike Snitzer } 62298fb372SMike Snitzer 63298fb372SMike Snitzer static void free_paths(struct list_head *paths) 64298fb372SMike Snitzer { 65298fb372SMike Snitzer struct path_info *pi, *next; 66298fb372SMike Snitzer 67298fb372SMike Snitzer list_for_each_entry_safe(pi, next, paths, list) { 68298fb372SMike Snitzer list_del(&pi->list); 69298fb372SMike Snitzer kfree(pi); 70298fb372SMike Snitzer } 71298fb372SMike Snitzer } 72298fb372SMike Snitzer 73298fb372SMike Snitzer static void st_destroy(struct path_selector *ps) 74298fb372SMike Snitzer { 75298fb372SMike Snitzer struct selector *s = ps->context; 76298fb372SMike Snitzer 77298fb372SMike Snitzer free_paths(&s->valid_paths); 78298fb372SMike Snitzer free_paths(&s->failed_paths); 79298fb372SMike Snitzer kfree(s); 80298fb372SMike Snitzer ps->context = NULL; 81298fb372SMike Snitzer } 82298fb372SMike Snitzer 83298fb372SMike Snitzer static int st_status(struct path_selector *ps, struct dm_path *path, 84298fb372SMike Snitzer status_type_t type, char *result, unsigned maxlen) 85298fb372SMike Snitzer { 86298fb372SMike Snitzer unsigned sz = 0; 87298fb372SMike Snitzer struct path_info *pi; 88298fb372SMike Snitzer 89298fb372SMike Snitzer if (!path) 90298fb372SMike Snitzer DMEMIT("0 "); 91298fb372SMike Snitzer else { 92298fb372SMike Snitzer pi = path->pscontext; 93298fb372SMike Snitzer 94298fb372SMike Snitzer switch (type) { 95298fb372SMike Snitzer case STATUSTYPE_INFO: 96298fb372SMike Snitzer DMEMIT("%d %u ", atomic_read(&pi->in_flight_size), 97298fb372SMike Snitzer pi->relative_throughput); 98298fb372SMike Snitzer break; 99298fb372SMike Snitzer case STATUSTYPE_TABLE: 100298fb372SMike Snitzer DMEMIT("%u %u ", pi->repeat_count, 101298fb372SMike Snitzer pi->relative_throughput); 102298fb372SMike Snitzer break; 1038ec45662STushar Sugandhi case STATUSTYPE_IMA: 1048ec45662STushar Sugandhi result[0] = '\0'; 1058ec45662STushar Sugandhi break; 106298fb372SMike Snitzer } 107298fb372SMike Snitzer } 108298fb372SMike Snitzer 109298fb372SMike Snitzer return sz; 110298fb372SMike Snitzer } 111298fb372SMike Snitzer 112298fb372SMike Snitzer static int st_add_path(struct path_selector *ps, struct dm_path *path, 113298fb372SMike Snitzer int argc, char **argv, char **error) 114298fb372SMike Snitzer { 115298fb372SMike Snitzer struct selector *s = ps->context; 116298fb372SMike Snitzer struct path_info *pi; 117298fb372SMike Snitzer unsigned repeat_count = ST_MIN_IO; 118298fb372SMike Snitzer unsigned relative_throughput = 1; 119298fb372SMike Snitzer char dummy; 120298fb372SMike Snitzer unsigned long flags; 121298fb372SMike Snitzer 122298fb372SMike Snitzer /* 123298fb372SMike Snitzer * Arguments: [<repeat_count> [<relative_throughput>]] 124298fb372SMike Snitzer * <repeat_count>: The number of I/Os before switching path. 125298fb372SMike Snitzer * If not given, default (ST_MIN_IO) is used. 126298fb372SMike Snitzer * <relative_throughput>: The relative throughput value of 127298fb372SMike Snitzer * the path among all paths in the path-group. 128298fb372SMike Snitzer * The valid range: 0-<ST_MAX_RELATIVE_THROUGHPUT> 129298fb372SMike Snitzer * If not given, minimum value '1' is used. 130298fb372SMike Snitzer * If '0' is given, the path isn't selected while 131298fb372SMike Snitzer * other paths having a positive value are 132298fb372SMike Snitzer * available. 133298fb372SMike Snitzer */ 134298fb372SMike Snitzer if (argc > 2) { 135298fb372SMike Snitzer *error = "service-time ps: incorrect number of arguments"; 136298fb372SMike Snitzer return -EINVAL; 137298fb372SMike Snitzer } 138298fb372SMike Snitzer 139298fb372SMike Snitzer if (argc && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 140298fb372SMike Snitzer *error = "service-time ps: invalid repeat count"; 141298fb372SMike Snitzer return -EINVAL; 142298fb372SMike Snitzer } 143298fb372SMike Snitzer 144298fb372SMike Snitzer if (repeat_count > 1) { 145298fb372SMike Snitzer DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead"); 146298fb372SMike Snitzer repeat_count = 1; 147298fb372SMike Snitzer } 148298fb372SMike Snitzer 149298fb372SMike Snitzer if ((argc == 2) && 150298fb372SMike Snitzer (sscanf(argv[1], "%u%c", &relative_throughput, &dummy) != 1 || 151298fb372SMike Snitzer relative_throughput > ST_MAX_RELATIVE_THROUGHPUT)) { 152298fb372SMike Snitzer *error = "service-time ps: invalid relative_throughput value"; 153298fb372SMike Snitzer return -EINVAL; 154298fb372SMike Snitzer } 155298fb372SMike Snitzer 156298fb372SMike Snitzer /* allocate the path */ 157298fb372SMike Snitzer pi = kmalloc(sizeof(*pi), GFP_KERNEL); 158298fb372SMike Snitzer if (!pi) { 159298fb372SMike Snitzer *error = "service-time ps: Error allocating path context"; 160298fb372SMike Snitzer return -ENOMEM; 161298fb372SMike Snitzer } 162298fb372SMike Snitzer 163298fb372SMike Snitzer pi->path = path; 164298fb372SMike Snitzer pi->repeat_count = repeat_count; 165298fb372SMike Snitzer pi->relative_throughput = relative_throughput; 166298fb372SMike Snitzer atomic_set(&pi->in_flight_size, 0); 167298fb372SMike Snitzer 168298fb372SMike Snitzer path->pscontext = pi; 169298fb372SMike Snitzer 170298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 171298fb372SMike Snitzer list_add_tail(&pi->list, &s->valid_paths); 172298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 173298fb372SMike Snitzer 174298fb372SMike Snitzer return 0; 175298fb372SMike Snitzer } 176298fb372SMike Snitzer 177298fb372SMike Snitzer static void st_fail_path(struct path_selector *ps, struct dm_path *path) 178298fb372SMike Snitzer { 179298fb372SMike Snitzer struct selector *s = ps->context; 180298fb372SMike Snitzer struct path_info *pi = path->pscontext; 181298fb372SMike Snitzer unsigned long flags; 182298fb372SMike Snitzer 183298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 184298fb372SMike Snitzer list_move(&pi->list, &s->failed_paths); 185298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 186298fb372SMike Snitzer } 187298fb372SMike Snitzer 188298fb372SMike Snitzer static int st_reinstate_path(struct path_selector *ps, struct dm_path *path) 189298fb372SMike Snitzer { 190298fb372SMike Snitzer struct selector *s = ps->context; 191298fb372SMike Snitzer struct path_info *pi = path->pscontext; 192298fb372SMike Snitzer unsigned long flags; 193298fb372SMike Snitzer 194298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 195298fb372SMike Snitzer list_move_tail(&pi->list, &s->valid_paths); 196298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 197298fb372SMike Snitzer 198298fb372SMike Snitzer return 0; 199298fb372SMike Snitzer } 200298fb372SMike Snitzer 201298fb372SMike Snitzer /* 202298fb372SMike Snitzer * Compare the estimated service time of 2 paths, pi1 and pi2, 203298fb372SMike Snitzer * for the incoming I/O. 204298fb372SMike Snitzer * 205298fb372SMike Snitzer * Returns: 206298fb372SMike Snitzer * < 0 : pi1 is better 207298fb372SMike Snitzer * 0 : no difference between pi1 and pi2 208298fb372SMike Snitzer * > 0 : pi2 is better 209298fb372SMike Snitzer * 210298fb372SMike Snitzer * Description: 211298fb372SMike Snitzer * Basically, the service time is estimated by: 212298fb372SMike Snitzer * ('pi->in-flight-size' + 'incoming') / 'pi->relative_throughput' 213298fb372SMike Snitzer * To reduce the calculation, some optimizations are made. 214298fb372SMike Snitzer * (See comments inline) 215298fb372SMike Snitzer */ 216298fb372SMike Snitzer static int st_compare_load(struct path_info *pi1, struct path_info *pi2, 217298fb372SMike Snitzer size_t incoming) 218298fb372SMike Snitzer { 219298fb372SMike Snitzer size_t sz1, sz2, st1, st2; 220298fb372SMike Snitzer 221298fb372SMike Snitzer sz1 = atomic_read(&pi1->in_flight_size); 222298fb372SMike Snitzer sz2 = atomic_read(&pi2->in_flight_size); 223298fb372SMike Snitzer 224298fb372SMike Snitzer /* 225298fb372SMike Snitzer * Case 1: Both have same throughput value. Choose less loaded path. 226298fb372SMike Snitzer */ 227298fb372SMike Snitzer if (pi1->relative_throughput == pi2->relative_throughput) 228298fb372SMike Snitzer return sz1 - sz2; 229298fb372SMike Snitzer 230298fb372SMike Snitzer /* 231298fb372SMike Snitzer * Case 2a: Both have same load. Choose higher throughput path. 232298fb372SMike Snitzer * Case 2b: One path has no throughput value. Choose the other one. 233298fb372SMike Snitzer */ 234298fb372SMike Snitzer if (sz1 == sz2 || 235298fb372SMike Snitzer !pi1->relative_throughput || !pi2->relative_throughput) 236298fb372SMike Snitzer return pi2->relative_throughput - pi1->relative_throughput; 237298fb372SMike Snitzer 238298fb372SMike Snitzer /* 239298fb372SMike Snitzer * Case 3: Calculate service time. Choose faster path. 240298fb372SMike Snitzer * Service time using pi1: 241298fb372SMike Snitzer * st1 = (sz1 + incoming) / pi1->relative_throughput 242298fb372SMike Snitzer * Service time using pi2: 243298fb372SMike Snitzer * st2 = (sz2 + incoming) / pi2->relative_throughput 244298fb372SMike Snitzer * 245298fb372SMike Snitzer * To avoid the division, transform the expression to use 246298fb372SMike Snitzer * multiplication. 247298fb372SMike Snitzer * Because ->relative_throughput > 0 here, if st1 < st2, 248298fb372SMike Snitzer * the expressions below are the same meaning: 249298fb372SMike Snitzer * (sz1 + incoming) / pi1->relative_throughput < 250298fb372SMike Snitzer * (sz2 + incoming) / pi2->relative_throughput 251298fb372SMike Snitzer * (sz1 + incoming) * pi2->relative_throughput < 252298fb372SMike Snitzer * (sz2 + incoming) * pi1->relative_throughput 253298fb372SMike Snitzer * So use the later one. 254298fb372SMike Snitzer */ 255298fb372SMike Snitzer sz1 += incoming; 256298fb372SMike Snitzer sz2 += incoming; 257298fb372SMike Snitzer if (unlikely(sz1 >= ST_MAX_INFLIGHT_SIZE || 258298fb372SMike Snitzer sz2 >= ST_MAX_INFLIGHT_SIZE)) { 259298fb372SMike Snitzer /* 260298fb372SMike Snitzer * Size may be too big for multiplying pi->relative_throughput 261298fb372SMike Snitzer * and overflow. 262298fb372SMike Snitzer * To avoid the overflow and mis-selection, shift down both. 263298fb372SMike Snitzer */ 264298fb372SMike Snitzer sz1 >>= ST_MAX_RELATIVE_THROUGHPUT_SHIFT; 265298fb372SMike Snitzer sz2 >>= ST_MAX_RELATIVE_THROUGHPUT_SHIFT; 266298fb372SMike Snitzer } 267298fb372SMike Snitzer st1 = sz1 * pi2->relative_throughput; 268298fb372SMike Snitzer st2 = sz2 * pi1->relative_throughput; 269298fb372SMike Snitzer if (st1 != st2) 270298fb372SMike Snitzer return st1 - st2; 271298fb372SMike Snitzer 272298fb372SMike Snitzer /* 273298fb372SMike Snitzer * Case 4: Service time is equal. Choose higher throughput path. 274298fb372SMike Snitzer */ 275298fb372SMike Snitzer return pi2->relative_throughput - pi1->relative_throughput; 276298fb372SMike Snitzer } 277298fb372SMike Snitzer 278298fb372SMike Snitzer static struct dm_path *st_select_path(struct path_selector *ps, size_t nr_bytes) 279298fb372SMike Snitzer { 280298fb372SMike Snitzer struct selector *s = ps->context; 281298fb372SMike Snitzer struct path_info *pi = NULL, *best = NULL; 282298fb372SMike Snitzer struct dm_path *ret = NULL; 283298fb372SMike Snitzer unsigned long flags; 284298fb372SMike Snitzer 285298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 286298fb372SMike Snitzer if (list_empty(&s->valid_paths)) 287298fb372SMike Snitzer goto out; 288298fb372SMike Snitzer 289298fb372SMike Snitzer list_for_each_entry(pi, &s->valid_paths, list) 290298fb372SMike Snitzer if (!best || (st_compare_load(pi, best, nr_bytes) < 0)) 291298fb372SMike Snitzer best = pi; 292298fb372SMike Snitzer 293298fb372SMike Snitzer if (!best) 294298fb372SMike Snitzer goto out; 295298fb372SMike Snitzer 296298fb372SMike Snitzer /* Move most recently used to least preferred to evenly balance. */ 297298fb372SMike Snitzer list_move_tail(&best->list, &s->valid_paths); 298298fb372SMike Snitzer 299298fb372SMike Snitzer ret = best->path; 300298fb372SMike Snitzer out: 301298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 302298fb372SMike Snitzer return ret; 303298fb372SMike Snitzer } 304298fb372SMike Snitzer 305298fb372SMike Snitzer static int st_start_io(struct path_selector *ps, struct dm_path *path, 306298fb372SMike Snitzer size_t nr_bytes) 307298fb372SMike Snitzer { 308298fb372SMike Snitzer struct path_info *pi = path->pscontext; 309298fb372SMike Snitzer 310298fb372SMike Snitzer atomic_add(nr_bytes, &pi->in_flight_size); 311298fb372SMike Snitzer 312298fb372SMike Snitzer return 0; 313298fb372SMike Snitzer } 314298fb372SMike Snitzer 315298fb372SMike Snitzer static int st_end_io(struct path_selector *ps, struct dm_path *path, 316298fb372SMike Snitzer size_t nr_bytes, u64 start_time) 317298fb372SMike Snitzer { 318298fb372SMike Snitzer struct path_info *pi = path->pscontext; 319298fb372SMike Snitzer 320298fb372SMike Snitzer atomic_sub(nr_bytes, &pi->in_flight_size); 321298fb372SMike Snitzer 322298fb372SMike Snitzer return 0; 323298fb372SMike Snitzer } 324298fb372SMike Snitzer 325298fb372SMike Snitzer static struct path_selector_type st_ps = { 326298fb372SMike Snitzer .name = "service-time", 327298fb372SMike Snitzer .module = THIS_MODULE, 328298fb372SMike Snitzer .table_args = 2, 329298fb372SMike Snitzer .info_args = 2, 330298fb372SMike Snitzer .create = st_create, 331298fb372SMike Snitzer .destroy = st_destroy, 332298fb372SMike Snitzer .status = st_status, 333298fb372SMike Snitzer .add_path = st_add_path, 334298fb372SMike Snitzer .fail_path = st_fail_path, 335298fb372SMike Snitzer .reinstate_path = st_reinstate_path, 336298fb372SMike Snitzer .select_path = st_select_path, 337298fb372SMike Snitzer .start_io = st_start_io, 338298fb372SMike Snitzer .end_io = st_end_io, 339298fb372SMike Snitzer }; 340298fb372SMike Snitzer 341298fb372SMike Snitzer static int __init dm_st_init(void) 342298fb372SMike Snitzer { 343298fb372SMike Snitzer int r = dm_register_path_selector(&st_ps); 344298fb372SMike Snitzer 345298fb372SMike Snitzer if (r < 0) 346298fb372SMike Snitzer DMERR("register failed %d", r); 347298fb372SMike Snitzer 348298fb372SMike Snitzer DMINFO("version " ST_VERSION " loaded"); 349298fb372SMike Snitzer 350298fb372SMike Snitzer return r; 351298fb372SMike Snitzer } 352298fb372SMike Snitzer 353298fb372SMike Snitzer static void __exit dm_st_exit(void) 354298fb372SMike Snitzer { 355298fb372SMike Snitzer int r = dm_unregister_path_selector(&st_ps); 356298fb372SMike Snitzer 357298fb372SMike Snitzer if (r < 0) 358298fb372SMike Snitzer DMERR("unregister failed %d", r); 359298fb372SMike Snitzer } 360298fb372SMike Snitzer 361298fb372SMike Snitzer module_init(dm_st_init); 362298fb372SMike Snitzer module_exit(dm_st_exit); 363298fb372SMike Snitzer 364298fb372SMike Snitzer MODULE_DESCRIPTION(DM_NAME " throughput oriented path selector"); 365298fb372SMike Snitzer MODULE_AUTHOR("Kiyoshi Ueda <k-ueda@ct.jp.nec.com>"); 366298fb372SMike Snitzer MODULE_LICENSE("GPL"); 367