Merge tag 'v3.3.7' into 3.3/master
[zen-stable.git] / drivers / md / dm-least-pending.c
blobf4e98b7fe9924a10c98f1648e4c9e4e0fa1e2f9b
1 /*
2 * (C) Copyright 2008 Hewlett-Packard Development Company, L.P
4 * This file is released under the GPL.
5 */
7 #include "dm-path-selector.h"
9 #include <linux/slab.h>
10 #include <linux/module.h>
12 #define DM_MSG_PREFIX "multipath least-pending"
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;
24 static void free_paths(struct list_head *paths)
26 struct path_info *pi, *next;
28 list_for_each_entry_safe(pi, next, paths, list) {
29 list_del(&pi->list);
30 kfree(pi);
34 /*-----------------------------------------------------------------
35 * Least-pending selector
36 *---------------------------------------------------------------*/
38 #define LPP_MIN_IO 1
40 struct selector {
41 struct list_head valid_paths;
42 struct list_head invalid_paths;
45 static struct selector *alloc_selector(void)
47 struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
49 if (s) {
50 INIT_LIST_HEAD(&s->valid_paths);
51 INIT_LIST_HEAD(&s->invalid_paths);
54 return s;
57 static int lpp_create(struct path_selector *ps, unsigned argc, char **argv)
59 struct selector *s;
61 s = alloc_selector();
62 if (!s)
63 return -ENOMEM;
65 ps->context = s;
66 return 0;
69 static void lpp_destroy(struct path_selector *ps)
71 struct selector *s = ps->context;
73 free_paths(&s->valid_paths);
74 free_paths(&s->invalid_paths);
75 kfree(s);
76 ps->context = NULL;
79 static int lpp_status(struct path_selector *ps, struct dm_path *path,
80 status_type_t type, char *result, unsigned int maxlen)
82 struct path_info *pi;
83 int sz = 0;
85 if (!path)
86 switch (type) {
87 case STATUSTYPE_INFO:
88 DMEMIT("1 ");
89 break;
90 case STATUSTYPE_TABLE:
91 DMEMIT("0 ");
92 break;
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;
106 return sz;
110 * Called during initialisation to register each path with an
111 * optional repeat_count.
113 static int lpp_add_path(struct path_selector *ps, struct dm_path *path,
114 int argc, char **argv, char **error)
116 struct selector *s = ps->context;
117 struct path_info *pi;
118 unsigned repeat_count = LPP_MIN_IO;
120 if (argc > 1) {
121 *error = "least-pending ps: incorrect number of arguments";
122 return -EINVAL;
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;
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;
138 pi->path = path;
139 pi->repeat_count = repeat_count;
140 atomic_set(&pi->io_count, 0);
142 path->pscontext = pi;
144 list_add(&pi->list, &s->valid_paths);
146 return 0;
149 static void lpp_fail_path(struct path_selector *ps, struct dm_path *p)
151 struct selector *s = ps->context;
152 struct path_info *pi = p->pscontext;
154 if (!pi)
155 return;
157 atomic_set(&pi->io_count, 0);
159 list_move(&pi->list, &s->invalid_paths);
162 static int lpp_reinstate_path(struct path_selector *ps, struct dm_path *p)
164 struct selector *s = ps->context;
165 struct path_info *pi = p->pscontext;
167 if (!pi)
168 return 1;
170 list_move(&pi->list, &s->valid_paths);
172 return 0;
175 static struct dm_path *lpp_select_path(struct path_selector *ps,
176 unsigned *repeat_count,
177 size_t nr_bytes)
179 struct selector *s = ps->context;
180 struct path_info *pi, *next, *least_io_path = NULL;
181 struct list_head *paths;
183 if (list_empty(&s->valid_paths))
184 return NULL;
186 paths = &s->valid_paths;
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;
195 if (!least_io_path)
196 return NULL;
198 atomic_inc(&least_io_path->io_count);
199 *repeat_count = least_io_path->repeat_count;
201 return least_io_path->path;
204 static int lpp_end_io(struct path_selector *ps, struct dm_path *path,
205 size_t nr_bytes)
207 struct path_info *pi = NULL;
209 pi = path->pscontext;
210 if (!pi)
211 return 1;
213 atomic_dec(&pi->io_count);
215 return 0;
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,
233 static int __init dm_lpp_init(void)
235 int r = dm_register_path_selector(&lpp_ps);
237 if (r < 0)
238 DMERR("register failed %d", r);
240 DMINFO("version 1.0.0 loaded");
242 return r;
245 static void __exit dm_lpp_exit(void)
247 int r = dm_unregister_path_selector(&lpp_ps);
249 if (r < 0)
250 DMERR("unregister failed %d", r);
253 module_init(dm_lpp_init);
254 module_exit(dm_lpp_exit);
256 MODULE_DESCRIPTION(DM_NAME " least-pending multipath path selector");
257 MODULE_AUTHOR("Sakshi Chaitanya Veni <vsakshi@hp.com>");
258 MODULE_LICENSE("GPL");