1 /*
2  * Copyright (C) 2004-2005 IBM Corp.  All Rights Reserved.
3  * Copyright (C) 2006-2009 NEC Corporation.
4  *
5  * dm-queue-length.c
6  *
7  * Module Author: Stefan Bader, IBM
8  * Modified by: Kiyoshi Ueda, NEC
9  *
10  * This file is released under the GPL.
11  *
12  * queue-length path selector - choose a path with the least number of
13  * in-flight I/Os.
14  */
15 
16 #include "dm.h"
17 #include "dm-path-selector.h"
18 
19 #include <linux/slab.h>
20 #include <linux/ctype.h>
21 #include <linux/errno.h>
22 #include <linux/module.h>
23 #include <linux/atomic.h>
24 
25 #define DM_MSG_PREFIX	"multipath queue-length"
26 #define QL_MIN_IO	128
27 #define QL_VERSION	"0.1.0"
28 
29 struct selector {
30 	struct list_head	valid_paths;
31 	struct list_head	failed_paths;
32 };
33 
34 struct path_info {
35 	struct list_head	list;
36 	struct dm_path		*path;
37 	unsigned		repeat_count;
38 	atomic_t		qlen;	/* the number of in-flight I/Os */
39 };
40 
alloc_selector(void)41 static struct selector *alloc_selector(void)
42 {
43 	struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
44 
45 	if (s) {
46 		INIT_LIST_HEAD(&s->valid_paths);
47 		INIT_LIST_HEAD(&s->failed_paths);
48 	}
49 
50 	return s;
51 }
52 
ql_create(struct path_selector * ps,unsigned argc,char ** argv)53 static int ql_create(struct path_selector *ps, unsigned argc, char **argv)
54 {
55 	struct selector *s = alloc_selector();
56 
57 	if (!s)
58 		return -ENOMEM;
59 
60 	ps->context = s;
61 	return 0;
62 }
63 
ql_free_paths(struct list_head * paths)64 static void ql_free_paths(struct list_head *paths)
65 {
66 	struct path_info *pi, *next;
67 
68 	list_for_each_entry_safe(pi, next, paths, list) {
69 		list_del(&pi->list);
70 		kfree(pi);
71 	}
72 }
73 
ql_destroy(struct path_selector * ps)74 static void ql_destroy(struct path_selector *ps)
75 {
76 	struct selector *s = ps->context;
77 
78 	ql_free_paths(&s->valid_paths);
79 	ql_free_paths(&s->failed_paths);
80 	kfree(s);
81 	ps->context = NULL;
82 }
83 
ql_status(struct path_selector * ps,struct dm_path * path,status_type_t type,char * result,unsigned maxlen)84 static int ql_status(struct path_selector *ps, struct dm_path *path,
85 		     status_type_t type, char *result, unsigned maxlen)
86 {
87 	unsigned sz = 0;
88 	struct path_info *pi;
89 
90 	/* When called with NULL path, return selector status/args. */
91 	if (!path)
92 		DMEMIT("0 ");
93 	else {
94 		pi = path->pscontext;
95 
96 		switch (type) {
97 		case STATUSTYPE_INFO:
98 			DMEMIT("%d ", atomic_read(&pi->qlen));
99 			break;
100 		case STATUSTYPE_TABLE:
101 			DMEMIT("%u ", pi->repeat_count);
102 			break;
103 		}
104 	}
105 
106 	return sz;
107 }
108 
ql_add_path(struct path_selector * ps,struct dm_path * path,int argc,char ** argv,char ** error)109 static int ql_add_path(struct path_selector *ps, struct dm_path *path,
110 		       int argc, char **argv, char **error)
111 {
112 	struct selector *s = ps->context;
113 	struct path_info *pi;
114 	unsigned repeat_count = QL_MIN_IO;
115 
116 	/*
117 	 * Arguments: [<repeat_count>]
118 	 * 	<repeat_count>: The number of I/Os before switching path.
119 	 * 			If not given, default (QL_MIN_IO) is used.
120 	 */
121 	if (argc > 1) {
122 		*error = "queue-length ps: incorrect number of arguments";
123 		return -EINVAL;
124 	}
125 
126 	if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) {
127 		*error = "queue-length ps: invalid repeat count";
128 		return -EINVAL;
129 	}
130 
131 	/* Allocate the path information structure */
132 	pi = kmalloc(sizeof(*pi), GFP_KERNEL);
133 	if (!pi) {
134 		*error = "queue-length ps: Error allocating path information";
135 		return -ENOMEM;
136 	}
137 
138 	pi->path = path;
139 	pi->repeat_count = repeat_count;
140 	atomic_set(&pi->qlen, 0);
141 
142 	path->pscontext = pi;
143 
144 	list_add_tail(&pi->list, &s->valid_paths);
145 
146 	return 0;
147 }
148 
ql_fail_path(struct path_selector * ps,struct dm_path * path)149 static void ql_fail_path(struct path_selector *ps, struct dm_path *path)
150 {
151 	struct selector *s = ps->context;
152 	struct path_info *pi = path->pscontext;
153 
154 	list_move(&pi->list, &s->failed_paths);
155 }
156 
ql_reinstate_path(struct path_selector * ps,struct dm_path * path)157 static int ql_reinstate_path(struct path_selector *ps, struct dm_path *path)
158 {
159 	struct selector *s = ps->context;
160 	struct path_info *pi = path->pscontext;
161 
162 	list_move_tail(&pi->list, &s->valid_paths);
163 
164 	return 0;
165 }
166 
167 /*
168  * Select a path having the minimum number of in-flight I/Os
169  */
ql_select_path(struct path_selector * ps,unsigned * repeat_count,size_t nr_bytes)170 static struct dm_path *ql_select_path(struct path_selector *ps,
171 				      unsigned *repeat_count, size_t nr_bytes)
172 {
173 	struct selector *s = ps->context;
174 	struct path_info *pi = NULL, *best = NULL;
175 
176 	if (list_empty(&s->valid_paths))
177 		return NULL;
178 
179 	/* Change preferred (first in list) path to evenly balance. */
180 	list_move_tail(s->valid_paths.next, &s->valid_paths);
181 
182 	list_for_each_entry(pi, &s->valid_paths, list) {
183 		if (!best ||
184 		    (atomic_read(&pi->qlen) < atomic_read(&best->qlen)))
185 			best = pi;
186 
187 		if (!atomic_read(&best->qlen))
188 			break;
189 	}
190 
191 	if (!best)
192 		return NULL;
193 
194 	*repeat_count = best->repeat_count;
195 
196 	return best->path;
197 }
198 
ql_start_io(struct path_selector * ps,struct dm_path * path,size_t nr_bytes)199 static int ql_start_io(struct path_selector *ps, struct dm_path *path,
200 		       size_t nr_bytes)
201 {
202 	struct path_info *pi = path->pscontext;
203 
204 	atomic_inc(&pi->qlen);
205 
206 	return 0;
207 }
208 
ql_end_io(struct path_selector * ps,struct dm_path * path,size_t nr_bytes)209 static int ql_end_io(struct path_selector *ps, struct dm_path *path,
210 		     size_t nr_bytes)
211 {
212 	struct path_info *pi = path->pscontext;
213 
214 	atomic_dec(&pi->qlen);
215 
216 	return 0;
217 }
218 
219 static struct path_selector_type ql_ps = {
220 	.name		= "queue-length",
221 	.module		= THIS_MODULE,
222 	.table_args	= 1,
223 	.info_args	= 1,
224 	.create		= ql_create,
225 	.destroy	= ql_destroy,
226 	.status		= ql_status,
227 	.add_path	= ql_add_path,
228 	.fail_path	= ql_fail_path,
229 	.reinstate_path	= ql_reinstate_path,
230 	.select_path	= ql_select_path,
231 	.start_io	= ql_start_io,
232 	.end_io		= ql_end_io,
233 };
234 
dm_ql_init(void)235 static int __init dm_ql_init(void)
236 {
237 	int r = dm_register_path_selector(&ql_ps);
238 
239 	if (r < 0)
240 		DMERR("register failed %d", r);
241 
242 	DMINFO("version " QL_VERSION " loaded");
243 
244 	return r;
245 }
246 
dm_ql_exit(void)247 static void __exit dm_ql_exit(void)
248 {
249 	int r = dm_unregister_path_selector(&ql_ps);
250 
251 	if (r < 0)
252 		DMERR("unregister failed %d", r);
253 }
254 
255 module_init(dm_ql_init);
256 module_exit(dm_ql_exit);
257 
258 MODULE_AUTHOR("Stefan Bader <Stefan.Bader at de.ibm.com>");
259 MODULE_DESCRIPTION(
260 	"(C) Copyright IBM Corp. 2004,2005   All Rights Reserved.\n"
261 	DM_NAME " path selector to balance the number of in-flight I/Os"
262 );
263 MODULE_LICENSE("GPL");
264