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 1 21 #define RR_VERSION "1.2.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 }; 51 52 static struct selector *alloc_selector(void) 53 { 54 struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); 55 56 if (s) { 57 INIT_LIST_HEAD(&s->valid_paths); 58 INIT_LIST_HEAD(&s->invalid_paths); 59 spin_lock_init(&s->lock); 60 } 61 62 return s; 63 } 64 65 static int rr_create(struct path_selector *ps, unsigned argc, char **argv) 66 { 67 struct selector *s; 68 69 s = alloc_selector(); 70 if (!s) 71 return -ENOMEM; 72 73 ps->context = s; 74 return 0; 75 } 76 77 static void rr_destroy(struct path_selector *ps) 78 { 79 struct selector *s = ps->context; 80 81 free_paths(&s->valid_paths); 82 free_paths(&s->invalid_paths); 83 kfree(s); 84 ps->context = NULL; 85 } 86 87 static int rr_status(struct path_selector *ps, struct dm_path *path, 88 status_type_t type, char *result, unsigned int maxlen) 89 { 90 struct path_info *pi; 91 int sz = 0; 92 93 if (!path) 94 DMEMIT("0 "); 95 else { 96 switch(type) { 97 case STATUSTYPE_INFO: 98 break; 99 case STATUSTYPE_TABLE: 100 pi = path->pscontext; 101 DMEMIT("%u ", pi->repeat_count); 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 rr_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 = RR_MIN_IO; 119 char dummy; 120 unsigned long flags; 121 122 if (argc > 1) { 123 *error = "round-robin ps: incorrect number of arguments"; 124 return -EINVAL; 125 } 126 127 /* First path argument is number of I/Os before switching path */ 128 if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 129 *error = "round-robin ps: invalid repeat count"; 130 return -EINVAL; 131 } 132 133 if (repeat_count > 1) { 134 DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead"); 135 repeat_count = 1; 136 } 137 138 /* allocate the path */ 139 pi = kmalloc(sizeof(*pi), GFP_KERNEL); 140 if (!pi) { 141 *error = "round-robin ps: Error allocating path context"; 142 return -ENOMEM; 143 } 144 145 pi->path = path; 146 pi->repeat_count = repeat_count; 147 148 path->pscontext = pi; 149 150 spin_lock_irqsave(&s->lock, flags); 151 list_add_tail(&pi->list, &s->valid_paths); 152 spin_unlock_irqrestore(&s->lock, flags); 153 154 return 0; 155 } 156 157 static void rr_fail_path(struct path_selector *ps, struct dm_path *p) 158 { 159 unsigned long flags; 160 struct selector *s = ps->context; 161 struct path_info *pi = p->pscontext; 162 163 spin_lock_irqsave(&s->lock, flags); 164 list_move(&pi->list, &s->invalid_paths); 165 spin_unlock_irqrestore(&s->lock, flags); 166 } 167 168 static int rr_reinstate_path(struct path_selector *ps, struct dm_path *p) 169 { 170 unsigned long flags; 171 struct selector *s = ps->context; 172 struct path_info *pi = p->pscontext; 173 174 spin_lock_irqsave(&s->lock, flags); 175 list_move(&pi->list, &s->valid_paths); 176 spin_unlock_irqrestore(&s->lock, flags); 177 178 return 0; 179 } 180 181 static struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes) 182 { 183 unsigned long flags; 184 struct selector *s = ps->context; 185 struct path_info *pi = NULL; 186 187 spin_lock_irqsave(&s->lock, flags); 188 if (!list_empty(&s->valid_paths)) { 189 pi = list_entry(s->valid_paths.next, struct path_info, list); 190 list_move_tail(&pi->list, &s->valid_paths); 191 } 192 spin_unlock_irqrestore(&s->lock, flags); 193 194 return pi ? pi->path : NULL; 195 } 196 197 static struct path_selector_type rr_ps = { 198 .name = "round-robin", 199 .module = THIS_MODULE, 200 .table_args = 1, 201 .info_args = 0, 202 .create = rr_create, 203 .destroy = rr_destroy, 204 .status = rr_status, 205 .add_path = rr_add_path, 206 .fail_path = rr_fail_path, 207 .reinstate_path = rr_reinstate_path, 208 .select_path = rr_select_path, 209 }; 210 211 static int __init dm_rr_init(void) 212 { 213 int r = dm_register_path_selector(&rr_ps); 214 215 if (r < 0) 216 DMERR("register failed %d", r); 217 218 DMINFO("version " RR_VERSION " loaded"); 219 220 return r; 221 } 222 223 static void __exit dm_rr_exit(void) 224 { 225 int r = dm_unregister_path_selector(&rr_ps); 226 227 if (r < 0) 228 DMERR("unregister failed %d", r); 229 } 230 231 module_init(dm_rr_init); 232 module_exit(dm_rr_exit); 233 234 MODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector"); 235 MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>"); 236 MODULE_LICENSE("GPL"); 237