forked from mirrors/linux
		
	 c3a1f2ef72
			
		
	
	
		c3a1f2ef72
		
	
	
	
	
		
			
			Use 2-factor multiplication argument form kcalloc() instead of instead of the deprecated kzalloc() [1]. [1] https://www.kernel.org/doc/html/next/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments Link: https://github.com/KSPP/linux/issues/162 Signed-off-by: Ethan Carter Edwards <ethan@ethancedwards.com> Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org> Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
		
			
				
	
	
		
			274 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| /*
 | |
|  * Copyright (C) 2020 Oracle Corporation
 | |
|  *
 | |
|  * Module Author: Mike Christie
 | |
|  */
 | |
| #include "dm-path-selector.h"
 | |
| 
 | |
| #include <linux/device-mapper.h>
 | |
| #include <linux/module.h>
 | |
| 
 | |
| #define DM_MSG_PREFIX "multipath io-affinity"
 | |
| 
 | |
| struct path_info {
 | |
| 	struct dm_path *path;
 | |
| 	cpumask_var_t cpumask;
 | |
| 	refcount_t refcount;
 | |
| 	bool failed;
 | |
| };
 | |
| 
 | |
| struct selector {
 | |
| 	struct path_info **path_map;
 | |
| 	cpumask_var_t path_mask;
 | |
| 	atomic_t map_misses;
 | |
| };
 | |
| 
 | |
| static void ioa_free_path(struct selector *s, unsigned int cpu)
 | |
