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