162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2003 Sistina Software.
462306a36Sopenharmony_ci * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Module Author: Heinz Mauelshagen
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file is released under the GPL.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Round-robin path selector.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/device-mapper.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "dm-path-selector.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define DM_MSG_PREFIX "multipath round-robin"
2162306a36Sopenharmony_ci#define RR_MIN_IO     1
2262306a36Sopenharmony_ci#define RR_VERSION    "1.2.0"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci *---------------------------------------------------------------
2662306a36Sopenharmony_ci * Path-handling code, paths are held in lists
2762306a36Sopenharmony_ci *---------------------------------------------------------------
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistruct path_info {
3062306a36Sopenharmony_ci	struct list_head list;
3162306a36Sopenharmony_ci	struct dm_path *path;
3262306a36Sopenharmony_ci	unsigned int repeat_count;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void free_paths(struct list_head *paths)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct path_info *pi, *next;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	list_for_each_entry_safe(pi, next, paths, list) {
4062306a36Sopenharmony_ci		list_del(&pi->list);
4162306a36Sopenharmony_ci		kfree(pi);
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci *---------------------------------------------------------------
4762306a36Sopenharmony_ci * Round-robin selector
4862306a36Sopenharmony_ci *---------------------------------------------------------------
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistruct selector {
5162306a36Sopenharmony_ci	struct list_head valid_paths;
5262306a36Sopenharmony_ci	struct list_head invalid_paths;
5362306a36Sopenharmony_ci	spinlock_t lock;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic struct selector *alloc_selector(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (s) {
6162306a36Sopenharmony_ci		INIT_LIST_HEAD(&s->valid_paths);
6262306a36Sopenharmony_ci		INIT_LIST_HEAD(&s->invalid_paths);
6362306a36Sopenharmony_ci		spin_lock_init(&s->lock);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return s;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int rr_create(struct path_selector *ps, unsigned int argc, char **argv)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct selector *s;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	s = alloc_selector();
7462306a36Sopenharmony_ci	if (!s)
7562306a36Sopenharmony_ci		return -ENOMEM;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	ps->context = s;
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void rr_destroy(struct path_selector *ps)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct selector *s = ps->context;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	free_paths(&s->valid_paths);
8662306a36Sopenharmony_ci	free_paths(&s->invalid_paths);
8762306a36Sopenharmony_ci	kfree(s);
8862306a36Sopenharmony_ci	ps->context = NULL;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int rr_status(struct path_selector *ps, struct dm_path *path,
9262306a36Sopenharmony_ci		     status_type_t type, char *result, unsigned int maxlen)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct path_info *pi;
9562306a36Sopenharmony_ci	int sz = 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!path)
9862306a36Sopenharmony_ci		DMEMIT("0 ");
9962306a36Sopenharmony_ci	else {
10062306a36Sopenharmony_ci		switch (type) {
10162306a36Sopenharmony_ci		case STATUSTYPE_INFO:
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci		case STATUSTYPE_TABLE:
10462306a36Sopenharmony_ci			pi = path->pscontext;
10562306a36Sopenharmony_ci			DMEMIT("%u ", pi->repeat_count);
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		case STATUSTYPE_IMA:
10962306a36Sopenharmony_ci			*result = '\0';
11062306a36Sopenharmony_ci			break;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return sz;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * Called during initialisation to register each path with an
11962306a36Sopenharmony_ci * optional repeat_count.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic int rr_add_path(struct path_selector *ps, struct dm_path *path,
12262306a36Sopenharmony_ci		       int argc, char **argv, char **error)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct selector *s = ps->context;
12562306a36Sopenharmony_ci	struct path_info *pi;
12662306a36Sopenharmony_ci	unsigned int repeat_count = RR_MIN_IO;
12762306a36Sopenharmony_ci	char dummy;
12862306a36Sopenharmony_ci	unsigned long flags;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (argc > 1) {
13162306a36Sopenharmony_ci		*error = "round-robin ps: incorrect number of arguments";
13262306a36Sopenharmony_ci		return -EINVAL;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* First path argument is number of I/Os before switching path */
13662306a36Sopenharmony_ci	if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) {
13762306a36Sopenharmony_ci		*error = "round-robin ps: invalid repeat count";
13862306a36Sopenharmony_ci		return -EINVAL;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (repeat_count > 1) {
14262306a36Sopenharmony_ci		DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead");
14362306a36Sopenharmony_ci		repeat_count = 1;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* allocate the path */
14762306a36Sopenharmony_ci	pi = kmalloc(sizeof(*pi), GFP_KERNEL);
14862306a36Sopenharmony_ci	if (!pi) {
14962306a36Sopenharmony_ci		*error = "round-robin ps: Error allocating path context";
15062306a36Sopenharmony_ci		return -ENOMEM;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	pi->path = path;
15462306a36Sopenharmony_ci	pi->repeat_count = repeat_count;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	path->pscontext = pi;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
15962306a36Sopenharmony_ci	list_add_tail(&pi->list, &s->valid_paths);
16062306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void rr_fail_path(struct path_selector *ps, struct dm_path *p)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	unsigned long flags;
16862306a36Sopenharmony_ci	struct selector *s = ps->context;
16962306a36Sopenharmony_ci	struct path_info *pi = p->pscontext;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
17262306a36Sopenharmony_ci	list_move(&pi->list, &s->invalid_paths);
17362306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic int rr_reinstate_path(struct path_selector *ps, struct dm_path *p)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	unsigned long flags;
17962306a36Sopenharmony_ci	struct selector *s = ps->context;
18062306a36Sopenharmony_ci	struct path_info *pi = p->pscontext;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
18362306a36Sopenharmony_ci	list_move(&pi->list, &s->valid_paths);
18462306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic struct dm_path *rr_select_path(struct path_selector *ps, size_t nr_bytes)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	unsigned long flags;
19262306a36Sopenharmony_ci	struct selector *s = ps->context;
19362306a36Sopenharmony_ci	struct path_info *pi = NULL;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
19662306a36Sopenharmony_ci	if (!list_empty(&s->valid_paths)) {
19762306a36Sopenharmony_ci		pi = list_entry(s->valid_paths.next, struct path_info, list);
19862306a36Sopenharmony_ci		list_move_tail(&pi->list, &s->valid_paths);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return pi ? pi->path : NULL;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic struct path_selector_type rr_ps = {
20662306a36Sopenharmony_ci	.name = "round-robin",
20762306a36Sopenharmony_ci	.module = THIS_MODULE,
20862306a36Sopenharmony_ci	.table_args = 1,
20962306a36Sopenharmony_ci	.info_args = 0,
21062306a36Sopenharmony_ci	.create = rr_create,
21162306a36Sopenharmony_ci	.destroy = rr_destroy,
21262306a36Sopenharmony_ci	.status = rr_status,
21362306a36Sopenharmony_ci	.add_path = rr_add_path,
21462306a36Sopenharmony_ci	.fail_path = rr_fail_path,
21562306a36Sopenharmony_ci	.reinstate_path = rr_reinstate_path,
21662306a36Sopenharmony_ci	.select_path = rr_select_path,
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int __init dm_rr_init(void)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	int r = dm_register_path_selector(&rr_ps);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (r < 0)
22462306a36Sopenharmony_ci		DMERR("register failed %d", r);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	DMINFO("version " RR_VERSION " loaded");
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return r;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void __exit dm_rr_exit(void)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int r = dm_unregister_path_selector(&rr_ps);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (r < 0)
23662306a36Sopenharmony_ci		DMERR("unregister failed %d", r);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cimodule_init(dm_rr_init);
24062306a36Sopenharmony_cimodule_exit(dm_rr_exit);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciMODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector");
24362306a36Sopenharmony_ciMODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
24462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
245