From: 闫海涛 <yanhaitao2(a)huawei.com>
---
 0007.patch | 633 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 633 insertions(+)
 create mode 100644 0007.patch
diff --git a/0007.patch b/0007.patch
new file mode 100644
index 0000000..0b1222a
--- /dev/null
+++ b/0007.patch
@@ -0,0 +1,633 @@
+diff --git a/fs/nfs/enfs/enfs_multipath.c b/fs/nfs/enfs/enfs_multipath.c
+new file mode 100644
+index 000000000000..7a58cab74869
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_multipath.c
+@@ -0,0 +1,627 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ */
++#include "enfs_multipath.h"
++#include <linux/in.h>
++#include <linux/in6.h>
++#include <linux/kallsyms.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/rcupdate.h>
++#include <linux/slab.h>
++#include <linux/socket.h>
++#include <linux/atomic.h>
++#include <linux/sunrpc/addr.h>
++#include <linux/sunrpc/bc_xprt.h>
++#include <linux/sunrpc/clnt.h>
++#include <linux/sunrpc/metrics.h>
++#include <linux/sunrpc/rpc_pipe_fs.h>
++#include <linux/sunrpc/xprt.h>
++#include <linux/sunrpc/xprtmultipath.h>
++#include <linux/types.h>
++#include <linux/un.h>
++#include <linux/utsname.h>
++#include <linux/workqueue.h>
++#include <trace/events/sunrpc.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++#include "enfs.h"
++#include "enfs_log.h"
++#include "enfs_multipath_parse.h"
++#include "enfs_path.h"
++#include "enfs_proc.h"
++#include "enfs_remount.h"
++#include "enfs_roundrobin.h"
++#include "failover_path.h"
++#include "failover_time.h"
++#include "pm_ping.h"
++#include "pm_state.h"
++
++struct xprt_attach_callback_data {
++	atomic_t *conditon;
++	wait_queue_head_t *waitq;
++};
++
++struct xprt_attach_info {
++	struct sockaddr_storage *localAddress;
++	struct sockaddr_storage *remoteAddress;
++	struct rpc_xprt *xprt;
++	struct xprt_attach_callback_data *data;
++};
++
++static DECLARE_WAIT_QUEUE_HEAD(path_attach_wait_queue);
++
++
++static void sockaddr_set_port(struct sockaddr *addr, int port)
++{
++	switch (addr->sa_family) {
++	case AF_INET:
++		((struct sockaddr_in *)addr)->sin_port = htons(port);
++		break;
++	case AF_INET6:
++		((struct sockaddr_in6 *)addr)->sin6_port = htons(port);
++		break;
++	}
++}
++
++static int sockaddr_ip_to_str(struct sockaddr *addr, char *buf, int len)
++{
++	switch (addr->sa_family) {
++	case AF_INET: {
++		struct sockaddr_in *sin = (struct sockaddr_in *)addr;
++
++		snprintf(buf, len, "%pI4", &sin->sin_addr);
++		return 0;
++	}
++	case AF_INET6: {
++		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
++
++		snprintf(buf, len, "%pI6", &sin6->sin6_addr);
++		return 0;
++	}
++	default:
++		break;
++	}
++	return 1;
++}
++
++void print_enfs_multipath_addr(struct sockaddr *local, struct sockaddr *remote)
++{
++	char buf1[128];
++	char buf2[128];
++
++	sockaddr_ip_to_str(local, buf1, sizeof(buf1));
++	sockaddr_ip_to_str(remote, buf2, sizeof(buf2));
++
++	pr_info("local:%s remote:%s\n", buf1, buf2);
++}
++
++static int enfs_servername(char *servername, unsigned long long len,
++						   struct rpc_create_args *args)
++{
++	struct sockaddr_un *sun = (struct sockaddr_un *)args->address;
++	struct sockaddr_in *sin = (struct sockaddr_in *)args->address;
++	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)args->address;
++
++	servername[0] = '\0';
++	switch (args->address->sa_family) {
++	case AF_LOCAL:
++		snprintf(servername, len, "%s", sun->sun_path);
++		break;
++	case AF_INET:
++		snprintf(servername, len, "%pI4", &sin->sin_addr.s_addr);
++		break;
++	case AF_INET6:
++		snprintf(servername, len, "%pI6", &sin6->sin6_addr);
++		break;
++	default:
++		pr_info("invalid family:%d\n",
++				args->address->sa_family);
++		return -EINVAL;
++	}
++	return 0;
++}
++
++static void pm_xprt_ping_callback(void *data)
++{
++	struct xprt_attach_callback_data *attach_callback_data =
++	(struct xprt_attach_callback_data *)data;
++
++	atomic_dec(attach_callback_data->conditon);
++	wake_up(attach_callback_data->waitq);
++}
++
++static
++int enfs_add_xprt_setup(struct rpc_clnt *clnt, struct rpc_xprt_switch *xps,
++			struct rpc_xprt *xprt, void *data)
++{
++	int ret;
++	struct enfs_xprt_context *ctx;
++	struct xprt_attach_info *attach_info = data;
++	struct sockaddr_storage *srcaddr = attach_info->localAddress;
++
++	ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++	ctx->stats = rpc_alloc_iostats(clnt);
++	ctx->main = false;
++	ctx->srcaddr = *srcaddr;
++	pm_set_path_state(xprt, PM_STATE_INIT);
++	pm_ping_set_path_check_state(xprt, PM_CHECK_INIT);
++
++	attach_info->xprt = xprt;
++	xprt_get(xprt);
++
++	ret = pm_ping_rpc_test_xprt_with_callback(clnt, xprt,
++				pm_xprt_ping_callback, attach_info->data);
++	if (ret != 1) {
++		enfs_log_error("Failed to add ping task.\n");
++		atomic_dec(attach_info->data->conditon);
++	}
++
++	// so that rpc_clnt_add_xprt
++	// does not call rpc_xprt_switch_add_xprt
++	return 1;
++}
++
++int enfs_configure_xprt_to_clnt(struct xprt_create *xprtargs,
++			struct rpc_clnt *clnt,
++			struct xprt_attach_info *attach_info)
++{
++	int err = 0;
++
++	xprtargs->srcaddr = (struct sockaddr *)attach_info->localAddress;
++	xprtargs->dstaddr = (struct sockaddr *)attach_info->remoteAddress;
++
++	sockaddr_set_port((struct sockaddr *)attach_info->localAddress, 2049);
++	sockaddr_set_port((struct sockaddr *)attach_info->remoteAddress, 2049);
++	print_enfs_multipath_addr((struct sockaddr *)attach_info->localAddress,
++				(struct sockaddr *)attach_info->remoteAddress);
++
++	err = rpc_clnt_add_xprt(clnt, xprtargs,
++							enfs_add_xprt_setup,
++							attach_info);
++	if (err != 1) {
++		enfs_log_error("clnt add xprt err:%d\n", err);
++		return err;
++	}
++	return 0;
++}
++
++// Calculate the greatest common divisor of two numbers
++static int enfs_cal_gcd(int num1, int num2)
++{
++	if (num2 == 0)
++		return num1;
++	return enfs_cal_gcd(num2, num1 % num2);
++}
++
++bool enfs_cmp_addrs(struct sockaddr_storage *srcaddr,
++					struct sockaddr_storage *dstaddr,
++					struct sockaddr_storage *localAddress,
++					struct sockaddr_storage *remoteAddress)
++{
++	if (rpc_cmp_addr((struct sockaddr *)srcaddr,
++		(struct sockaddr *)localAddress)) {
++
++		if (rpc_cmp_addr((struct sockaddr *)dstaddr,
++			(struct sockaddr *)remoteAddress))
++			return true;
++
++	}
++
++	return false;
++}
++
++bool enfs_xprt_addrs_is_same(struct rpc_xprt *xprt,
++			struct sockaddr_storage *localAddress,
++			struct sockaddr_storage *remoteAddress)
++{
++	struct enfs_xprt_context *xprt_local_info = NULL;
++	struct sockaddr_storage *srcaddr = NULL;
++	struct sockaddr_storage *dstaddr = NULL;
++
++	if (xprt == NULL)
++		return true;
++	xprt_local_info = (struct enfs_xprt_context *)xprt->multipath_context;
++	srcaddr = &xprt_local_info->srcaddr;
++	dstaddr = &xprt->addr;
++
++	return enfs_cmp_addrs(srcaddr, dstaddr, localAddress, remoteAddress);
++}
++
++bool enfs_already_have_xprt(struct rpc_clnt *clnt,
++			struct sockaddr_storage *localAddress,
++			struct sockaddr_storage *remoteAddress)
++{
++	struct rpc_xprt *pos = NULL;
++	struct rpc_xprt_switch *xps = NULL;
++
++	rcu_read_lock();
++	xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
++	if (xps == NULL) {
++		rcu_read_unlock();
++		return false;
++	}
++	list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
++		if (enfs_xprt_addrs_is_same(pos, localAddress, remoteAddress)) {
++			xprt_switch_put(xps);
++			rcu_read_unlock();
++			return true;
++		}
++	}
++	xprt_switch_put(xps);
++	rcu_read_unlock();
++	return false;
++}
++
++static void enfs_xprt_switch_add_xprt(struct rpc_clnt *clnt,
++			struct rpc_xprt *xprt)
++{
++	struct rpc_xprt_switch *xps;
++
++	rcu_read_lock();
++	xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
++	spin_lock(&xps->xps_lock);
++	if (xps->xps_net == xprt->xprt_net || xps->xps_net == NULL)
++		xprt_switch_add_xprt_locked(xps, xprt);
++	spin_unlock(&xps->xps_lock);
++	rcu_read_unlock();
++}
++
++static void enfs_add_xprts_to_clnt(struct rpc_clnt *clnt,
++			struct xprt_attach_info *attach_infos,
++			int total_combinations)
++{
++	struct rpc_xprt *xprt;
++	enum pm_path_state state;
++	int i;
++	int link_count = 0;
++
++	for (i = 0; i < total_combinations; i++) {
++		xprt = attach_infos[i].xprt;
++
++		if (xprt == NULL)
++			continue;
++
++		state = pm_get_path_state(xprt);
++
++		if (link_count < MAX_XPRT_NUM_PER_CLIENT &&
++			state == PM_STATE_NORMAL) {
++
++			enfs_xprt_switch_add_xprt(clnt, xprt);
++			link_count++;
++			continue;
++
++		}
++
++		xprt_put(xprt);
++	}
++}
++
++static void enfs_combine_addr(struct xprt_create *xprtargs,
++			struct rpc_clnt *clnt,
++			struct nfs_ip_list *local,
++			struct nfs_ip_list *remote)
++{
++	int i;
++	int err;
++	int local_index;
++	int remote_index;
++	int link_count = 0;
++	int local_total = local->count;
++	int remote_total = remote->count;
++	int local_remote_total_lcm;
++	int total_combinations = local_total * remote_total;
++	struct xprt_attach_info *attach_infos;
++	atomic_t wait_queue_condition;
++
++	struct xprt_attach_callback_data attach_callback_data = {
++		&wait_queue_condition, &path_attach_wait_queue};
++
++	atomic_set(&wait_queue_condition, total_combinations);
++
++	pr_info("local count:%d remote count:%d\n", local_total, remote_total);
++	if (local_total == 0 || remote_total == 0) {
++		pr_err("no ip list is present.\n");
++		return;
++	}
++
++	attach_infos = kzalloc(
++			(total_combinations) * sizeof(struct xprt_attach_info),
++			GFP_KERNEL);
++	if (attach_infos == NULL) {
++		enfs_log_error("Failed to kzalloc memory\n");
++		return;
++	}
++	// Calculate the least common multiple of local_total and remote_total
++	local_remote_total_lcm = total_combinations /
++				enfs_cal_gcd(local_total, remote_total);
++
++	// It needs to be offset one for lcm times of
++	// cycle so that all possible link setup method would be traversed
++	for (i = 0; i < total_combinations; i++) {
++		local_index = i % local_total;
++		remote_index = (i + link_count / local_remote_total_lcm) %
++						remote_total;
++
++		if (enfs_already_have_xprt(clnt,
++			&local->address[local_index],
++			&remote->address[remote_index])) {
++
++			atomic_dec(&wait_queue_condition);
++			link_count++;
++			continue;
++
++		}
++
++		attach_infos[i].localAddress = &local->address[local_index];
++		attach_infos[i].remoteAddress = &remote->address[remote_index];
++		attach_infos[i].data = &attach_callback_data;
++
++		err = enfs_configure_xprt_to_clnt(xprtargs,
++					clnt, &attach_infos[i]);
++		if (err) {
++			pr_err("add xprt ippair err:%d\n", err);
++			atomic_dec(&wait_queue_condition);
++		}
++		link_count++;
++	}
++
++	wait_event(path_attach_wait_queue,
++			atomic_read(&wait_queue_condition) == 0);
++
++	enfs_add_xprts_to_clnt(clnt, attach_infos, total_combinations);
++
++	kfree(attach_infos);
++}
++
++void enfs_xprt_ippair_create(struct xprt_create *xprtargs,
++			struct rpc_clnt *clnt,
++			void *data)
++{
++	struct multipath_mount_options *mopt =
++				(struct multipath_mount_options *)data;
++
++	if (mopt == NULL) {
++		pr_err("ip list is NULL.\n");
++		return;
++	}
++
++	enfs_combine_addr(xprtargs, clnt,
++				mopt->local_ip_list,
++				mopt->remote_ip_list);
++	enfs_lb_set_policy(clnt);
++}
++
++struct xprts_options_and_clnt {
++	struct rpc_create_args *args;
++	struct rpc_clnt *clnt;
++	void *data;
++};
++
++static void set_clnt_enfs_flag(struct rpc_clnt *clnt)
++{
++	clnt->cl_enfs = true;
++}
++
++int enfs_config_xprt_create_args(struct xprt_create *xprtargs,
++			struct rpc_create_args *args,
++			char *servername, size_t length)
++{
++	int errno = 0;
++
++	xprtargs->ident = args->protocol;
++	xprtargs->net = args->net;
++	xprtargs->addrlen = args->addrsize;
++	xprtargs->servername = args->servername;
++
++	if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS)
++		xprtargs->flags |= XPRT_CREATE_INFINITE_SLOTS;
++	if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT)
++		xprtargs->flags |= XPRT_CREATE_NO_IDLE_TIMEOUT;
++
++	if (xprtargs->servername == NULL) {
++		errno = enfs_servername(servername, length, args);
++		if (errno)
++			return errno;
++		xprtargs->servername = servername;
++	}
++
++	return 0;
++}
++
++int enfs_multipath_create_thread(void *data)
++{
++	int errno;
++	char servername[48];
++	struct xprts_options_and_clnt *create_args =
++			(struct xprts_options_and_clnt *)data;
++	struct multipath_mount_options *mount_options =
++			(struct multipath_mount_options *)
++			create_args->args->multipath_option;
++	struct xprt_create xprtargs;
++
++	memset(&xprtargs, 0, sizeof(struct xprt_create));
++
++	if (mount_options == NULL) {
++		enfs_log_error("enfs: local and remot addr empty\n");
++		return -EINVAL;
++	}
++
++	errno = enfs_config_xprt_create_args(&xprtargs,
++				create_args->args, servername,
++				sizeof(servername));
++	if (errno) {
++		enfs_log_error("enfs: create xprt failed! errno:%d\n",
++			errno);
++		return errno;
++	}
++
++	//mount : localaddrs or remoteaddrs is empty
++	if (mount_options->local_ip_list->count == 0) {
++		errno = rpc_localaddr(create_args->clnt,
++				(struct sockaddr *)
++				&mount_options->local_ip_list->address[0],
++				sizeof(struct sockaddr_storage));
++		if (errno) {
++			enfs_log_error("enfs: get clnt srcaddr errno:%d\n",
++				errno);
++			return errno;
++		}
++		mount_options->local_ip_list->count = 1;
++	}
++
++	if (mount_options->remote_ip_list->count == 0) {
++		errno = rpc_peeraddr(create_args->clnt,
++				(struct sockaddr *)
++				&mount_options->remote_ip_list->address[0],
++				sizeof(struct sockaddr_storage));
++		if (errno == 0) {
++			enfs_log_error("enfs: get clnt dstaddr errno:%d\n",
++				errno);
++			return errno;
++		}
++		mount_options->remote_ip_list->count = 1;
++	}
++
++	errno = enfs_proc_create_clnt(create_args->clnt);
++	if (errno != 0)
++		pr_err("create clnt proc failed.\n");
++
++	set_clnt_enfs_flag(create_args->clnt);
++	enfs_xprt_ippair_create(&xprtargs, create_args->clnt, mount_options);
++
++	kfree(create_args->args);
++	kfree(data);
++	return 0;
++}
++
++static int set_main_xprt_ctx(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
++			struct sockaddr_storage *srcaddr)
++{
++	struct enfs_xprt_context *ctx = xprt->multipath_context;
++
++	if (!ctx) {
++		enfs_log_error("main xprt not multipath ctx.\n");
++		return -1;
++	}
++
++	ctx->main = true;
++	ctx->stats = rpc_alloc_iostats(clnt);
++	ctx->srcaddr = *srcaddr;
++	pm_set_path_state(xprt, PM_STATE_NORMAL);
++	pm_ping_set_path_check_state(xprt, PM_CHECK_INIT);
++
++	return 0;
++}
++
++static int alloc_main_xprt_multicontext(struct rpc_create_args *args,
++			struct rpc_clnt *clnt)
++{
++	int err;
++	struct sockaddr_storage srcaddr;
++
++	// avoid main xprt multicontex local addr is empty.
++	err = rpc_localaddr(clnt, (struct sockaddr *)&srcaddr, sizeof(srcaddr));
++	if (err) {
++		enfs_log_error("get clnt localaddr err:%d\n", err);
++		return err;
++	}
++
++	err = set_main_xprt_ctx(clnt, clnt->cl_xprt, &srcaddr);
++	if (err)
++		enfs_log_error("alloc main xprt failed.\n");
++
++	return err;
++}
++
++void enfs_create_multi_xprt(struct rpc_create_args *args, struct rpc_clnt *clnt)
++{
++	// struct task_struct *th;
++	struct xprts_options_and_clnt *thargs;
++	struct rpc_create_args *cargs;
++	int err;
++
++	enfs_log_info("%s %p\n", __func__, clnt);
++
++	cargs = kmalloc(sizeof(struct rpc_create_args), GFP_KERNEL);
++	if (cargs == NULL)
++		return;
++
++	*cargs = *args;
++
++	thargs = kmalloc(sizeof(struct xprts_options_and_clnt), GFP_KERNEL);
++	if (thargs == NULL) {
++		kfree(cargs);
++		return;
++	}
++
++	alloc_main_xprt_multicontext(args, clnt);
++
++	thargs->args = cargs;
++	thargs->clnt = clnt;
++	thargs->data = args->multipath_option;
++
++	err = enfs_multipath_create_thread(thargs);
++
++	if (err != 0) {
++		kfree(cargs);
++		kfree(thargs);
++	}
++}
++
++static void enfs_create_xprt_ctx(struct rpc_xprt *xprt)
++{
++	int err;
++
++	err = enfs_alloc_xprt_ctx(xprt);
++	if (err)
++		enfs_log_error("alloc xprt failed.\n");
++}
++
++static struct rpc_multipath_ops ops = {
++	.owner = THIS_MODULE,
++	.create_clnt = enfs_create_multi_xprt,
++	.releas_clnt = enfs_proc_delete_clnt,
++	.create_xprt = enfs_create_xprt_ctx,
++	.destroy_xprt = enfs_free_xprt_ctx,
++	.xprt_iostat = enfs_count_iostat,
++	.failover_handle = failover_handle,
++	.adjust_task_timeout = failover_adjust_task_timeout,
++	.init_task_req = failover_init_task_req,
++	.prepare_transmit = failover_prepare_transmit,
++};
++
++int enfs_multipath_init(void)
++{
++	int err;
++
++	enfs_log_info("multipath init.\n");
++
++	err = pm_ping_init();
++	if (err != 0) {
++		enfs_log_error("pm ping init err:%d\n", err);
++		return err;
++	}
++
++	err = enfs_proc_init();
++	if (err != 0) {
++		enfs_log_error("enfs proc init err:%d\n", err);
++		pm_ping_fini();
++		return err;
++	}
++
++	rpc_multipath_ops_register(&ops);
++
++	return 0;
++}
++
++void enfs_multipath_exit(void)
++{
++	enfs_log_info("multipath exit.\n");
++	rpc_multipath_ops_unregister(&ops);
++	enfs_proc_exit();
++	pm_ping_fini();
++}
-- 
2.25.0.windows.1