162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2004-2005 IBM Corp. All Rights Reserved. 462306a36Sopenharmony_ci * Copyright (C) 2006-2009 NEC Corporation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * dm-queue-length.c 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Module Author: Stefan Bader, IBM 962306a36Sopenharmony_ci * Modified by: Kiyoshi Ueda, NEC 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This file is released under the GPL. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * queue-length path selector - choose a path with the least number of 1462306a36Sopenharmony_ci * in-flight I/Os. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "dm.h" 1862306a36Sopenharmony_ci#include "dm-path-selector.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/ctype.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/atomic.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DM_MSG_PREFIX "multipath queue-length" 2762306a36Sopenharmony_ci#define QL_MIN_IO 1 2862306a36Sopenharmony_ci#define QL_VERSION "0.2.0" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct selector { 3162306a36Sopenharmony_ci struct list_head valid_paths; 3262306a36Sopenharmony_ci struct list_head failed_paths; 3362306a36Sopenharmony_ci spinlock_t lock; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct path_info { 3762306a36Sopenharmony_ci struct list_head list; 3862306a36Sopenharmony_ci struct dm_path *path; 3962306a36Sopenharmony_ci unsigned int repeat_count; 4062306a36Sopenharmony_ci atomic_t qlen; /* the number of in-flight I/Os */ 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct selector *alloc_selector(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (s) { 4862306a36Sopenharmony_ci INIT_LIST_HEAD(&s->valid_paths); 4962306a36Sopenharmony_ci INIT_LIST_HEAD(&s->failed_paths); 5062306a36Sopenharmony_ci spin_lock_init(&s->lock); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return s; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int ql_create(struct path_selector *ps, unsigned int argc, char **argv) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct selector *s = alloc_selector(); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!s) 6162306a36Sopenharmony_ci return -ENOMEM; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ps->context = s; 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void ql_free_paths(struct list_head *paths) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct path_info *pi, *next; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci list_for_each_entry_safe(pi, next, paths, list) { 7262306a36Sopenharmony_ci list_del(&pi->list); 7362306a36Sopenharmony_ci kfree(pi); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void ql_destroy(struct path_selector *ps) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct selector *s = ps->context; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ql_free_paths(&s->valid_paths); 8262306a36Sopenharmony_ci ql_free_paths(&s->failed_paths); 8362306a36Sopenharmony_ci kfree(s); 8462306a36Sopenharmony_ci ps->context = NULL; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int ql_status(struct path_selector *ps, struct dm_path *path, 8862306a36Sopenharmony_ci status_type_t type, char *result, unsigned int maxlen) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci unsigned int sz = 0; 9162306a36Sopenharmony_ci struct path_info *pi; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* When called with NULL path, return selector status/args. */ 9462306a36Sopenharmony_ci if (!path) 9562306a36Sopenharmony_ci DMEMIT("0 "); 9662306a36Sopenharmony_ci else { 9762306a36Sopenharmony_ci pi = path->pscontext; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci switch (type) { 10062306a36Sopenharmony_ci case STATUSTYPE_INFO: 10162306a36Sopenharmony_ci DMEMIT("%d ", atomic_read(&pi->qlen)); 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case STATUSTYPE_TABLE: 10462306a36Sopenharmony_ci DMEMIT("%u ", pi->repeat_count); 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci case STATUSTYPE_IMA: 10762306a36Sopenharmony_ci *result = '\0'; 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return sz; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int ql_add_path(struct path_selector *ps, struct dm_path *path, 11662306a36Sopenharmony_ci int argc, char **argv, char **error) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct selector *s = ps->context; 11962306a36Sopenharmony_ci struct path_info *pi; 12062306a36Sopenharmony_ci unsigned int repeat_count = QL_MIN_IO; 12162306a36Sopenharmony_ci char dummy; 12262306a36Sopenharmony_ci unsigned long flags; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * Arguments: [<repeat_count>] 12662306a36Sopenharmony_ci * <repeat_count>: The number of I/Os before switching path. 12762306a36Sopenharmony_ci * If not given, default (QL_MIN_IO) is used. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci if (argc > 1) { 13062306a36Sopenharmony_ci *error = "queue-length ps: incorrect number of arguments"; 13162306a36Sopenharmony_ci return -EINVAL; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if ((argc == 1) && (sscanf(argv[0], "%u%c", &repeat_count, &dummy) != 1)) { 13562306a36Sopenharmony_ci *error = "queue-length ps: invalid repeat count"; 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (repeat_count > 1) { 14062306a36Sopenharmony_ci DMWARN_LIMIT("repeat_count > 1 is deprecated, using 1 instead"); 14162306a36Sopenharmony_ci repeat_count = 1; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Allocate the path information structure */ 14562306a36Sopenharmony_ci pi = kmalloc(sizeof(*pi), GFP_KERNEL); 14662306a36Sopenharmony_ci if (!pi) { 14762306a36Sopenharmony_ci *error = "queue-length ps: Error allocating path information"; 14862306a36Sopenharmony_ci return -ENOMEM; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci pi->path = path; 15262306a36Sopenharmony_ci pi->repeat_count = repeat_count; 15362306a36Sopenharmony_ci atomic_set(&pi->qlen, 0); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci path->pscontext = pi; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 15862306a36Sopenharmony_ci list_add_tail(&pi->list, &s->valid_paths); 15962306a36Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void ql_fail_path(struct path_selector *ps, struct dm_path *path) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct selector *s = ps->context; 16762306a36Sopenharmony_ci struct path_info *pi = path->pscontext; 16862306a36Sopenharmony_ci unsigned long flags; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 17162306a36Sopenharmony_ci list_move(&pi->list, &s->failed_paths); 17262306a36Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int ql_reinstate_path(struct path_selector *ps, struct dm_path *path) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct selector *s = ps->context; 17862306a36Sopenharmony_ci struct path_info *pi = path->pscontext; 17962306a36Sopenharmony_ci unsigned long flags; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 18262306a36Sopenharmony_ci list_move_tail(&pi->list, &s->valid_paths); 18362306a36Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * Select a path having the minimum number of in-flight I/Os 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic struct dm_path *ql_select_path(struct path_selector *ps, size_t nr_bytes) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct selector *s = ps->context; 19462306a36Sopenharmony_ci struct path_info *pi = NULL, *best = NULL; 19562306a36Sopenharmony_ci struct dm_path *ret = NULL; 19662306a36Sopenharmony_ci unsigned long flags; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci spin_lock_irqsave(&s->lock, flags); 19962306a36Sopenharmony_ci if (list_empty(&s->valid_paths)) 20062306a36Sopenharmony_ci goto out; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci list_for_each_entry(pi, &s->valid_paths, list) { 20362306a36Sopenharmony_ci if (!best || 20462306a36Sopenharmony_ci (atomic_read(&pi->qlen) < atomic_read(&best->qlen))) 20562306a36Sopenharmony_ci best = pi; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!atomic_read(&best->qlen)) 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!best) 21262306a36Sopenharmony_ci goto out; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Move most recently used to least preferred to evenly balance. */ 21562306a36Sopenharmony_ci list_move_tail(&best->list, &s->valid_paths); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = best->path; 21862306a36Sopenharmony_ciout: 21962306a36Sopenharmony_ci spin_unlock_irqrestore(&s->lock, flags); 22062306a36Sopenharmony_ci return ret; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int ql_start_io(struct path_selector *ps, struct dm_path *path, 22462306a36Sopenharmony_ci size_t nr_bytes) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct path_info *pi = path->pscontext; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci atomic_inc(&pi->qlen); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int ql_end_io(struct path_selector *ps, struct dm_path *path, 23462306a36Sopenharmony_ci size_t nr_bytes, u64 start_time) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct path_info *pi = path->pscontext; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci atomic_dec(&pi->qlen); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic struct path_selector_type ql_ps = { 24462306a36Sopenharmony_ci .name = "queue-length", 24562306a36Sopenharmony_ci .module = THIS_MODULE, 24662306a36Sopenharmony_ci .table_args = 1, 24762306a36Sopenharmony_ci .info_args = 1, 24862306a36Sopenharmony_ci .create = ql_create, 24962306a36Sopenharmony_ci .destroy = ql_destroy, 25062306a36Sopenharmony_ci .status = ql_status, 25162306a36Sopenharmony_ci .add_path = ql_add_path, 25262306a36Sopenharmony_ci .fail_path = ql_fail_path, 25362306a36Sopenharmony_ci .reinstate_path = ql_reinstate_path, 25462306a36Sopenharmony_ci .select_path = ql_select_path, 25562306a36Sopenharmony_ci .start_io = ql_start_io, 25662306a36Sopenharmony_ci .end_io = ql_end_io, 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int __init dm_ql_init(void) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int r = dm_register_path_selector(&ql_ps); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (r < 0) 26462306a36Sopenharmony_ci DMERR("register failed %d", r); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci DMINFO("version " QL_VERSION " loaded"); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return r; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void __exit dm_ql_exit(void) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int r = dm_unregister_path_selector(&ql_ps); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (r < 0) 27662306a36Sopenharmony_ci DMERR("unregister failed %d", r); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cimodule_init(dm_ql_init); 28062306a36Sopenharmony_cimodule_exit(dm_ql_exit); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciMODULE_AUTHOR("Stefan Bader <Stefan.Bader at de.ibm.com>"); 28362306a36Sopenharmony_ciMODULE_DESCRIPTION( 28462306a36Sopenharmony_ci "(C) Copyright IBM Corp. 2004,2005 All Rights Reserved.\n" 28562306a36Sopenharmony_ci DM_NAME " path selector to balance the number of in-flight I/Os" 28662306a36Sopenharmony_ci); 28762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 288