245 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2003 Sistina Software.4 * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.5 *6 * Module Author: Heinz Mauelshagen7 *8 * This file is released under the GPL.9 *10 * Round-robin path selector.11 */12 13#include <linux/device-mapper.h>14 15#include "dm-path-selector.h"16 17#include <linux/slab.h>18#include <linux/module.h>19 20#define DM_MSG_PREFIX "multipath round-robin"21#define RR_MIN_IO 122#define RR_VERSION "1.2.0"23 24/*25 *---------------------------------------------------------------26 * Path-handling code, paths are held in lists27 *---------------------------------------------------------------28 */29struct path_info {30 struct list_head list;31 struct dm_path *path;32 unsigned int repeat_count;33};34 35static void free_paths(struct list_head *paths)36{37 struct path_info *pi, *next;38 39 list_for_each_entry_safe(pi, next, paths, list) {40 list_del(&pi->list);41 kfree(pi);42 }43}44 45/*46 *---------------------------------------------------------------47 * Round-robin selector48 *---------------------------------------------------------------49 */50struct selector {51 struct list_head valid_paths;52 struct list_head invalid_paths;53 spinlock_t lock;54};55 56static struct selector *alloc_selector(void)57{58 struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);59 60 if (s) {61 INIT_LIST_HEAD(&s->valid_paths);62 INIT_LIST_HEAD(&s->invalid_paths);63 spin_lock_init(&s->lock);64 }65 66 return s;67}68 69static int rr_create(struct path_selector *ps, unsigned int argc, char **argv)70{71 struct selector *s;72 73 s = alloc_selector();74 if (!s)75 return -ENOMEM;76 77 ps->context = s;78 return 0;79}80 81static void rr_destroy(struct path_selector *ps)82{83 struct selector *s = ps->context;84 85 free_paths(&s->valid_paths);86 free_paths(&s->invalid_paths);87 kfree(s);88 ps->context = NULL;89}90 91static int rr_status(struct path_selector *ps, struct dm_path *path,92 status_type_t type, char *result, unsigned int maxlen)93{94 struct path_info *pi;95 int sz = 0;96 97 if (!path)98 DMEMIT("0 ");99 else {100 switch (type) {101 case STATUSTYPE_INFO:102 break;103 case STATUSTYPE_TABLE:104 pi = path->pscontext;105 DMEMIT("%u ", pi->repeat_count);106 break;107 108 case STATUSTYPE_IMA:109 *result = '\0';110 break;111 }112 }113 114 return sz;115}116 117/*118 * Called during initialisation to register each path with an119 * optional repeat_count.120 */121static int rr_add_path(struct path_selector *ps, struct dm_path *path,122 int argc, char **argv, char **error)123{124 struct selector *s = ps->context;125 struct path_info *pi;126 unsigned int repeat_count = RR_MIN_IO;127 char dummy;128 unsigned long flags;129 130 if (argc > 1) {131 *error = "round-robin ps: incorrect number of arguments";132 return -EINVAL;133 }134 135 /* First path argument is number of I/Os before switching path */136 if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {137 *error = "round-robin ps: invalid repeat count";138 return -EINVAL;139 }140 141 if (repeat_count > 1) {142 DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");143 repeat_count = 1;144 }145 146 /* allocate the path */147 pi = kmalloc(sizeof(*pi), GFP_KERNEL);148 if (!pi) {149 *error = "round-robin ps: Error allocating path context";150 return -ENOMEM;151 }152 153 pi->path = path;154 pi->repeat_count = repeat_count;155 156 path->pscontext = pi;157 158 spin_lock_irqsave(&s->lock, flags);159 list_add_tail(&pi->list, &s->valid_paths);160 spin_unlock_irqrestore(&s->lock, flags);161 162 return 0;163}164 165static void rr_fail_path(struct path_selector *ps, struct dm_path *p)166{167 unsigned long flags;168 struct selector *s = ps->context;169 struct path_info *pi = p->pscontext;170 171 spin_lock_irqsave(&s->lock, flags);172 list_move(&pi->list, &s->invalid_paths);173 spin_unlock_irqrestore(&s->lock, flags);174}175 176static int rr_reinstate_path(struct path_selector *ps, struct dm_path *p)177{178 unsigned long flags;179 struct selector *s = ps->context;180 struct path_info *pi = p->pscontext;181 182 spin_lock_irqsave(&s->lock, flags);183 list_move(&pi->list, &s->valid_paths);184 spin_unlock_irqrestore(&s->lock, flags);185 186 return 0;187}188 189static struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes)190{191 unsigned long flags;192 struct selector *s = ps->context;193 struct path_info *pi = NULL;194 195 spin_lock_irqsave(&s->lock, flags);196 if (!list_empty(&s->valid_paths)) {197 pi = list_entry(s->valid_paths.next, struct path_info, list);198 list_move_tail(&pi->list, &s->valid_paths);199 }200 spin_unlock_irqrestore(&s->lock, flags);201 202 return pi ? pi->path : NULL;203}204 205static struct path_selector_type rr_ps = {206 .name = "round-robin",207 .module = THIS_MODULE,208 .table_args = 1,209 .info_args = 0,210 .create = rr_create,211 .destroy = rr_destroy,212 .status = rr_status,213 .add_path = rr_add_path,214 .fail_path = rr_fail_path,215 .reinstate_path = rr_reinstate_path,216 .select_path = rr_select_path,217};218 219static int __init dm_rr_init(void)220{221 int r = dm_register_path_selector(&rr_ps);222 223 if (r < 0)224 DMERR("register failed %d", r);225 226 DMINFO("version " RR_VERSION " loaded");227 228 return r;229}230 231static void __exit dm_rr_exit(void)232{233 int r = dm_unregister_path_selector(&rr_ps);234 235 if (r < 0)236 DMERR("unregister failed %d", r);237}238 239module_init(dm_rr_init);240module_exit(dm_rr_exit);241 242MODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector");243MODULE_AUTHOR("Sistina Software <dm-devel@lists.linux.dev>");244MODULE_LICENSE("GPL");245