2 * Copyright (C) 2003 Sistina Software.
3 * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
5 * Module Author: Heinz Mauelshagen
7 * This file is released under the GPL.
9 * Round-robin path selector.
12 #include <linux/device-mapper.h>
14 #include "dm-path-selector.h"
16 #include <linux/slab.h>
17 #include <linux/module.h>
19 #define DM_MSG_PREFIX "multipath round-robin"
20 #define RR_MIN_IO 1000
21 #define RR_VERSION "1.1.0"
23 /*-----------------------------------------------------------------
24 * Path-handling code, paths are held in lists
25 *---------------------------------------------------------------*/
27 struct list_head list
;
29 unsigned repeat_count
;
32 static void free_paths(struct list_head
*paths
)
34 struct path_info
*pi
, *next
;
36 list_for_each_entry_safe(pi
, next
, paths
, list
) {
42 /*-----------------------------------------------------------------
43 * Round-robin selector
44 *---------------------------------------------------------------*/
47 struct list_head valid_paths
;
48 struct list_head invalid_paths
;
50 struct dm_path
* __percpu
*current_path
;
51 struct percpu_counter repeat_count
;
54 static void set_percpu_current_path(struct selector
*s
, struct dm_path
*path
)
58 for_each_possible_cpu(cpu
)
59 *per_cpu_ptr(s
->current_path
, cpu
) = path
;
62 static struct selector
*alloc_selector(void)
64 struct selector
*s
= kmalloc(sizeof(*s
), GFP_KERNEL
);
69 INIT_LIST_HEAD(&s
->valid_paths
);
70 INIT_LIST_HEAD(&s
->invalid_paths
);
71 spin_lock_init(&s
->lock
);
73 s
->current_path
= alloc_percpu(struct dm_path
*);
75 goto out_current_path
;
76 set_percpu_current_path(s
, NULL
);
78 if (percpu_counter_init(&s
->repeat_count
, 0, GFP_KERNEL
))
79 goto out_repeat_count
;
84 free_percpu(s
->current_path
);
90 static int rr_create(struct path_selector
*ps
, unsigned argc
, char **argv
)
102 static void rr_destroy(struct path_selector
*ps
)
104 struct selector
*s
= ps
->context
;
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
);
114 static int rr_status(struct path_selector
*ps
, struct dm_path
*path
,
115 status_type_t type
, char *result
, unsigned int maxlen
)
117 struct path_info
*pi
;
124 case STATUSTYPE_INFO
:
126 case STATUSTYPE_TABLE
:
127 pi
= path
->pscontext
;
128 DMEMIT("%u ", pi
->repeat_count
);
137 * Called during initialisation to register each path with an
138 * optional repeat_count.
140 static int rr_add_path(struct path_selector
*ps
, struct dm_path
*path
,
141 int argc
, char **argv
, char **error
)
143 struct selector
*s
= ps
->context
;
144 struct path_info
*pi
;
145 unsigned repeat_count
= RR_MIN_IO
;
150 *error
= "round-robin ps: incorrect number of arguments";
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";
160 /* allocate the path */
161 pi
= kmalloc(sizeof(*pi
), GFP_KERNEL
);
163 *error
= "round-robin ps: Error allocating path context";
168 pi
->repeat_count
= repeat_count
;
170 path
->pscontext
= pi
;
172 spin_lock_irqsave(&s
->lock
, flags
);
173 list_add_tail(&pi
->list
, &s
->valid_paths
);
174 spin_unlock_irqrestore(&s
->lock
, flags
);
179 static void rr_fail_path(struct path_selector
*ps
, struct dm_path
*p
)
182 struct selector
*s
= ps
->context
;
183 struct path_info
*pi
= p
->pscontext
;
185 spin_lock_irqsave(&s
->lock
, flags
);
186 if (p
== *this_cpu_ptr(s
->current_path
))
187 set_percpu_current_path(s
, NULL
);
189 list_move(&pi
->list
, &s
->invalid_paths
);
190 spin_unlock_irqrestore(&s
->lock
, flags
);
193 static int rr_reinstate_path(struct path_selector
*ps
, struct dm_path
*p
)
196 struct selector
*s
= ps
->context
;
197 struct path_info
*pi
= p
->pscontext
;
199 spin_lock_irqsave(&s
->lock
, flags
);
200 list_move(&pi
->list
, &s
->valid_paths
);
201 spin_unlock_irqrestore(&s
->lock
, flags
);
206 static struct dm_path
*rr_select_path(struct path_selector
*ps
, size_t nr_bytes
)
209 struct selector
*s
= ps
->context
;
210 struct path_info
*pi
= NULL
;
211 struct dm_path
*current_path
= NULL
;
213 local_irq_save(flags
);
214 current_path
= *this_cpu_ptr(s
->current_path
);
216 percpu_counter_dec(&s
->repeat_count
);
217 if (percpu_counter_read_positive(&s
->repeat_count
) > 0) {
218 local_irq_restore(flags
);
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
;
231 spin_unlock_irqrestore(&s
->lock
, flags
);
236 static struct path_selector_type rr_ps
= {
237 .name
= "round-robin",
238 .module
= THIS_MODULE
,
242 .destroy
= rr_destroy
,
244 .add_path
= rr_add_path
,
245 .fail_path
= rr_fail_path
,
246 .reinstate_path
= rr_reinstate_path
,
247 .select_path
= rr_select_path
,
250 static int __init
dm_rr_init(void)
252 int r
= dm_register_path_selector(&rr_ps
);
255 DMERR("register failed %d", r
);
257 DMINFO("version " RR_VERSION
" loaded");
262 static void __exit
dm_rr_exit(void)
264 int r
= dm_unregister_path_selector(&rr_ps
);
267 DMERR("unregister failed %d", r
);
270 module_init(dm_rr_init
);
271 module_exit(dm_rr_exit
);
273 MODULE_DESCRIPTION(DM_NAME
" round-robin multipath path selector");
274 MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
275 MODULE_LICENSE("GPL");