18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2003 Sistina Software.
38c2ecf20Sopenharmony_ci * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Module Author: Heinz Mauelshagen
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is released under the GPL.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Round-robin path selector.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/device-mapper.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "dm-path-selector.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "multipath round-robin"
208c2ecf20Sopenharmony_ci#define RR_MIN_IO     1
218c2ecf20Sopenharmony_ci#define RR_VERSION    "1.2.0"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*-----------------------------------------------------------------
248c2ecf20Sopenharmony_ci * Path-handling code, paths are held in lists
258c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/
268c2ecf20Sopenharmony_cistruct path_info {
278c2ecf20Sopenharmony_ci	struct list_head list;
288c2ecf20Sopenharmony_ci	struct dm_path *path;
298c2ecf20Sopenharmony_ci	unsigned repeat_count;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void free_paths(struct list_head *paths)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct path_info *pi, *next;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pi, next, paths, list) {
378c2ecf20Sopenharmony_ci		list_del(&pi->list);
388c2ecf20Sopenharmony_ci		kfree(pi);
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*-----------------------------------------------------------------
438c2ecf20Sopenharmony_ci * Round-robin selector
448c2ecf20Sopenharmony_ci *---------------------------------------------------------------*/
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct selector {
478c2ecf20Sopenharmony_ci	struct list_head valid_paths;
488c2ecf20Sopenharmony_ci	struct list_head invalid_paths;
498c2ecf20Sopenharmony_ci	spinlock_t lock;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic struct selector *alloc_selector(void)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (s) {
578c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&s->valid_paths);
588c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&s->invalid_paths);
598c2ecf20Sopenharmony_ci		spin_lock_init(&s->lock);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return s;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int rr_create(struct path_selector *ps, unsigned argc, char **argv)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct selector *s;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	s = alloc_selector();
708c2ecf20Sopenharmony_ci	if (!s)
718c2ecf20Sopenharmony_ci		return -ENOMEM;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ps->context = s;
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void rr_destroy(struct path_selector *ps)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct selector *s = ps->context;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	free_paths(&s->valid_paths);
828c2ecf20Sopenharmony_ci	free_paths(&s->invalid_paths);
838c2ecf20Sopenharmony_ci	kfree(s);
848c2ecf20Sopenharmony_ci	ps->context = NULL;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int rr_status(struct path_selector *ps, struct dm_path *path,
888c2ecf20Sopenharmony_ci		     status_type_t type, char *result, unsigned int maxlen)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct path_info *pi;
918c2ecf20Sopenharmony_ci	int sz = 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (!path)
948c2ecf20Sopenharmony_ci		DMEMIT("0 ");
958c2ecf20Sopenharmony_ci	else {
968c2ecf20Sopenharmony_ci		switch(type) {
978c2ecf20Sopenharmony_ci		case STATUSTYPE_INFO:
988c2ecf20Sopenharmony_ci			break;
998c2ecf20Sopenharmony_ci		case STATUSTYPE_TABLE:
1008c2ecf20Sopenharmony_ci			pi = path->pscontext;
1018c2ecf20Sopenharmony_ci			DMEMIT("%u ", pi->repeat_count);
1028c2ecf20Sopenharmony_ci			break;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return sz;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Called during initialisation to register each path with an
1118c2ecf20Sopenharmony_ci * optional repeat_count.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic int rr_add_path(struct path_selector *ps, struct dm_path *path,
1148c2ecf20Sopenharmony_ci		       int argc, char **argv, char **error)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct selector *s = ps->context;
1178c2ecf20Sopenharmony_ci	struct path_info *pi;
1188c2ecf20Sopenharmony_ci	unsigned repeat_count = RR_MIN_IO;
1198c2ecf20Sopenharmony_ci	char dummy;
1208c2ecf20Sopenharmony_ci	unsigned long flags;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (argc > 1) {
1238c2ecf20Sopenharmony_ci		*error = "round-robin ps: incorrect number of arguments";
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* First path argument is number of I/Os before switching path */
1288c2ecf20Sopenharmony_ci	if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
1298c2ecf20Sopenharmony_ci		*error = "round-robin ps: invalid repeat count";
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (repeat_count > 1) {
1348c2ecf20Sopenharmony_ci		DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");
1358c2ecf20Sopenharmony_ci		repeat_count = 1;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* allocate the path */
1398c2ecf20Sopenharmony_ci	pi = kmalloc(sizeof(*pi), GFP_KERNEL);
1408c2ecf20Sopenharmony_ci	if (!pi) {
1418c2ecf20Sopenharmony_ci		*error = "round-robin ps: Error allocating path context";
1428c2ecf20Sopenharmony_ci		return -ENOMEM;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	pi->path = path;
1468c2ecf20Sopenharmony_ci	pi->repeat_count = repeat_count;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	path->pscontext = pi;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
1518c2ecf20Sopenharmony_ci	list_add_tail(&pi->list, &s->valid_paths);
1528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return 0;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void rr_fail_path(struct path_selector *ps, struct dm_path *p)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	unsigned long flags;
1608c2ecf20Sopenharmony_ci	struct selector *s = ps->context;
1618c2ecf20Sopenharmony_ci	struct path_info *pi = p->pscontext;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
1648c2ecf20Sopenharmony_ci	list_move(&pi->list, &s->invalid_paths);
1658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int rr_reinstate_path(struct path_selector *ps, struct dm_path *p)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	unsigned long flags;
1718c2ecf20Sopenharmony_ci	struct selector *s = ps->context;
1728c2ecf20Sopenharmony_ci	struct path_info *pi = p->pscontext;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
1758c2ecf20Sopenharmony_ci	list_move(&pi->list, &s->valid_paths);
1768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	unsigned long flags;
1848c2ecf20Sopenharmony_ci	struct selector *s = ps->context;
1858c2ecf20Sopenharmony_ci	struct path_info *pi = NULL;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
1888c2ecf20Sopenharmony_ci	if (!list_empty(&s->valid_paths)) {
1898c2ecf20Sopenharmony_ci		pi = list_entry(s->valid_paths.next, struct path_info, list);
1908c2ecf20Sopenharmony_ci		list_move_tail(&pi->list, &s->valid_paths);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return pi ? pi->path : NULL;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic struct path_selector_type rr_ps = {
1988c2ecf20Sopenharmony_ci	.name = "round-robin",
1998c2ecf20Sopenharmony_ci	.module = THIS_MODULE,
2008c2ecf20Sopenharmony_ci	.table_args = 1,
2018c2ecf20Sopenharmony_ci	.info_args = 0,
2028c2ecf20Sopenharmony_ci	.create = rr_create,
2038c2ecf20Sopenharmony_ci	.destroy = rr_destroy,
2048c2ecf20Sopenharmony_ci	.status = rr_status,
2058c2ecf20Sopenharmony_ci	.add_path = rr_add_path,
2068c2ecf20Sopenharmony_ci	.fail_path = rr_fail_path,
2078c2ecf20Sopenharmony_ci	.reinstate_path = rr_reinstate_path,
2088c2ecf20Sopenharmony_ci	.select_path = rr_select_path,
2098c2ecf20Sopenharmony_ci};
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic int __init dm_rr_init(void)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	int r = dm_register_path_selector(&rr_ps);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (r < 0)
2168c2ecf20Sopenharmony_ci		DMERR("register failed %d", r);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	DMINFO("version " RR_VERSION " loaded");
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return r;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void __exit dm_rr_exit(void)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int r = dm_unregister_path_selector(&rr_ps);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (r < 0)
2288c2ecf20Sopenharmony_ci		DMERR("unregister failed %d", r);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cimodule_init(dm_rr_init);
2328c2ecf20Sopenharmony_cimodule_exit(dm_rr_exit);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector");
2358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
2368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
237