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