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 18#define DM_MSG_PREFIX "multipath round-robin" 19 20/*----------------------------------------------------------------- 21 * Path-handling code, paths are held in lists 22 *---------------------------------------------------------------*/ 23struct path_info { 24 struct list_head list; 25 struct dm_path *path; 26 unsigned repeat_count; 27}; 28 29static void free_paths(struct list_head *paths) 30{ 31 struct path_info *pi, *next; 32 33 list_for_each_entry_safe(pi, next, paths, list) { 34 list_del(&pi->list); 35 kfree(pi); 36 } 37} 38 39/*----------------------------------------------------------------- 40 * Round-robin selector 41 *---------------------------------------------------------------*/ 42 43#define RR_MIN_IO 1000 44 45struct selector { 46 struct list_head valid_paths; 47 struct list_head invalid_paths; 48}; 49 50static struct selector *alloc_selector(void) 51{ 52 struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); 53 54 if (s) { 55 INIT_LIST_HEAD(&s->valid_paths); 56 INIT_LIST_HEAD(&s->invalid_paths); 57 } 58 59 return s; 60} 61 62static int rr_create(struct path_selector *ps, unsigned argc, char **argv) 63{ 64 struct selector *s; 65 66 s = alloc_selector(); 67 if (!s) 68 return -ENOMEM; 69 70 ps->context = s; 71 return 0; 72} 73 74static void rr_destroy(struct path_selector *ps) 75{ 76 struct selector *s = (struct selector *) ps->context; 77 78 free_paths(&s->valid_paths); 79 free_paths(&s->invalid_paths); 80 kfree(s); 81 ps->context = NULL; 82} 83 84static int rr_status(struct path_selector *ps, struct dm_path *path, 85 status_type_t type, char *result, unsigned int maxlen) 86{ 87 struct path_info *pi; 88 int sz = 0; 89 90 if (!path) 91 DMEMIT("0 "); 92 else { 93 switch(type) { 94 case STATUSTYPE_INFO: 95 break; 96 case STATUSTYPE_TABLE: 97 pi = path->pscontext; 98 DMEMIT("%u ", pi->repeat_count); 99 break; 100 } 101 } 102 103 return sz; 104} 105 106/* 107 * Called during initialisation to register each path with an 108 * optional repeat_count. 109 */ 110static int rr_add_path(struct path_selector *ps, struct dm_path *path, 111 int argc, char **argv, char **error) 112{ 113 struct selector *s = (struct selector *) ps->context; 114 struct path_info *pi; 115 unsigned repeat_count = RR_MIN_IO; 116 117 if (argc > 1) { 118 *error = "round-robin ps: incorrect number of arguments"; 119 return -EINVAL; 120 } 121 122 /* First path argument is number of I/Os before switching path */ 123 if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) { 124 *error = "round-robin ps: invalid repeat count"; 125 return -EINVAL; 126 } 127 128 /* allocate the path */ 129 pi = kmalloc(sizeof(*pi), GFP_KERNEL); 130 if (!pi) { 131 *error = "round-robin ps: Error allocating path context"; 132 return -ENOMEM; 133 } 134 135 pi->path = path; 136 pi->repeat_count = repeat_count; 137 138 path->pscontext = pi; 139 140 list_add_tail(&pi->list, &s->valid_paths); 141 142 return 0; 143} 144 145static void rr_fail_path(struct path_selector *ps, struct dm_path *p) 146{ 147 struct selector *s = (struct selector *) ps->context; 148 struct path_info *pi = p->pscontext; 149 150 list_move(&pi->list, &s->invalid_paths); 151} 152 153static int rr_reinstate_path(struct path_selector *ps, struct dm_path *p) 154{ 155 struct selector *s = (struct selector *) ps->context; 156 struct path_info *pi = p->pscontext; 157 158 list_move(&pi->list, &s->valid_paths); 159 160 return 0; 161} 162 163static struct dm_path *rr_select_path(struct path_selector *ps, 164 unsigned *repeat_count, size_t nr_bytes) 165{ 166 struct selector *s = (struct selector *) ps->context; 167 struct path_info *pi = NULL; 168 169 if (!list_empty(&s->valid_paths)) { 170 pi = list_entry(s->valid_paths.next, struct path_info, list); 171 list_move_tail(&pi->list, &s->valid_paths); 172 *repeat_count = pi->repeat_count; 173 } 174 175 return pi ? pi->path : NULL; 176} 177 178static struct path_selector_type rr_ps = { 179 .name = "round-robin", 180 .module = THIS_MODULE, 181 .table_args = 1, 182 .info_args = 0, 183 .create = rr_create, 184 .destroy = rr_destroy, 185 .status = rr_status, 186 .add_path = rr_add_path, 187 .fail_path = rr_fail_path, 188 .reinstate_path = rr_reinstate_path, 189 .select_path = rr_select_path, 190}; 191 192static int __init dm_rr_init(void) 193{ 194 int r = dm_register_path_selector(&rr_ps); 195 196 if (r < 0) 197 DMERR("register failed %d", r); 198 199 DMINFO("version 1.0.0 loaded"); 200 201 return r; 202} 203 204static void __exit dm_rr_exit(void) 205{ 206 int r = dm_unregister_path_selector(&rr_ps); 207 208 if (r < 0) 209 DMERR("unregister failed %d", r); 210} 211 212module_init(dm_rr_init); 213module_exit(dm_rr_exit); 214 215MODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector"); 216MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>"); 217MODULE_LICENSE("GPL"); 218