]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/md/dm-round-robin.c
dm round robin: do not use this_cpu_ptr() without having preemption disabled
[karo-tx-linux.git] / drivers / md / dm-round-robin.c
1 /*
2  * Copyright (C) 2003 Sistina Software.
3  * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
4  *
5  * Module Author: Heinz Mauelshagen
6  *
7  * This file is released under the GPL.
8  *
9  * Round-robin path selector.
10  */
11
12 #include <linux/device-mapper.h>
13
14 #include "dm-path-selector.h"
15
16 #include <linux/slab.h>
17 #include <linux/module.h>
18
19 #define DM_MSG_PREFIX "multipath round-robin"
20 #define RR_MIN_IO     1000
21 #define RR_VERSION    "1.1.0"
22
23 /*-----------------------------------------------------------------
24  * Path-handling code, paths are held in lists
25  *---------------------------------------------------------------*/
26 struct path_info {
27         struct list_head list;
28         struct dm_path *path;
29         unsigned repeat_count;
30 };
31
32 static void free_paths(struct list_head *paths)
33 {
34         struct path_info *pi, *next;
35
36         list_for_each_entry_safe(pi, next, paths, list) {
37                 list_del(&pi->list);
38                 kfree(pi);
39         }
40 }
41
42 /*-----------------------------------------------------------------
43  * Round-robin selector
44  *---------------------------------------------------------------*/
45
46 struct selector {
47         struct list_head valid_paths;
48         struct list_head invalid_paths;
49         spinlock_t lock;
50         struct dm_path * __percpu *current_path;
51         struct percpu_counter repeat_count;
52 };
53
54 static void set_percpu_current_path(struct selector *s, struct dm_path *path)
55 {
56         int cpu;
57
58         for_each_possible_cpu(cpu)
59                 *per_cpu_ptr(s->current_path, cpu) = path;
60 }
61
62 static struct selector *alloc_selector(void)
63 {
64         struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
65
66         if (!s)
67                 return NULL;
68
69         INIT_LIST_HEAD(&s->valid_paths);
70         INIT_LIST_HEAD(&s->invalid_paths);
71         spin_lock_init(&s->lock);
72
73         s->current_path = alloc_percpu(struct dm_path *);
74         if (!s->current_path)
75                 goto out_current_path;
76         set_percpu_current_path(s, NULL);
77
78         if (percpu_counter_init(&s->repeat_count, 0, GFP_KERNEL))
79                 goto out_repeat_count;
80
81         return s;
82
83 out_repeat_count:
84         free_percpu(s->current_path);
85 out_current_path:
86         kfree(s);
87         return NULL;;
88 }
89
90 static int rr_create(struct path_selector *ps, unsigned argc, char **argv)
91 {
92         struct selector *s;
93
94         s = alloc_selector();
95         if (!s)
96                 return -ENOMEM;
97
98         ps->context = s;
99         return 0;
100 }
101
102 static void rr_destroy(struct path_selector *ps)
103 {
104         struct selector *s = ps->context;
105
106         free_paths(&s->valid_paths);
107         free_paths(&s->invalid_paths);
108         free_percpu(s->current_path);
109         percpu_counter_destroy(&s->repeat_count);
110         kfree(s);
111         ps->context = NULL;
112 }
113
114 static int rr_status(struct path_selector *ps, struct dm_path *path,
115                      status_type_t type, char *result, unsigned int maxlen)
116 {
117         struct path_info *pi;
118         int sz = 0;
119
120         if (!path)
121                 DMEMIT("0 ");
122         else {
123                 switch(type) {
124                 case STATUSTYPE_INFO:
125                         break;
126                 case STATUSTYPE_TABLE:
127                         pi = path->pscontext;
128                         DMEMIT("%u ", pi->repeat_count);
129                         break;
130                 }
131         }
132
133         return sz;
134 }
135
136 /*
137  * Called during initialisation to register each path with an
138  * optional repeat_count.
139  */
140 static int rr_add_path(struct path_selector *ps, struct dm_path *path,
141                        int argc, char **argv, char **error)
142 {
143         struct selector *s = ps->context;
144         struct path_info *pi;
145         unsigned repeat_count = RR_MIN_IO;
146         char dummy;
147         unsigned long flags;
148
149         if (argc > 1) {
150                 *error = "round-robin ps: incorrect number of arguments";
151                 return -EINVAL;
152         }
153
154         /* First path argument is number of I/Os before switching path */
155         if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
156                 *error = "round-robin ps: invalid repeat count";
157                 return -EINVAL;
158         }
159
160         /* allocate the path */
161         pi = kmalloc(sizeof(*pi), GFP_KERNEL);
162         if (!pi) {
163                 *error = "round-robin ps: Error allocating path context";
164                 return -ENOMEM;
165         }
166
167         pi->path = path;
168         pi->repeat_count = repeat_count;
169
170         path->pscontext = pi;
171
172         spin_lock_irqsave(&s->lock, flags);
173         list_add_tail(&pi->list, &s->valid_paths);
174         spin_unlock_irqrestore(&s->lock, flags);
175
176         return 0;
177 }
178
179 static void rr_fail_path(struct path_selector *ps, struct dm_path *p)
180 {
181         unsigned long flags;
182         struct selector *s = ps->context;
183         struct path_info *pi = p->pscontext;
184
185         spin_lock_irqsave(&s->lock, flags);
186         if (p == *this_cpu_ptr(s->current_path))
187                 set_percpu_current_path(s, NULL);
188
189         list_move(&pi->list, &s->invalid_paths);
190         spin_unlock_irqrestore(&s->lock, flags);
191 }
192
193 static int rr_reinstate_path(struct path_selector *ps, struct dm_path *p)
194 {
195         unsigned long flags;
196         struct selector *s = ps->context;
197         struct path_info *pi = p->pscontext;
198
199         spin_lock_irqsave(&s->lock, flags);
200         list_move(&pi->list, &s->valid_paths);
201         spin_unlock_irqrestore(&s->lock, flags);
202
203         return 0;
204 }
205
206 static struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes)
207 {
208         unsigned long flags;
209         struct selector *s = ps->context;
210         struct path_info *pi = NULL;
211         struct dm_path *current_path = NULL;
212
213         local_irq_save(flags);
214         current_path = *this_cpu_ptr(s->current_path);
215         if (current_path) {
216                 percpu_counter_dec(&s->repeat_count);
217                 if (percpu_counter_read_positive(&s->repeat_count) > 0) {
218                         local_irq_restore(flags);
219                         return current_path;
220                 }
221         }
222
223         spin_lock(&s->lock);
224         if (!list_empty(&s->valid_paths)) {
225                 pi = list_entry(s->valid_paths.next, struct path_info, list);
226                 list_move_tail(&pi->list, &s->valid_paths);
227                 percpu_counter_set(&s->repeat_count, pi->repeat_count);
228                 set_percpu_current_path(s, pi->path);
229                 current_path = pi->path;
230         }
231         spin_unlock_irqrestore(&s->lock, flags);
232
233         return current_path;
234 }
235
236 static struct path_selector_type rr_ps = {
237         .name = "round-robin",
238         .module = THIS_MODULE,
239         .table_args = 1,
240         .info_args = 0,
241         .create = rr_create,
242         .destroy = rr_destroy,
243         .status = rr_status,
244         .add_path = rr_add_path,
245         .fail_path = rr_fail_path,
246         .reinstate_path = rr_reinstate_path,
247         .select_path = rr_select_path,
248 };
249
250 static int __init dm_rr_init(void)
251 {
252         int r = dm_register_path_selector(&rr_ps);
253
254         if (r < 0)
255                 DMERR("register failed %d", r);
256
257         DMINFO("version " RR_VERSION " loaded");
258
259         return r;
260 }
261
262 static void __exit dm_rr_exit(void)
263 {
264         int r = dm_unregister_path_selector(&rr_ps);
265
266         if (r < 0)
267                 DMERR("unregister failed %d", r);
268 }
269
270 module_init(dm_rr_init);
271 module_exit(dm_rr_exit);
272
273 MODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector");
274 MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
275 MODULE_LICENSE("GPL");