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