288 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2004-2005 IBM Corp. All Rights Reserved.4 * Copyright (C) 2006-2009 NEC Corporation.5 *6 * dm-queue-length.c7 *8 * Module Author: Stefan Bader, IBM9 * Modified by: Kiyoshi Ueda, NEC10 *11 * This file is released under the GPL.12 *13 * queue-length path selector - choose a path with the least number of14 * 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 128#define QL_VERSION "0.2.0"29 30struct selector {31 struct list_head valid_paths;32 struct list_head failed_paths;33 spinlock_t lock;34};35 36struct 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 43static 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 56static 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 67static 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 77static 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 87static 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 115static 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 164static 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 175static 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/Os190 */191static 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;218out:219 spin_unlock_irqrestore(&s->lock, flags);220 return ret;221}222 223static 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 233static 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 243static 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 259static 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 271static 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 279module_init(dm_ql_init);280module_exit(dm_ql_exit);281 282MODULE_AUTHOR("Stefan Bader <Stefan.Bader at de.ibm.com>");283MODULE_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);287MODULE_LICENSE("GPL");288