| {
 | |
| 	struct path_info *pi = s->path_map[cpu];
 | |
| 
 | |
| 	if (!pi)
 | |
| 		return;
 | |
| 
 | |
| 	if (refcount_dec_and_test(&pi->refcount)) {
 | |
| 		cpumask_clear_cpu(cpu, s->path_mask);
 | |
| 		free_cpumask_var(pi->cpumask);
 | |
| 		kfree(pi);
 | |
| 
 | |
| 		s->path_map[cpu] = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int ioa_add_path(struct path_selector *ps, struct dm_path *path,
 | |
| 			int argc, char **argv, char **error)
 | |
| {
 | |
| 	struct selector *s = ps->context;
 | |
| 	struct path_info *pi = NULL;
 | |
| 	unsigned int cpu;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (argc != 1) {
 | |
| 		*error = "io-affinity ps: invalid number of arguments";
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	pi = kzalloc(sizeof(*pi), GFP_KERNEL);
 | |
| 	if (!pi) {
 | |
| 		*error = "io-affinity ps: Error allocating path context";
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	pi->path = path;
 | |
| 	path->pscontext = pi;
 | |
| 	refcount_set(&pi->refcount, 1);
 | |
| 
 | |
| 	if (!zalloc_cpumask_var(&pi->cpumask, GFP_KERNEL)) {
 | |
| 		*error = "io-affinity ps: Error allocating cpumask context";
 | |
| 		ret = -ENOMEM;
 | |
| 		goto free_pi;
 | |
| 	}
 | |
| 
 | |
| 	ret = cpumask_parse(argv[0], pi->cpumask);
 | |
| 	if (ret) {
 | |
| 		*error = "io-affinity ps: invalid cpumask";
 | |
| 		ret = -EINVAL;
 | |
| 		goto free_mask;
 | |
| 	}
 | |
| 
 | |
| 	for_each_cpu(cpu, pi->cpumask) {
 | |
| 		if (cpu >= nr_cpu_ids) {
 | |
| 			DMWARN_LIMIT("Ignoring mapping for CPU %u. Max CPU is %u",
 | |
| 				     cpu, nr_cpu_ids);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (s->path_map[cpu]) {
 | |
| 			DMWARN("CPU mapping for %u exists. Ignoring.", cpu);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		cpumask_set_cpu(cpu, s->path_mask);
 | |
| 		s->path_map[cpu] = pi;
 | |
| 		refcount_inc(&pi->refcount);
 | |
| 	}
 | |
| 
 | |
| 	if (refcount_dec_and_test(&pi->refcount)) {
 | |
| 		*error = "io-affinity ps: No new/valid CPU mapping found";
 | |
| 		ret = -EINVAL;
 | |
| 		goto free_mask;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| free_mask:
 | |
| 	free_cpumask_var(pi->cpumask);
 | |
| free_pi:
 | |
| 	kfree(pi);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int ioa_create(struct path_selector *ps, unsigned int argc, char **argv)
 | |
| {
 | |
| 	struct selector *s;
 | |
| 
 | |
| 	s = kmalloc(sizeof(*s), GFP_KERNEL);
 | |
| 	if (!s)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	s->path_map = kcalloc(nr_cpu_ids, sizeof(struct path_info *),
 | |
| 			      GFP_KERNEL);
 | |
| 	if (!s->path_map)
 | |
| 		goto free_selector;
 | |
| 
 | |
| 	if (!zalloc_cpumask_var(&s->path_mask, GFP_KERNEL))
 | |
| 		goto free_map;
 | |
| 
 | |
| 	atomic_set(&s->map_misses, 0);
 | |
| 	ps->context = s;
 | |
| 	return 0;
 | |
| 
 | |
| free_map:
 | |
| 	kfree(s->path_map);
 | |
| free_selector:
 | |
| 	kfree(s);
 | |
| 	return -ENOMEM;
 | |
| }
 | |
| 
 | |
| static void ioa_destroy(struct path_selector *ps)
 | |
| {
 | |
| 	struct selector *s = ps->context;
 | |
| 	unsigned int cpu;
 | |
| 
 | |
| 	for_each_cpu(cpu, s->path_mask)
 | |
| 		ioa_free_path(s, cpu);
 | |
| 
 | |
| 	free_cpumask_var(s->path_mask);
 | |
| 	kfree(s->path_map);
 | |
| 	kfree(s);
 | |
| 
 | |
| 	ps->context = NULL;
 | |
| }
 | |
| 
 | |
| static int ioa_status(struct path_selector *ps, struct dm_path *path,
 | |
| 		      status_type_t type, char *result, unsigned int maxlen)
 | |
| {
 | |
| 	struct selector *s = ps->context;
 | |
| 	struct path_info *pi;
 | |
| 	int sz = 0;
 | |
| 
 | |
| 	if (!path) {
 | |
| 		DMEMIT("0 ");
 | |
| 		return sz;
 | |
| 	}
 | |
| 
 | |
| 	switch (type) {
 | |
| 	case STATUSTYPE_INFO:
 | |
| 		DMEMIT("%d ", atomic_read(&s->map_misses));
 | |
| 		break;
 | |
| 	case STATUSTYPE_TABLE:
 | |
| 		pi = path->pscontext;
 | |
| 		DMEMIT("%*pb ", cpumask_pr_args(pi->cpumask));
 | |
| 		break;
 | |
| 	case STATUSTYPE_IMA:
 | |
| 		*result = '\0';
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return sz;
 | |
| }
 | |
| 
 | |
| static void ioa_fail_path(struct path_selector *ps, struct dm_path *p)
 | |
| {
 | |
| 	struct path_info *pi = p->pscontext;
 | |
| 
 | |
| 	pi->failed = true;
 | |
| }
 | |
| 
 | |
| static int ioa_reinstate_path(struct path_selector *ps, struct dm_path *p)
 | |
| {
 | |
| 	struct path_info *pi = p->pscontext;
 | |
| 
 | |
| 	pi->failed = false;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct dm_path *ioa_select_path(struct path_selector *ps,
 | |
| 				       size_t nr_bytes)
 | |
| {
 | |
| 	unsigned int cpu, node;
 | |
| 	struct selector *s = ps->context;
 | |
| 	const struct cpumask *cpumask;
 | |
| 	struct path_info *pi;
 | |
| 	int i;
 | |
| 
 | |
| 	cpu = get_cpu();
 | |
| 
 | |
| 	pi = s->path_map[cpu];
 | |
| 	if (pi && !pi->failed)
 | |
| 		goto done;
 | |
| 
 | |
| 	/*
 | |
| 	 * Perf is not optimal, but we at least try the local node then just
 | |
| 	 * try not to fail.
 | |
| 	 */
 | |
| 	if (!pi)
 | |
| 		atomic_inc(&s->map_misses);
 | |
| 
 | |
| 	node = cpu_to_node(cpu);
 | |
| 	cpumask = cpumask_of_node(node);
 | |
| 	for_each_cpu(i, cpumask) {
 | |
| 		pi = s->path_map[i];
 | |
| 		if (pi && !pi->failed)
 | |
| 			goto done;
 | |
| 	}
 | |
| 
 | |
| 	for_each_cpu(i, s->path_mask) {
 | |
| 		pi = s->path_map[i];
 | |
| 		if (pi && !pi->failed)
 | |
| 			goto done;
 | |
| 	}
 | |
| 	pi = NULL;
 | |
| 
 | |
| done:
 | |
| 	put_cpu();
 | |
| 	return pi ? pi->path : NULL;
 | |
| }
 | |
| 
 | |
| static struct path_selector_type ioa_ps = {
 | |
| 	.name		= "io-affinity",
 | |
| 	.module		= THIS_MODULE,
 | |
| 	.table_args	= 1,
 | |
| 	.info_args	= 1,
 | |
| 	.create		= ioa_create,
 | |
| 	.destroy	= ioa_destroy,
 | |
| 	.status		= ioa_status,
 | |
| 	.add_path	= ioa_add_path,
 | |
| 	.fail_path	= ioa_fail_path,
 | |
| 	.reinstate_path	= ioa_reinstate_path,
 | |
| 	.select_path	= ioa_select_path,
 | |
| };
 | |
| 
 | |
| static int __init dm_ioa_init(void)
 | |
| {
 | |
| 	int ret = dm_register_path_selector(&ioa_ps);
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		DMERR("register failed %d", ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void __exit dm_ioa_exit(void)
 | |
| {
 | |
| 	int ret = dm_unregister_path_selector(&ioa_ps);
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		DMERR("unregister failed %d", ret);
 | |
| }
 | |
| 
 | |
| module_init(dm_ioa_init);
 | |
| module_exit(dm_ioa_exit);
 | |
| 
 | |
| MODULE_DESCRIPTION(DM_NAME " multipath path selector that selects paths based on the CPU IO is being executed on");
 | |
| MODULE_AUTHOR("Mike Christie <michael.christie@oracle.com>");
 | |
| MODULE_LICENSE("GPL");
 |