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 case STATUSTYPE_IMA: 105 *result = '\0'; 106 break; 107 } 108 } 109 110 return sz; 111 } 112 113 /* 114 * Called during initialisation to register each path with an 115 * optional repeat_count. 116 */ 117 static int rr_add_path(struct path_selector *ps, struct dm_path *path, 118 int argc, char **argv, char **error) 119 { 120 struct selector *s = ps->context; 121 struct path_info *pi; 122 unsigned repeat_count = RR_MIN_IO; 123 char dummy; 124 unsigned long flags; 125 126 if (argc > 1) { 127 *error = "round-robin ps: incorrect number of arguments"; 128 return -EINVAL; 129 } 130 131 /* First path argument is number of I/Os before switching path */ 132 if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 133 *error = "round-robin ps: invalid repeat count"; 134 return -EINVAL; 135 } 136 137 if (repeat_count > 1) { 138 DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead"); 139 repeat_count = 1; 140 } 141 142 /* allocate the path */ 143 pi = kmalloc(sizeof(*pi), GFP_KERNEL); 144 if (!pi) { 145 *error = "round-robin ps: Error allocating path context"; 146 return -ENOMEM; 147 } 148 149 pi->path = path; 150 pi->repeat_count = repeat_count; 151 152 path->pscontext = pi; 153 154 spin_lock_irqsave(&s->lock, flags); 155 list_add_tail(&pi->list, &s->valid_paths); 156 spin_unlock_irqrestore(&s->lock, flags); 157 158 return 0; 159 } 160 161 static void rr_fail_path(struct path_selector *ps, struct dm_path *p) 162 { 163 unsigned long flags; 164 struct selector *s = ps->context; 165 struct path_info *pi = p->pscontext; 166 167 spin_lock_irqsave(&s->lock, flags); 168 list_move(&pi->list, &s->invalid_paths); 169 spin_unlock_irqrestore(&s->lock, flags); 170 } 171 172 static int rr_reinstate_path(struct path_selector *ps, struct dm_path *p) 173 { 174 unsigned long flags; 175 struct selector *s = ps->context; 176 struct path_info *pi = p->pscontext; 177 178 spin_lock_irqsave(&s->lock, flags); 179 list_move(&pi->list, &s->valid_paths); 180 spin_unlock_irqrestore(&s->lock, flags); 181 182 return 0; 183 } 184 185 static struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes) 186 { 187 unsigned long flags; 188 struct selector *s = ps->context; 189 struct path_info *pi = NULL; 190 191 spin_lock_irqsave(&s->lock, flags); 192 if (!list_empty(&s->valid_paths)) { 193 pi = list_entry(s->valid_paths.next, struct path_info, list); 194 list_move_tail(&pi->list, &s->valid_paths); 195 } 196 spin_unlock_irqrestore(&s->lock, flags); 197 198 return pi ? pi->path : NULL; 199 } 200 201 static struct path_selector_type rr_ps = { 202 .name = "round-robin", 203 .module = THIS_MODULE, 204 .table_args = 1, 205 .info_args = 0, 206 .create = rr_create, 207 .destroy = rr_destroy, 208 .status = rr_status, 209 .add_path = rr_add_path, 210 .fail_path = rr_fail_path, 211 .reinstate_path = rr_reinstate_path, 212 .select_path = rr_select_path, 213 }; 214 215 static int __init dm_rr_init(void) 216 { 217 int r = dm_register_path_selector(&rr_ps); 218 219 if (r < 0) 220 DMERR("register failed %d", r); 221 222 DMINFO("version " RR_VERSION " loaded"); 223 224 return r; 225 } 226 227 static void __exit dm_rr_exit(void) 228 { 229 int r = dm_unregister_path_selector(&rr_ps); 230 231 if (r < 0) 232 DMERR("unregister failed %d", r); 233 } 234 235 module_init(dm_rr_init); 236 module_exit(dm_rr_exit); 237 238 MODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector"); 239 MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>"); 240 MODULE_LICENSE("GPL"); 241