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