51d82290ad4c6d25ef2c52d599e3f88f6c96e934
[cascardo/linux.git] / drivers / md / dm-queue-length.c
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       1
27 #define QL_VERSION      "0.2.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
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
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
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
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
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
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         char dummy;
116
117         /*
118          * Arguments: [<repeat_count>]
119          *      <repeat_count>: The number of I/Os before switching path.
120          *                      If not given, default (QL_MIN_IO) is used.
121          */
122         if (argc > 1) {
123                 *error = "queue-length ps: incorrect number of arguments";
124                 return -EINVAL;
125         }
126
127         if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
128                 *error = "queue-length ps: invalid repeat count";
129                 return -EINVAL;
130         }
131
132         if (repeat_count > 1) {
133                 DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");
134                 repeat_count = 1;
135         }
136
137         /* Allocate the path information structure */
138         pi = kmalloc(sizeof(*pi), GFP_KERNEL);
139         if (!pi) {
140                 *error = "queue-length ps: Error allocating path information";
141                 return -ENOMEM;
142         }
143
144         pi->path = path;
145         pi->repeat_count = repeat_count;
146         atomic_set(&pi->qlen, 0);
147
148         path->pscontext = pi;
149
150         list_add_tail(&pi->list, &s->valid_paths);
151
152         return 0;
153 }
154
155 static void ql_fail_path(struct path_selector *ps, struct dm_path *path)
156 {
157         struct selector *s = ps->context;
158         struct path_info *pi = path->pscontext;
159
160         list_move(&pi->list, &s->failed_paths);
161 }
162
163 static int ql_reinstate_path(struct path_selector *ps, struct dm_path *path)
164 {
165         struct selector *s = ps->context;
166         struct path_info *pi = path->pscontext;
167
168         list_move_tail(&pi->list, &s->valid_paths);
169
170         return 0;
171 }
172
173 /*
174  * Select a path having the minimum number of in-flight I/Os
175  */
176 static struct dm_path *ql_select_path(struct path_selector *ps,
177                                       unsigned *repeat_count, size_t nr_bytes)
178 {
179         struct selector *s = ps->context;
180         struct path_info *pi = NULL, *best = NULL;
181
182         if (list_empty(&s->valid_paths))
183                 return NULL;
184
185         /* Change preferred (first in list) path to evenly balance. */
186         list_move_tail(s->valid_paths.next, &s->valid_paths);
187
188         list_for_each_entry(pi, &s->valid_paths, list) {
189                 if (!best ||
190                     (atomic_read(&pi->qlen) < atomic_read(&best->qlen)))
191                         best = pi;
192
193                 if (!atomic_read(&best->qlen))
194                         break;
195         }
196
197         if (!best)
198                 return NULL;
199
200         *repeat_count = best->repeat_count;
201
202         return best->path;
203 }
204
205 static int ql_start_io(struct path_selector *ps, struct dm_path *path,
206                        size_t nr_bytes)
207 {
208         struct path_info *pi = path->pscontext;
209
210         atomic_inc(&pi->qlen);
211
212         return 0;
213 }
214
215 static int ql_end_io(struct path_selector *ps, struct dm_path *path,
216                      size_t nr_bytes)
217 {
218         struct path_info *pi = path->pscontext;
219
220         atomic_dec(&pi->qlen);
221
222         return 0;
223 }
224
225 static struct path_selector_type ql_ps = {
226         .name           = "queue-length",
227         .module         = THIS_MODULE,
228         .table_args     = 1,
229         .info_args      = 1,
230         .create         = ql_create,
231         .destroy        = ql_destroy,
232         .status         = ql_status,
233         .add_path       = ql_add_path,
234         .fail_path      = ql_fail_path,
235         .reinstate_path = ql_reinstate_path,
236         .select_path    = ql_select_path,
237         .start_io       = ql_start_io,
238         .end_io         = ql_end_io,
239 };
240
241 static int __init dm_ql_init(void)
242 {
243         int r = dm_register_path_selector(&ql_ps);
244
245         if (r < 0)
246                 DMERR("register failed %d", r);
247
248         DMINFO("version " QL_VERSION " loaded");
249
250         return r;
251 }
252
253 static void __exit dm_ql_exit(void)
254 {
255         int r = dm_unregister_path_selector(&ql_ps);
256
257         if (r < 0)
258                 DMERR("unregister failed %d", r);
259 }
260
261 module_init(dm_ql_init);
262 module_exit(dm_ql_exit);
263
264 MODULE_AUTHOR("Stefan Bader <Stefan.Bader at de.ibm.com>");
265 MODULE_DESCRIPTION(
266         "(C) Copyright IBM Corp. 2004,2005   All Rights Reserved.\n"
267         DM_NAME " path selector to balance the number of in-flight I/Os"
268 );
269 MODULE_LICENSE("GPL");