Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / md / dm-least-pending.c
1 /*
2  * (C) Copyright 2008 Hewlett-Packard Development Company, L.P
3  *
4  * This file is released under the GPL.
5  */
6
7 #include "dm-path-selector.h"
8
9 #include <linux/slab.h>
10 #include <linux/module.h>
11
12 #define DM_MSG_PREFIX "multipath least-pending"
13
14 /*-----------------------------------------------------------------
15 * Path-handling code, paths are held in lists
16 *---------------------------------------------------------------*/
17 struct path_info {
18        struct list_head list;
19        struct dm_path *path;
20        unsigned repeat_count;
21        atomic_t io_count;
22 };
23
24 static void free_paths(struct list_head *paths)
25 {
26        struct path_info *pi, *next;
27
28        list_for_each_entry_safe(pi, next, paths, list) {
29                 list_del(&pi->list);
30                 kfree(pi);
31        }
32 }
33
34 /*-----------------------------------------------------------------
35  * Least-pending selector
36  *---------------------------------------------------------------*/
37
38 #define LPP_MIN_IO     1
39
40 struct selector {
41        struct list_head valid_paths;
42        struct list_head invalid_paths;
43 };
44
45 static struct selector *alloc_selector(void)
46 {
47        struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
48
49        if (s) {
50                 INIT_LIST_HEAD(&s->valid_paths);
51                 INIT_LIST_HEAD(&s->invalid_paths);
52        }
53
54        return s;
55 }
56
57 static int lpp_create(struct path_selector *ps, unsigned argc, char **argv)
58 {
59        struct selector *s;
60
61        s = alloc_selector();
62        if (!s)
63                 return -ENOMEM;
64
65        ps->context = s;
66        return 0;
67 }
68
69 static void lpp_destroy(struct path_selector *ps)
70 {
71        struct selector *s = ps->context;
72
73        free_paths(&s->valid_paths);
74        free_paths(&s->invalid_paths);
75        kfree(s);
76        ps->context = NULL;
77 }
78
79 static int lpp_status(struct path_selector *ps, struct dm_path *path,
80                         status_type_t type, char *result, unsigned int maxlen)
81 {
82        struct path_info *pi;
83        int sz = 0;
84
85        if (!path)
86                 switch (type) {
87                 case STATUSTYPE_INFO:
88                         DMEMIT("1 ");
89                 break;
90                 case STATUSTYPE_TABLE:
91                         DMEMIT("0 ");
92                 break;
93                 }
94        else {
95                 pi = path->pscontext;
96                 switch (type) {
97                 case STATUSTYPE_INFO:
98                         DMEMIT("%u:%u ", pi->repeat_count,
99                                          atomic_read(&pi->io_count));
100                 break;
101                 case STATUSTYPE_TABLE:
102                 break;
103                 }
104         }
105
106        return sz;
107 }
108
109 /*
110  * Called during initialisation to register each path with an
111  * optional repeat_count.
112  */
113 static int lpp_add_path(struct path_selector *ps, struct dm_path *path,
114                         int argc, char **argv, char **error)
115 {
116        struct selector *s = ps->context;
117        struct path_info *pi;
118        unsigned repeat_count = LPP_MIN_IO;
119
120         if (argc > 1) {
121                 *error = "least-pending ps: incorrect number of arguments";
122                 return -EINVAL;
123         }
124
125        /* First path argument is number of I/Os before switching path */
126        if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) {
127                 *error = "least-pending ps: invalid repeat count";
128                 return -EINVAL;
129        }
130
131        /* allocate the path */
132        pi = kmalloc(sizeof(*pi), GFP_KERNEL);
133        if (!pi) {
134                 *error = "least-pending ps: Error allocating path context";
135                 return -ENOMEM;
136        }
137
138        pi->path = path;
139        pi->repeat_count = repeat_count;
140        atomic_set(&pi->io_count, 0);
141
142        path->pscontext = pi;
143
144        list_add(&pi->list, &s->valid_paths);
145
146        return 0;
147 }
148
149 static void lpp_fail_path(struct path_selector *ps, struct dm_path *p)
150 {
151        struct selector *s = ps->context;
152        struct path_info *pi = p->pscontext;
153
154        if (!pi)
155         return;
156
157        atomic_set(&pi->io_count, 0);
158
159        list_move(&pi->list, &s->invalid_paths);
160 }
161
162 static int lpp_reinstate_path(struct path_selector *ps, struct dm_path *p)
163 {
164        struct selector *s = ps->context;
165        struct path_info *pi = p->pscontext;
166
167        if (!pi)
168         return 1;
169
170        list_move(&pi->list, &s->valid_paths);
171
172        return 0;
173 }
174
175 static struct dm_path *lpp_select_path(struct path_selector *ps,
176                                         unsigned *repeat_count,
177                                         size_t nr_bytes)
178 {
179        struct selector *s = ps->context;
180        struct path_info *pi, *next, *least_io_path = NULL;
181        struct list_head *paths;
182
183        if (list_empty(&s->valid_paths))
184                 return NULL;
185
186        paths = &s->valid_paths;
187
188        list_for_each_entry_safe(pi, next, paths, list) {
189                 if (!least_io_path || atomic_read(&least_io_path->io_count) < atomic_read(&pi->io_count))
190                         least_io_path = pi;
191                 if (!atomic_read(&least_io_path->io_count))
192                         break;
193        }
194
195        if (!least_io_path)
196                 return NULL;
197
198        atomic_inc(&least_io_path->io_count);
199        *repeat_count = least_io_path->repeat_count;
200
201        return least_io_path->path;
202 }
203
204 static int lpp_end_io(struct path_selector *ps, struct dm_path *path,
205                       size_t nr_bytes)
206 {
207        struct path_info *pi = NULL;
208
209        pi = path->pscontext;
210        if (!pi)
211         return 1;
212
213        atomic_dec(&pi->io_count);
214
215        return 0;
216 }
217
218 static struct path_selector_type lpp_ps = {
219        .name = "least-pending",
220        .module = THIS_MODULE,
221        .table_args = 1,
222        .info_args = 0,
223        .create = lpp_create,
224        .destroy = lpp_destroy,
225        .status = lpp_status,
226        .add_path = lpp_add_path,
227        .fail_path = lpp_fail_path,
228        .reinstate_path = lpp_reinstate_path,
229        .select_path = lpp_select_path,
230        .end_io = lpp_end_io,
231 };
232
233 static int __init dm_lpp_init(void)
234 {
235        int r = dm_register_path_selector(&lpp_ps);
236
237        if (r < 0)
238                 DMERR("register failed %d", r);
239
240        DMINFO("version 1.0.0 loaded");
241
242        return r;
243 }
244
245 static void __exit dm_lpp_exit(void)
246 {
247        int r = dm_unregister_path_selector(&lpp_ps);
248
249        if (r < 0)
250                 DMERR("unregister failed %d", r);
251 }
252
253 module_init(dm_lpp_init);
254 module_exit(dm_lpp_exit);
255
256 MODULE_DESCRIPTION(DM_NAME " least-pending multipath path selector");
257 MODULE_AUTHOR("Sakshi Chaitanya Veni <vsakshi@hp.com>");
258 MODULE_LICENSE("GPL");
259