0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include "dm.h"
0012 #include "dm-path-selector.h"
0013
0014 #include <linux/slab.h>
0015 #include <linux/module.h>
0016
0017 #define DM_MSG_PREFIX "multipath service-time"
0018 #define ST_MIN_IO 1
0019 #define ST_MAX_RELATIVE_THROUGHPUT 100
0020 #define ST_MAX_RELATIVE_THROUGHPUT_SHIFT 7
0021 #define ST_MAX_INFLIGHT_SIZE ((size_t)-1 >> ST_MAX_RELATIVE_THROUGHPUT_SHIFT)
0022 #define ST_VERSION "0.3.0"
0023
0024 struct selector {
0025 struct list_head valid_paths;
0026 struct list_head failed_paths;
0027 spinlock_t lock;
0028 };
0029
0030 struct path_info {
0031 struct list_head list;
0032 struct dm_path *path;
0033 unsigned repeat_count;
0034 unsigned relative_throughput;
0035 atomic_t in_flight_size;
0036 };
0037
0038 static struct selector *alloc_selector(void)
0039 {
0040 struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
0041
0042 if (s) {
0043 INIT_LIST_HEAD(&s->valid_paths);
0044 INIT_LIST_HEAD(&s->failed_paths);
0045 spin_lock_init(&s->lock);
0046 }
0047
0048 return s;
0049 }
0050
0051 static int st_create(struct path_selector *ps, unsigned argc, char **argv)
0052 {
0053 struct selector *s = alloc_selector();
0054
0055 if (!s)
0056 return -ENOMEM;
0057
0058 ps->context = s;
0059 return 0;
0060 }
0061
0062 static void free_paths(struct list_head *paths)
0063 {
0064 struct path_info *pi, *next;
0065
0066 list_for_each_entry_safe(pi, next, paths, list) {
0067 list_del(&pi->list);
0068 kfree(pi);
0069 }
0070 }
0071
0072 static void st_destroy(struct path_selector *ps)
0073 {
0074 struct selector *s = ps->context;
0075
0076 free_paths(&s->valid_paths);
0077 free_paths(&s->failed_paths);
0078 kfree(s);
0079 ps->context = NULL;
0080 }
0081
0082 static int st_status(struct path_selector *ps, struct dm_path *path,
0083 status_type_t type, char *result, unsigned maxlen)
0084 {
0085 unsigned sz = 0;
0086 struct path_info *pi;
0087
0088 if (!path)
0089 DMEMIT("0 ");
0090 else {
0091 pi = path->pscontext;
0092
0093 switch (type) {
0094 case STATUSTYPE_INFO:
0095 DMEMIT("%d %u ", atomic_read(&pi->in_flight_size),
0096 pi->relative_throughput);
0097 break;
0098 case STATUSTYPE_TABLE:
0099 DMEMIT("%u %u ", pi->repeat_count,
0100 pi->relative_throughput);
0101 break;
0102 case STATUSTYPE_IMA:
0103 result[0] = '\0';
0104 break;
0105 }
0106 }
0107
0108 return sz;
0109 }
0110
0111 static int st_add_path(struct path_selector *ps, struct dm_path *path,
0112 int argc, char **argv, char **error)
0113 {
0114 struct selector *s = ps->context;
0115 struct path_info *pi;
0116 unsigned repeat_count = ST_MIN_IO;
0117 unsigned relative_throughput = 1;
0118 char dummy;
0119 unsigned long flags;
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133 if (argc > 2) {
0134 *error = "service-time ps: incorrect number of arguments";
0135 return -EINVAL;
0136 }
0137
0138 if (argc && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
0139 *error = "service-time ps: invalid repeat count";
0140 return -EINVAL;
0141 }
0142
0143 if (repeat_count > 1) {
0144 DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");
0145 repeat_count = 1;
0146 }
0147
0148 if ((argc == 2) &&
0149 (sscanf(argv[1], "%u%c", &relative_throughput, &dummy) != 1 ||
0150 relative_throughput > ST_MAX_RELATIVE_THROUGHPUT)) {
0151 *error = "service-time ps: invalid relative_throughput value";
0152 return -EINVAL;
0153 }
0154
0155
0156 pi = kmalloc(sizeof(*pi), GFP_KERNEL);
0157 if (!pi) {
0158 *error = "service-time ps: Error allocating path context";
0159 return -ENOMEM;
0160 }
0161
0162 pi->path = path;
0163 pi->repeat_count = repeat_count;
0164 pi->relative_throughput = relative_throughput;
0165 atomic_set(&pi->in_flight_size, 0);
0166
0167 path->pscontext = pi;
0168
0169 spin_lock_irqsave(&s->lock, flags);
0170 list_add_tail(&pi->list, &s->valid_paths);
0171 spin_unlock_irqrestore(&s->lock, flags);
0172
0173 return 0;
0174 }
0175
0176 static void st_fail_path(struct path_selector *ps, struct dm_path *path)
0177 {
0178 struct selector *s = ps->context;
0179 struct path_info *pi = path->pscontext;
0180 unsigned long flags;
0181
0182 spin_lock_irqsave(&s->lock, flags);
0183 list_move(&pi->list, &s->failed_paths);
0184 spin_unlock_irqrestore(&s->lock, flags);
0185 }
0186
0187 static int st_reinstate_path(struct path_selector *ps, struct dm_path *path)
0188 {
0189 struct selector *s = ps->context;
0190 struct path_info *pi = path->pscontext;
0191 unsigned long flags;
0192
0193 spin_lock_irqsave(&s->lock, flags);
0194 list_move_tail(&pi->list, &s->valid_paths);
0195 spin_unlock_irqrestore(&s->lock, flags);
0196
0197 return 0;
0198 }
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215 static int st_compare_load(struct path_info *pi1, struct path_info *pi2,
0216 size_t incoming)
0217 {
0218 size_t sz1, sz2, st1, st2;
0219
0220 sz1 = atomic_read(&pi1->in_flight_size);
0221 sz2 = atomic_read(&pi2->in_flight_size);
0222
0223
0224
0225
0226 if (pi1->relative_throughput == pi2->relative_throughput)
0227 return sz1 - sz2;
0228
0229
0230
0231
0232
0233 if (sz1 == sz2 ||
0234 !pi1->relative_throughput || !pi2->relative_throughput)
0235 return pi2->relative_throughput - pi1->relative_throughput;
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254 sz1 += incoming;
0255 sz2 += incoming;
0256 if (unlikely(sz1 >= ST_MAX_INFLIGHT_SIZE ||
0257 sz2 >= ST_MAX_INFLIGHT_SIZE)) {
0258
0259
0260
0261
0262
0263 sz1 >>= ST_MAX_RELATIVE_THROUGHPUT_SHIFT;
0264 sz2 >>= ST_MAX_RELATIVE_THROUGHPUT_SHIFT;
0265 }
0266 st1 = sz1 * pi2->relative_throughput;
0267 st2 = sz2 * pi1->relative_throughput;
0268 if (st1 != st2)
0269 return st1 - st2;
0270
0271
0272
0273
0274 return pi2->relative_throughput - pi1->relative_throughput;
0275 }
0276
0277 static struct dm_path *st_select_path(struct path_selector *ps, size_t nr_bytes)
0278 {
0279 struct selector *s = ps->context;
0280 struct path_info *pi = NULL, *best = NULL;
0281 struct dm_path *ret = NULL;
0282 unsigned long flags;
0283
0284 spin_lock_irqsave(&s->lock, flags);
0285 if (list_empty(&s->valid_paths))
0286 goto out;
0287
0288 list_for_each_entry(pi, &s->valid_paths, list)
0289 if (!best || (st_compare_load(pi, best, nr_bytes) < 0))
0290 best = pi;
0291
0292 if (!best)
0293 goto out;
0294
0295
0296 list_move_tail(&best->list, &s->valid_paths);
0297
0298 ret = best->path;
0299 out:
0300 spin_unlock_irqrestore(&s->lock, flags);
0301 return ret;
0302 }
0303
0304 static int st_start_io(struct path_selector *ps, struct dm_path *path,
0305 size_t nr_bytes)
0306 {
0307 struct path_info *pi = path->pscontext;
0308
0309 atomic_add(nr_bytes, &pi->in_flight_size);
0310
0311 return 0;
0312 }
0313
0314 static int st_end_io(struct path_selector *ps, struct dm_path *path,
0315 size_t nr_bytes, u64 start_time)
0316 {
0317 struct path_info *pi = path->pscontext;
0318
0319 atomic_sub(nr_bytes, &pi->in_flight_size);
0320
0321 return 0;
0322 }
0323
0324 static struct path_selector_type st_ps = {
0325 .name = "service-time",
0326 .module = THIS_MODULE,
0327 .table_args = 2,
0328 .info_args = 2,
0329 .create = st_create,
0330 .destroy = st_destroy,
0331 .status = st_status,
0332 .add_path = st_add_path,
0333 .fail_path = st_fail_path,
0334 .reinstate_path = st_reinstate_path,
0335 .select_path = st_select_path,
0336 .start_io = st_start_io,
0337 .end_io = st_end_io,
0338 };
0339
0340 static int __init dm_st_init(void)
0341 {
0342 int r = dm_register_path_selector(&st_ps);
0343
0344 if (r < 0)
0345 DMERR("register failed %d", r);
0346
0347 DMINFO("version " ST_VERSION " loaded");
0348
0349 return r;
0350 }
0351
0352 static void __exit dm_st_exit(void)
0353 {
0354 int r = dm_unregister_path_selector(&st_ps);
0355
0356 if (r < 0)
0357 DMERR("unregister failed %d", r);
0358 }
0359
0360 module_init(dm_st_init);
0361 module_exit(dm_st_exit);
0362
0363 MODULE_DESCRIPTION(DM_NAME " throughput oriented path selector");
0364 MODULE_AUTHOR("Kiyoshi Ueda <k-ueda@ct.jp.nec.com>");
0365 MODULE_LICENSE("GPL");