1*298fb372SMike Snitzer /* 2*298fb372SMike Snitzer * Copyright (C) 2007-2009 NEC Corporation. All Rights Reserved. 3*298fb372SMike Snitzer * 4*298fb372SMike Snitzer * Module Author: Kiyoshi Ueda 5*298fb372SMike Snitzer * 6*298fb372SMike Snitzer * This file is released under the GPL. 7*298fb372SMike Snitzer * 8*298fb372SMike Snitzer * Throughput oriented path selector. 9*298fb372SMike Snitzer */ 10*298fb372SMike Snitzer 11*298fb372SMike Snitzer #include "dm.h" 12*298fb372SMike Snitzer #include "dm-path-selector.h" 13*298fb372SMike Snitzer 14*298fb372SMike Snitzer #include <linux/slab.h> 15*298fb372SMike Snitzer #include <linux/module.h> 16*298fb372SMike Snitzer 17*298fb372SMike Snitzer #define DM_MSG_PREFIX "multipath service-time" 18*298fb372SMike Snitzer #define ST_MIN_IO 1 19*298fb372SMike Snitzer #define ST_MAX_RELATIVE_THROUGHPUT 100 20*298fb372SMike Snitzer #define ST_MAX_RELATIVE_THROUGHPUT_SHIFT 7 21*298fb372SMike Snitzer #define ST_MAX_INFLIGHT_SIZE ((size_t)-1 >> ST_MAX_RELATIVE_THROUGHPUT_SHIFT) 22*298fb372SMike Snitzer #define ST_VERSION "0.3.0" 23*298fb372SMike Snitzer 24*298fb372SMike Snitzer struct selector { 25*298fb372SMike Snitzer struct list_head valid_paths; 26*298fb372SMike Snitzer struct list_head failed_paths; 27*298fb372SMike Snitzer spinlock_t lock; 28*298fb372SMike Snitzer }; 29*298fb372SMike Snitzer 30*298fb372SMike Snitzer struct path_info { 31*298fb372SMike Snitzer struct list_head list; 32*298fb372SMike Snitzer struct dm_path *path; 33*298fb372SMike Snitzer unsigned repeat_count; 34*298fb372SMike Snitzer unsigned relative_throughput; 35*298fb372SMike Snitzer atomic_t in_flight_size; /* Total size of in-flight I/Os */ 36*298fb372SMike Snitzer }; 37*298fb372SMike Snitzer 38*298fb372SMike Snitzer static struct selector *alloc_selector(void) 39*298fb372SMike Snitzer { 40*298fb372SMike Snitzer struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); 41*298fb372SMike Snitzer 42*298fb372SMike Snitzer if (s) { 43*298fb372SMike Snitzer INIT_LIST_HEAD(&s->valid_paths); 44*298fb372SMike Snitzer INIT_LIST_HEAD(&s->failed_paths); 45*298fb372SMike Snitzer spin_lock_init(&s->lock); 46*298fb372SMike Snitzer } 47*298fb372SMike Snitzer 48*298fb372SMike Snitzer return s; 49*298fb372SMike Snitzer } 50*298fb372SMike Snitzer 51*298fb372SMike Snitzer static int st_create(struct path_selector *ps, unsigned argc, char **argv) 52*298fb372SMike Snitzer { 53*298fb372SMike Snitzer struct selector *s = alloc_selector(); 54*298fb372SMike Snitzer 55*298fb372SMike Snitzer if (!s) 56*298fb372SMike Snitzer return -ENOMEM; 57*298fb372SMike Snitzer 58*298fb372SMike Snitzer ps->context = s; 59*298fb372SMike Snitzer return 0; 60*298fb372SMike Snitzer } 61*298fb372SMike Snitzer 62*298fb372SMike Snitzer static void free_paths(struct list_head *paths) 63*298fb372SMike Snitzer { 64*298fb372SMike Snitzer struct path_info *pi, *next; 65*298fb372SMike Snitzer 66*298fb372SMike Snitzer list_for_each_entry_safe(pi, next, paths, list) { 67*298fb372SMike Snitzer list_del(&pi->list); 68*298fb372SMike Snitzer kfree(pi); 69*298fb372SMike Snitzer } 70*298fb372SMike Snitzer } 71*298fb372SMike Snitzer 72*298fb372SMike Snitzer static void st_destroy(struct path_selector *ps) 73*298fb372SMike Snitzer { 74*298fb372SMike Snitzer struct selector *s = ps->context; 75*298fb372SMike Snitzer 76*298fb372SMike Snitzer free_paths(&s->valid_paths); 77*298fb372SMike Snitzer free_paths(&s->failed_paths); 78*298fb372SMike Snitzer kfree(s); 79*298fb372SMike Snitzer ps->context = NULL; 80*298fb372SMike Snitzer } 81*298fb372SMike Snitzer 82*298fb372SMike Snitzer static int st_status(struct path_selector *ps, struct dm_path *path, 83*298fb372SMike Snitzer status_type_t type, char *result, unsigned maxlen) 84*298fb372SMike Snitzer { 85*298fb372SMike Snitzer unsigned sz = 0; 86*298fb372SMike Snitzer struct path_info *pi; 87*298fb372SMike Snitzer 88*298fb372SMike Snitzer if (!path) 89*298fb372SMike Snitzer DMEMIT("0 "); 90*298fb372SMike Snitzer else { 91*298fb372SMike Snitzer pi = path->pscontext; 92*298fb372SMike Snitzer 93*298fb372SMike Snitzer switch (type) { 94*298fb372SMike Snitzer case STATUSTYPE_INFO: 95*298fb372SMike Snitzer DMEMIT("%d %u ", atomic_read(&pi->in_flight_size), 96*298fb372SMike Snitzer pi->relative_throughput); 97*298fb372SMike Snitzer break; 98*298fb372SMike Snitzer case STATUSTYPE_TABLE: 99*298fb372SMike Snitzer DMEMIT("%u %u ", pi->repeat_count, 100*298fb372SMike Snitzer pi->relative_throughput); 101*298fb372SMike Snitzer break; 102*298fb372SMike Snitzer } 103*298fb372SMike Snitzer } 104*298fb372SMike Snitzer 105*298fb372SMike Snitzer return sz; 106*298fb372SMike Snitzer } 107*298fb372SMike Snitzer 108*298fb372SMike Snitzer static int st_add_path(struct path_selector *ps, struct dm_path *path, 109*298fb372SMike Snitzer int argc, char **argv, char **error) 110*298fb372SMike Snitzer { 111*298fb372SMike Snitzer struct selector *s = ps->context; 112*298fb372SMike Snitzer struct path_info *pi; 113*298fb372SMike Snitzer unsigned repeat_count = ST_MIN_IO; 114*298fb372SMike Snitzer unsigned relative_throughput = 1; 115*298fb372SMike Snitzer char dummy; 116*298fb372SMike Snitzer unsigned long flags; 117*298fb372SMike Snitzer 118*298fb372SMike Snitzer /* 119*298fb372SMike Snitzer * Arguments: [<repeat_count> [<relative_throughput>]] 120*298fb372SMike Snitzer * <repeat_count>: The number of I/Os before switching path. 121*298fb372SMike Snitzer * If not given, default (ST_MIN_IO) is used. 122*298fb372SMike Snitzer * <relative_throughput>: The relative throughput value of 123*298fb372SMike Snitzer * the path among all paths in the path-group. 124*298fb372SMike Snitzer * The valid range: 0-<ST_MAX_RELATIVE_THROUGHPUT> 125*298fb372SMike Snitzer * If not given, minimum value '1' is used. 126*298fb372SMike Snitzer * If '0' is given, the path isn't selected while 127*298fb372SMike Snitzer * other paths having a positive value are 128*298fb372SMike Snitzer * available. 129*298fb372SMike Snitzer */ 130*298fb372SMike Snitzer if (argc > 2) { 131*298fb372SMike Snitzer *error = "service-time ps: incorrect number of arguments"; 132*298fb372SMike Snitzer return -EINVAL; 133*298fb372SMike Snitzer } 134*298fb372SMike Snitzer 135*298fb372SMike Snitzer if (argc && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 136*298fb372SMike Snitzer *error = "service-time ps: invalid repeat count"; 137*298fb372SMike Snitzer return -EINVAL; 138*298fb372SMike Snitzer } 139*298fb372SMike Snitzer 140*298fb372SMike Snitzer if (repeat_count > 1) { 141*298fb372SMike Snitzer DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead"); 142*298fb372SMike Snitzer repeat_count = 1; 143*298fb372SMike Snitzer } 144*298fb372SMike Snitzer 145*298fb372SMike Snitzer if ((argc == 2) && 146*298fb372SMike Snitzer (sscanf(argv[1], "%u%c", &relative_throughput, &dummy) != 1 || 147*298fb372SMike Snitzer relative_throughput > ST_MAX_RELATIVE_THROUGHPUT)) { 148*298fb372SMike Snitzer *error = "service-time ps: invalid relative_throughput value"; 149*298fb372SMike Snitzer return -EINVAL; 150*298fb372SMike Snitzer } 151*298fb372SMike Snitzer 152*298fb372SMike Snitzer /* allocate the path */ 153*298fb372SMike Snitzer pi = kmalloc(sizeof(*pi), GFP_KERNEL); 154*298fb372SMike Snitzer if (!pi) { 155*298fb372SMike Snitzer *error = "service-time ps: Error allocating path context"; 156*298fb372SMike Snitzer return -ENOMEM; 157*298fb372SMike Snitzer } 158*298fb372SMike Snitzer 159*298fb372SMike Snitzer pi->path = path; 160*298fb372SMike Snitzer pi->repeat_count = repeat_count; 161*298fb372SMike Snitzer pi->relative_throughput = relative_throughput; 162*298fb372SMike Snitzer atomic_set(&pi->in_flight_size, 0); 163*298fb372SMike Snitzer 164*298fb372SMike Snitzer path->pscontext = pi; 165*298fb372SMike Snitzer 166*298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 167*298fb372SMike Snitzer list_add_tail(&pi->list, &s->valid_paths); 168*298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 169*298fb372SMike Snitzer 170*298fb372SMike Snitzer return 0; 171*298fb372SMike Snitzer } 172*298fb372SMike Snitzer 173*298fb372SMike Snitzer static void st_fail_path(struct path_selector *ps, struct dm_path *path) 174*298fb372SMike Snitzer { 175*298fb372SMike Snitzer struct selector *s = ps->context; 176*298fb372SMike Snitzer struct path_info *pi = path->pscontext; 177*298fb372SMike Snitzer unsigned long flags; 178*298fb372SMike Snitzer 179*298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 180*298fb372SMike Snitzer list_move(&pi->list, &s->failed_paths); 181*298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 182*298fb372SMike Snitzer } 183*298fb372SMike Snitzer 184*298fb372SMike Snitzer static int st_reinstate_path(struct path_selector *ps, struct dm_path *path) 185*298fb372SMike Snitzer { 186*298fb372SMike Snitzer struct selector *s = ps->context; 187*298fb372SMike Snitzer struct path_info *pi = path->pscontext; 188*298fb372SMike Snitzer unsigned long flags; 189*298fb372SMike Snitzer 190*298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 191*298fb372SMike Snitzer list_move_tail(&pi->list, &s->valid_paths); 192*298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 193*298fb372SMike Snitzer 194*298fb372SMike Snitzer return 0; 195*298fb372SMike Snitzer } 196*298fb372SMike Snitzer 197*298fb372SMike Snitzer /* 198*298fb372SMike Snitzer * Compare the estimated service time of 2 paths, pi1 and pi2, 199*298fb372SMike Snitzer * for the incoming I/O. 200*298fb372SMike Snitzer * 201*298fb372SMike Snitzer * Returns: 202*298fb372SMike Snitzer * < 0 : pi1 is better 203*298fb372SMike Snitzer * 0 : no difference between pi1 and pi2 204*298fb372SMike Snitzer * > 0 : pi2 is better 205*298fb372SMike Snitzer * 206*298fb372SMike Snitzer * Description: 207*298fb372SMike Snitzer * Basically, the service time is estimated by: 208*298fb372SMike Snitzer * ('pi->in-flight-size' + 'incoming') / 'pi->relative_throughput' 209*298fb372SMike Snitzer * To reduce the calculation, some optimizations are made. 210*298fb372SMike Snitzer * (See comments inline) 211*298fb372SMike Snitzer */ 212*298fb372SMike Snitzer static int st_compare_load(struct path_info *pi1, struct path_info *pi2, 213*298fb372SMike Snitzer size_t incoming) 214*298fb372SMike Snitzer { 215*298fb372SMike Snitzer size_t sz1, sz2, st1, st2; 216*298fb372SMike Snitzer 217*298fb372SMike Snitzer sz1 = atomic_read(&pi1->in_flight_size); 218*298fb372SMike Snitzer sz2 = atomic_read(&pi2->in_flight_size); 219*298fb372SMike Snitzer 220*298fb372SMike Snitzer /* 221*298fb372SMike Snitzer * Case 1: Both have same throughput value. Choose less loaded path. 222*298fb372SMike Snitzer */ 223*298fb372SMike Snitzer if (pi1->relative_throughput == pi2->relative_throughput) 224*298fb372SMike Snitzer return sz1 - sz2; 225*298fb372SMike Snitzer 226*298fb372SMike Snitzer /* 227*298fb372SMike Snitzer * Case 2a: Both have same load. Choose higher throughput path. 228*298fb372SMike Snitzer * Case 2b: One path has no throughput value. Choose the other one. 229*298fb372SMike Snitzer */ 230*298fb372SMike Snitzer if (sz1 == sz2 || 231*298fb372SMike Snitzer !pi1->relative_throughput || !pi2->relative_throughput) 232*298fb372SMike Snitzer return pi2->relative_throughput - pi1->relative_throughput; 233*298fb372SMike Snitzer 234*298fb372SMike Snitzer /* 235*298fb372SMike Snitzer * Case 3: Calculate service time. Choose faster path. 236*298fb372SMike Snitzer * Service time using pi1: 237*298fb372SMike Snitzer * st1 = (sz1 + incoming) / pi1->relative_throughput 238*298fb372SMike Snitzer * Service time using pi2: 239*298fb372SMike Snitzer * st2 = (sz2 + incoming) / pi2->relative_throughput 240*298fb372SMike Snitzer * 241*298fb372SMike Snitzer * To avoid the division, transform the expression to use 242*298fb372SMike Snitzer * multiplication. 243*298fb372SMike Snitzer * Because ->relative_throughput > 0 here, if st1 < st2, 244*298fb372SMike Snitzer * the expressions below are the same meaning: 245*298fb372SMike Snitzer * (sz1 + incoming) / pi1->relative_throughput < 246*298fb372SMike Snitzer * (sz2 + incoming) / pi2->relative_throughput 247*298fb372SMike Snitzer * (sz1 + incoming) * pi2->relative_throughput < 248*298fb372SMike Snitzer * (sz2 + incoming) * pi1->relative_throughput 249*298fb372SMike Snitzer * So use the later one. 250*298fb372SMike Snitzer */ 251*298fb372SMike Snitzer sz1 += incoming; 252*298fb372SMike Snitzer sz2 += incoming; 253*298fb372SMike Snitzer if (unlikely(sz1 >= ST_MAX_INFLIGHT_SIZE || 254*298fb372SMike Snitzer sz2 >= ST_MAX_INFLIGHT_SIZE)) { 255*298fb372SMike Snitzer /* 256*298fb372SMike Snitzer * Size may be too big for multiplying pi->relative_throughput 257*298fb372SMike Snitzer * and overflow. 258*298fb372SMike Snitzer * To avoid the overflow and mis-selection, shift down both. 259*298fb372SMike Snitzer */ 260*298fb372SMike Snitzer sz1 >>= ST_MAX_RELATIVE_THROUGHPUT_SHIFT; 261*298fb372SMike Snitzer sz2 >>= ST_MAX_RELATIVE_THROUGHPUT_SHIFT; 262*298fb372SMike Snitzer } 263*298fb372SMike Snitzer st1 = sz1 * pi2->relative_throughput; 264*298fb372SMike Snitzer st2 = sz2 * pi1->relative_throughput; 265*298fb372SMike Snitzer if (st1 != st2) 266*298fb372SMike Snitzer return st1 - st2; 267*298fb372SMike Snitzer 268*298fb372SMike Snitzer /* 269*298fb372SMike Snitzer * Case 4: Service time is equal. Choose higher throughput path. 270*298fb372SMike Snitzer */ 271*298fb372SMike Snitzer return pi2->relative_throughput - pi1->relative_throughput; 272*298fb372SMike Snitzer } 273*298fb372SMike Snitzer 274*298fb372SMike Snitzer static struct dm_path *st_select_path(struct path_selector *ps, size_t nr_bytes) 275*298fb372SMike Snitzer { 276*298fb372SMike Snitzer struct selector *s = ps->context; 277*298fb372SMike Snitzer struct path_info *pi = NULL, *best = NULL; 278*298fb372SMike Snitzer struct dm_path *ret = NULL; 279*298fb372SMike Snitzer unsigned long flags; 280*298fb372SMike Snitzer 281*298fb372SMike Snitzer spin_lock_irqsave(&s->lock, flags); 282*298fb372SMike Snitzer if (list_empty(&s->valid_paths)) 283*298fb372SMike Snitzer goto out; 284*298fb372SMike Snitzer 285*298fb372SMike Snitzer list_for_each_entry(pi, &s->valid_paths, list) 286*298fb372SMike Snitzer if (!best || (st_compare_load(pi, best, nr_bytes) < 0)) 287*298fb372SMike Snitzer best = pi; 288*298fb372SMike Snitzer 289*298fb372SMike Snitzer if (!best) 290*298fb372SMike Snitzer goto out; 291*298fb372SMike Snitzer 292*298fb372SMike Snitzer /* Move most recently used to least preferred to evenly balance. */ 293*298fb372SMike Snitzer list_move_tail(&best->list, &s->valid_paths); 294*298fb372SMike Snitzer 295*298fb372SMike Snitzer ret = best->path; 296*298fb372SMike Snitzer out: 297*298fb372SMike Snitzer spin_unlock_irqrestore(&s->lock, flags); 298*298fb372SMike Snitzer return ret; 299*298fb372SMike Snitzer } 300*298fb372SMike Snitzer 301*298fb372SMike Snitzer static int st_start_io(struct path_selector *ps, struct dm_path *path, 302*298fb372SMike Snitzer size_t nr_bytes) 303*298fb372SMike Snitzer { 304*298fb372SMike Snitzer struct path_info *pi = path->pscontext; 305*298fb372SMike Snitzer 306*298fb372SMike Snitzer atomic_add(nr_bytes, &pi->in_flight_size); 307*298fb372SMike Snitzer 308*298fb372SMike Snitzer return 0; 309*298fb372SMike Snitzer } 310*298fb372SMike Snitzer 311*298fb372SMike Snitzer static int st_end_io(struct path_selector *ps, struct dm_path *path, 312*298fb372SMike Snitzer size_t nr_bytes, u64 start_time) 313*298fb372SMike Snitzer { 314*298fb372SMike Snitzer struct path_info *pi = path->pscontext; 315*298fb372SMike Snitzer 316*298fb372SMike Snitzer atomic_sub(nr_bytes, &pi->in_flight_size); 317*298fb372SMike Snitzer 318*298fb372SMike Snitzer return 0; 319*298fb372SMike Snitzer } 320*298fb372SMike Snitzer 321*298fb372SMike Snitzer static struct path_selector_type st_ps = { 322*298fb372SMike Snitzer .name = "service-time", 323*298fb372SMike Snitzer .module = THIS_MODULE, 324*298fb372SMike Snitzer .table_args = 2, 325*298fb372SMike Snitzer .info_args = 2, 326*298fb372SMike Snitzer .create = st_create, 327*298fb372SMike Snitzer .destroy = st_destroy, 328*298fb372SMike Snitzer .status = st_status, 329*298fb372SMike Snitzer .add_path = st_add_path, 330*298fb372SMike Snitzer .fail_path = st_fail_path, 331*298fb372SMike Snitzer .reinstate_path = st_reinstate_path, 332*298fb372SMike Snitzer .select_path = st_select_path, 333*298fb372SMike Snitzer .start_io = st_start_io, 334*298fb372SMike Snitzer .end_io = st_end_io, 335*298fb372SMike Snitzer }; 336*298fb372SMike Snitzer 337*298fb372SMike Snitzer static int __init dm_st_init(void) 338*298fb372SMike Snitzer { 339*298fb372SMike Snitzer int r = dm_register_path_selector(&st_ps); 340*298fb372SMike Snitzer 341*298fb372SMike Snitzer if (r < 0) 342*298fb372SMike Snitzer DMERR("register failed %d", r); 343*298fb372SMike Snitzer 344*298fb372SMike Snitzer DMINFO("version " ST_VERSION " loaded"); 345*298fb372SMike Snitzer 346*298fb372SMike Snitzer return r; 347*298fb372SMike Snitzer } 348*298fb372SMike Snitzer 349*298fb372SMike Snitzer static void __exit dm_st_exit(void) 350*298fb372SMike Snitzer { 351*298fb372SMike Snitzer int r = dm_unregister_path_selector(&st_ps); 352*298fb372SMike Snitzer 353*298fb372SMike Snitzer if (r < 0) 354*298fb372SMike Snitzer DMERR("unregister failed %d", r); 355*298fb372SMike Snitzer } 356*298fb372SMike Snitzer 357*298fb372SMike Snitzer module_init(dm_st_init); 358*298fb372SMike Snitzer module_exit(dm_st_exit); 359*298fb372SMike Snitzer 360*298fb372SMike Snitzer MODULE_DESCRIPTION(DM_NAME " throughput oriented path selector"); 361*298fb372SMike Snitzer MODULE_AUTHOR("Kiyoshi Ueda <k-ueda@ct.jp.nec.com>"); 362*298fb372SMike Snitzer MODULE_LICENSE("GPL"); 363