diff --git a/fs/nfs/enfs/enfs_multipath.c b/fs/nfs/enfs/enfs_multipath.c
new file mode 100644
index 000000000..d49dc91e1
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath.c
@@ -0,0 +1,654 @@
+// 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);
+
+/**
+ * set socket port.
+ * @ns: need transform to network byte order
+ */
+static void sockaddr_set_port(struct sockaddr *addr, __be16 port, bool ns)
+{
+	switch (addr->sa_family) {
+	case AF_INET:
+		((struct sockaddr_in *)addr)->sin_port =
+			ns ? htons(port) : port;
+		break;
+	case AF_INET6:
+		((struct sockaddr_in6 *)addr)->sin6_port =
+			ns ? htons(port) : port;
+		break;
+	}
+}
+
+static __be16 get_rpc_clnt_port(struct rpc_clnt *clnt)
+{
+	struct sockaddr_storage ss;
+	struct sockaddr *addr = (struct sockaddr *)&ss;
+
+	rpc_peeraddr(clnt, (struct sockaddr *)&ss, sizeof(ss));
+	switch (addr->sa_family) {
+	case AF_INET:
+		return ((struct sockaddr_in *)addr)->sin_port;
+
+	case AF_INET6:
+		return ((struct sockaddr_in6 *)addr)->sin6_port;
+
+	default:
+		enfs_log_error("not support family:%d.\n", addr->sa_family);
+		return -1;
+	}
+}
+
+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;
+	__be16 port;
+
+	xprtargs->srcaddr = (struct sockaddr *)attach_info->localAddress;
+	xprtargs->dstaddr = (struct sockaddr *)attach_info->remoteAddress;
+
+	port = get_rpc_clnt_port(clnt);
+	sockaddr_set_port((struct sockaddr *)attach_info->remoteAddress, port,
+					  false);
+	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,
+	.task_need_call_start_again = failover_task_need_call_start_again,
+	.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();
+}
diff --git a/fs/nfs/enfs/enfs_multipath.h b/fs/nfs/enfs/enfs_multipath.h
new file mode 100644
index 000000000..3c58b0c4c
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Description: enfs multipath
+ * Create: 2023-07-31
+ */
+
+#ifndef ENFS_MULTIPATH_H
+#define ENFS_MULTIPATH_H
+#include <linux/sunrpc/clnt.h>
+
+#define MAX_XPRT_NUM_PER_CLIENT 32
+
+int enfs_multipath_init(void);
+void enfs_multipath_exit(void);
+void enfs_xprt_ippair_create(struct xprt_create *xprtargs,
+			struct rpc_clnt *clnt, void *data);
+int enfs_config_xprt_create_args(struct xprt_create *xprtargs,
+			struct rpc_create_args *args,
+			char *servername, size_t length);
+void print_enfs_multipath_addr(struct sockaddr *local, struct sockaddr *remote);
+
+#endif // ENFS_MULTIPATH_H
diff --git a/fs/nfs/enfs/enfs_path.c b/fs/nfs/enfs/enfs_path.c
new file mode 100644
index 000000000..7355f8c2f
--- /dev/null
+++ b/fs/nfs/enfs/enfs_path.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/xprt.h>
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_path.h"
+
+// only create ctx in this function
+// alloc iostat memory in create_clnt
+int enfs_alloc_xprt_ctx(struct rpc_xprt *xprt)
+{
+	struct enfs_xprt_context *ctx;
+
+	if (!xprt) {
+		enfs_log_error("invalid xprt pointer.\n");
+		return -EINVAL;
+	}
+
+	ctx = kzalloc(sizeof(struct enfs_xprt_context), GFP_KERNEL);
+	if (!ctx) {
+		enfs_log_error("add xprt test failed.\n");
+		return -ENOMEM;
+	}
+
+	xprt->multipath_context = (void *)ctx;
+	return 0;
+}
+
+// free multi_context and iostat memory
+void enfs_free_xprt_ctx(struct rpc_xprt *xprt)
+{
+	struct enfs_xprt_context *ctx = xprt->multipath_context;
+
+	if (ctx) {
+		if (ctx->stats) {
+			rpc_free_iostats(ctx->stats);
+			ctx->stats = NULL;
+		}
+		kfree(xprt->multipath_context);
+		xprt->multipath_context = NULL;
+	}
+}
diff --git a/fs/nfs/enfs/enfs_path.h b/fs/nfs/enfs/enfs_path.h
new file mode 100644
index 000000000..97b1ef373
--- /dev/null
+++ b/fs/nfs/enfs/enfs_path.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+
+#ifndef ENFS_PATH_H
+#define ENFS_PATH_H
+
+int enfs_alloc_xprt_ctx(struct rpc_xprt *xprt);
+void enfs_free_xprt_ctx(struct rpc_xprt *xprt);
+
+#endif  // ENFS_PATH_H
diff --git a/fs/nfs/enfs/enfs_proc.c b/fs/nfs/enfs/enfs_proc.c
new file mode 100644
index 000000000..53fa1a076
--- /dev/null
+++ b/fs/nfs/enfs/enfs_proc.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/xprtsock.h>
+#include <net/netns/generic.h>
+
+#include "../../../net/sunrpc/netns.h"
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_proc.h"
+#include "enfs_multipath.h"
+#include "pm_state.h"
+
+#define ENFS_PROC_DIR "enfs"
+#define ENFS_PROC_PATH_STATUS_LEN 256
+
+static struct proc_dir_entry *enfs_proc_parent;
+
+void
+enfs_iterate_each_rpc_clnt(int (*fn)(struct rpc_clnt *clnt, void *data),
+			void *data)
+{
+	struct net *net;
+	struct sunrpc_net *sn;
+	struct rpc_clnt *clnt;
+
+	rcu_read_lock();
+	for_each_net_rcu(net) {
+		sn = net_generic(net, sunrpc_net_id);
+		if (sn == NULL)
+			continue;
+		spin_lock(&sn->rpc_client_lock);
+		list_for_each_entry(clnt, &sn->all_clients, cl_clients) {
+			fn(clnt, data);
+		}
+		spin_unlock(&sn->rpc_client_lock);
+	}
+	rcu_read_unlock();
+}
+
+struct proc_dir_entry *enfs_get_proc_parent(void)
+{
+	return enfs_proc_parent;
+}
+
+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;
+}
+
+static bool should_print(const char *name)
+{
+	int i;
+	static const char * const proc_names[] = {
+	    "READ",
+	    "WRITE",
+	};
+
+	if (name == NULL)
+		return false;
+
+	for (i = 0; i < ARRAY_SIZE(proc_names); i++) {
+		if (strcmp(name, proc_names[i]) == 0)
+			return true;
+	}
+	return false;
+}
+
+struct enfs_xprt_iter {
+	unsigned int id;
+	struct seq_file *seq;
+	unsigned int max_addrs_length;
+};
+
+static int debug_show_xprt(struct rpc_clnt *clnt,
+			struct rpc_xprt *xprt,
+			void *data)
+{
+	struct enfs_xprt_context *ctx = NULL;
+
+	if (xprt->multipath_context)
+		ctx = xprt->multipath_context;
+
+	pr_info("	xprt:%p ctx:%p main:%d queue_len:%lu.\n", xprt,
+			xprt->multipath_context,
+			ctx ? ctx->main : false,
+			atomic_long_read(&xprt->queuelen));
+	return 0;
+}
+
+static int debug_show_clnt(struct rpc_clnt *clnt, void *data)
+{
+	pr_info("    clnt %d addr:%p enfs:%d\n",
+			clnt->cl_clid, clnt,
+			clnt->cl_enfs);
+	rpc_clnt_iterate_for_each_xprt(clnt, debug_show_xprt, NULL);
+	return 0;
+}
+
+static void debug_print_all_xprt(void)
+{
+	enfs_iterate_each_rpc_clnt(debug_show_clnt, NULL);
+}
+
+static
+void enfs_proc_format_xprt_addr_display(struct rpc_clnt *clnt,
+			struct rpc_xprt *xprt,
+			char *local_name_buf,
+			int local_name_buf_len,
+			char *remote_name_buf,
+			int remote_name_buf_len)
+{
+	int err;
+	struct sockaddr_storage srcaddr;
+	struct enfs_xprt_context *ctx;
+
+	ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+	sockaddr_ip_to_str((struct sockaddr *)&xprt->addr,
+				remote_name_buf, remote_name_buf_len);
+
+	// get local address depend one main or not
+	if (enfs_is_main_xprt(xprt)) {
+		err = rpc_localaddr(clnt, (struct sockaddr *)&srcaddr,
+					sizeof(srcaddr));
+		if (err != 0)
+			(void)snprintf(local_name_buf,
+						local_name_buf_len, "Unknown");
+		else
+			sockaddr_ip_to_str((struct sockaddr *)&srcaddr,
+						local_name_buf,
+						local_name_buf_len);
+	} else {
+		sockaddr_ip_to_str((struct sockaddr *)&ctx->srcaddr,
+					local_name_buf,
+					local_name_buf_len);
+	}
+}
+
+static int enfs_show_xprt_stats(struct rpc_clnt *clnt,
+			struct rpc_xprt *xprt,
+			void *data)
+{
+	unsigned int op;
+	unsigned int maxproc = clnt->cl_maxproc;
+	struct enfs_xprt_iter *iter = (struct enfs_xprt_iter *)data;
+	struct enfs_xprt_context *ctx;
+	char local_name[INET6_ADDRSTRLEN];
+	char remote_name[INET6_ADDRSTRLEN];
+
+	if (!xprt->multipath_context)
+		return 0;
+
+	ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+	enfs_proc_format_xprt_addr_display(clnt, xprt, local_name,
+					sizeof(local_name),
+					remote_name, sizeof(remote_name));
+
+	seq_printf(iter->seq, "%-6u%-*s%-*s", iter->id,
+			iter->max_addrs_length + 4,
+			local_name,
+			iter->max_addrs_length + 4,
+			remote_name);
+
+	iter->id++;
+
+	for (op = 0; op < maxproc; op++) {
+		if (!should_print(clnt->cl_procinfo[op].p_name))
+			continue;
+
+		seq_printf(iter->seq, "%-22lu%-22Lu%-22Lu",
+			ctx->stats[op].om_ops,
+			ctx->stats[op].om_ops == 0 ? 0 :
+			ktime_to_ms(ctx->stats[op].om_rtt) /
+			ctx->stats[op].om_ops,
+			ctx->stats[op].om_ops == 0 ? 0 :
+			ktime_to_ms(ctx->stats[op].om_execute) /
+			ctx->stats[op].om_ops);
+	}
+	seq_puts(iter->seq, "\n");
+	return 0;
+}
+
+static int rpc_proc_show_path_status(struct rpc_clnt *clnt,
+			struct rpc_xprt *xprt,
+			void *data)
+{
+	struct enfs_xprt_iter *iter = (struct enfs_xprt_iter *)data;
+	struct enfs_xprt_context *ctx = NULL;
+	char local_name[INET6_ADDRSTRLEN] = {0};
+	char remote_name[INET6_ADDRSTRLEN] = {0};
+	char multiapth_status[ENFS_PROC_PATH_STATUS_LEN] = {0};
+	char xprt_status[ENFS_PROC_PATH_STATUS_LEN] = {0};
+
+	if (!xprt->multipath_context) {
+		enfs_log_debug("multipath_context is null.\n");
+		return 0;
+	}
+	ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+	enfs_proc_format_xprt_addr_display(clnt, xprt,
+				local_name,
+				sizeof(local_name),
+				remote_name, sizeof(remote_name));
+
+	pm_get_path_state_desc(xprt,
+				multiapth_status,
+				ENFS_PROC_PATH_STATUS_LEN);
+
+	pm_get_xprt_state_desc(xprt,
+				xprt_status,
+				ENFS_PROC_PATH_STATUS_LEN);
+
+	seq_printf(iter->seq, "%-6u%-*s%-*s%-12s%-12s\n",
+			iter->id, iter->max_addrs_length + 4,
+			local_name, iter->max_addrs_length + 4,
+			remote_name, multiapth_status,
+			xprt_status);
+	iter->id++;
+	return 0;
+}
+
+static int enfs_get_max_addrs_length(struct rpc_clnt *clnt,
+			struct rpc_xprt *xprt,
+			void *data)
+{
+	struct enfs_xprt_iter *iter = (struct enfs_xprt_iter *)data;
+	char local_name[INET6_ADDRSTRLEN];
+	char remote_name[INET6_ADDRSTRLEN];
+
+	enfs_proc_format_xprt_addr_display(clnt, xprt,
+				local_name, sizeof(local_name),
+				remote_name, sizeof(remote_name));
+
+	if (iter->max_addrs_length < strlen(local_name))
+		iter->max_addrs_length = strlen(local_name);
+
+	if (iter->max_addrs_length < strlen(remote_name))
+		iter->max_addrs_length = strlen(remote_name);
+
+	return 0;
+}
+
+static int rpc_proc_clnt_showpath(struct seq_file *seq, void *v)
+{
+	struct rpc_clnt *clnt = seq->private;
+	struct enfs_xprt_iter iter;
+
+	iter.seq = seq;
+	iter.id = 0;
+	iter.max_addrs_length = 0;
+
+	rpc_clnt_iterate_for_each_xprt(clnt,
+				enfs_get_max_addrs_length,
+				(void *)&iter);
+
+	seq_printf(seq, "%-6s%-*s%-*s%-12s%-12s\n", "id",
+			iter.max_addrs_length + 4,
+			"local_addr",
+			iter.max_addrs_length + 4,
+			"remote_addr",
+			"path_state",
+			"xprt_state");
+
+	rpc_clnt_iterate_for_each_xprt(clnt,
+				rpc_proc_show_path_status,
+				(void *)&iter);
+	return 0;
+}
+
+static int enfs_rpc_proc_show(struct seq_file *seq, void *v)
+{
+	struct rpc_clnt *clnt = seq->private;
+	struct enfs_xprt_iter iter;
+
+	iter.seq = seq;
+	iter.id = 0;
+	iter.max_addrs_length = 0;
+
+	debug_print_all_xprt();
+	pr_info("enfs proc clnt:%p\n", clnt);
+
+	rpc_clnt_iterate_for_each_xprt(clnt,
+				enfs_get_max_addrs_length,
+				(void *)&iter);
+
+	seq_printf(seq, "%-6s%-*s%-*s%-22s%-22s%-22s%-22s%-22s%-22s\n", "id",
+			iter.max_addrs_length + 4, "local_addr",
+			iter.max_addrs_length + 4,
+			"remote_addr", "r_count",
+			"r_rtt", "r_exec", "w_count", "w_rtt", "w_exec");
+
+	// rpc_clnt_show_stats(seq, clnt);
+	rpc_clnt_iterate_for_each_xprt(clnt,
+				enfs_show_xprt_stats,
+				(void *)&iter);
+	return 0;
+}
+
+static int rpc_proc_open(struct inode *inode, struct file *file)
+{
+	struct rpc_clnt *clnt = PDE_DATA(inode);
+
+	pr_info("%s %p\n", __func__, clnt);
+	return single_open(file, enfs_rpc_proc_show, clnt);
+}
+
+static int enfs_reset_xprt_stats(struct rpc_clnt *clnt,
+			struct rpc_xprt *xprt,
+			void *data)
+{
+	unsigned int op;
+	struct enfs_xprt_context *ctx;
+	unsigned int maxproc = clnt->cl_maxproc;
+	struct rpc_iostats stats = {0};
+
+	if (!xprt->multipath_context)
+		return 0;
+	ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+	for (op = 0; op < maxproc; op++) {
+		spin_lock(&ctx->stats[op].om_lock);
+		ctx->stats[op] = stats;
+		spin_unlock(&ctx->stats[op].om_lock);
+	}
+	return 0;
+}
+
+static void trim_newline_ch(char *str, int len)
+{
+	int i;
+
+	for (i = 0; str[i] != '\0' && i < len; i++) {
+		if (str[i] == '\n')
+			str[i] = '\0';
+	}
+}
+
+static ssize_t enfs_proc_write(struct file *file,
+			const char __user *user_buf,
+			size_t len,
+			loff_t *offset)
+{
+	char buffer[128];
+	struct rpc_clnt *clnt =
+	((struct seq_file *)file->private_data)->private;
+
+	if (len >= sizeof(buffer))
+		return -E2BIG;
+
+	if (copy_from_user(buffer, user_buf, len) != 0)
+		return -EFAULT;
+
+	buffer[len] = '\0';
+	trim_newline_ch(buffer, len);
+	if (strcmp(buffer, "reset") != 0)
+		return -EINVAL;
+
+	rpc_clnt_iterate_for_each_xprt(clnt, enfs_reset_xprt_stats, NULL);
+	return len;
+}
+
+static int rpc_proc_show_path(struct inode *inode, struct file *file)
+{
+	struct rpc_clnt *clnt = PDE_DATA(inode);
+
+	return single_open(file, rpc_proc_clnt_showpath, clnt);
+}
+
+static const struct file_operations rpc_proc_fops = {
+	.owner = THIS_MODULE,
+	.open = rpc_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = enfs_proc_write,
+};
+
+static const struct file_operations rpc_show_path_fops = {
+	.owner = THIS_MODULE,
+	.open = rpc_proc_show_path,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int clnt_proc_name(struct rpc_clnt *clnt, char *buf, int len)
+{
+	int ret;
+
+	ret = snprintf(buf, len, "%s_%u",
+				rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR),
+				clnt->cl_clid);
+	if (ret > len)
+		return -E2BIG;
+	return 0;
+}
+
+static int enfs_proc_create_file(struct rpc_clnt *clnt)
+{
+	int err;
+	char buf[128];
+
+	struct proc_dir_entry *clnt_entry;
+	struct proc_dir_entry *stat_entry;
+
+	err = clnt_proc_name(clnt, buf, sizeof(buf));
+	if (err)
+		return err;
+
+	clnt_entry = proc_mkdir(buf, enfs_proc_parent);
+	if (clnt_entry == NULL)
+		return -EINVAL;
+
+	stat_entry = proc_create_data("stat",
+				0, clnt_entry,
+				&rpc_proc_fops, clnt);
+
+	if (stat_entry == NULL)
+		return -EINVAL;
+
+	stat_entry = proc_create_data("path",
+				0, clnt_entry,
+				&rpc_show_path_fops, clnt);
+
+	if (stat_entry == NULL)
+		return -EINVAL;
+
+	return 0;
+}
+
+void enfs_count_iostat(struct rpc_task *task)
+{
+	struct enfs_xprt_context *ctx = task->tk_xprt->multipath_context;
+
+	if (!ctx || !ctx->stats)
+		return;
+	rpc_count_iostats(task, ctx->stats);
+}
+
+static void enfs_proc_delete_file(struct rpc_clnt *clnt)
+{
+	int err;
+	char buf[128];
+
+	err = clnt_proc_name(clnt, buf, sizeof(buf));
+	if (err) {
+		pr_err("gen clnt name failed.\n");
+		return;
+	}
+	remove_proc_subtree(buf, enfs_proc_parent);
+}
+
+// create proc file "/porc/enfs/[mount_ip]_[id]/stat"
+int enfs_proc_create_clnt(struct rpc_clnt *clnt)
+{
+	int err;
+
+	err = enfs_proc_create_file(clnt);
+	if (err) {
+		pr_err("create client %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+void enfs_proc_delete_clnt(struct rpc_clnt *clnt)
+{
+	if (clnt->cl_enfs)
+		enfs_proc_delete_file(clnt);
+}
+
+static int enfs_proc_create_parent(void)
+{
+	enfs_proc_parent = proc_mkdir(ENFS_PROC_DIR, NULL);
+
+	if (enfs_proc_parent == NULL) {
+		pr_err("Enfs create proc dir err\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void enfs_proc_delete_parent(void)
+{
+	remove_proc_entry(ENFS_PROC_DIR, NULL);
+}
+
+static int enfs_proc_init_create_clnt(struct rpc_clnt *clnt, void *data)
+{
+	if (clnt->cl_enfs)
+		enfs_proc_create_file(clnt);
+	return 0;
+}
+
+static int enfs_proc_destroy_clnt(struct rpc_clnt *clnt, void *data)
+{
+	if (clnt->cl_enfs)
+		enfs_proc_delete_file(clnt);
+	return 0;
+}
+
+int enfs_proc_init(void)
+{
+	int err;
+
+	err = enfs_proc_create_parent();
+	if (err)
+		return err;
+
+	enfs_iterate_each_rpc_clnt(enfs_proc_init_create_clnt, NULL);
+	return 0;
+}
+
+void enfs_proc_exit(void)
+{
+	enfs_iterate_each_rpc_clnt(enfs_proc_destroy_clnt, NULL);
+	enfs_proc_delete_parent();
+}
diff --git a/fs/nfs/enfs/enfs_proc.h b/fs/nfs/enfs/enfs_proc.h
new file mode 100644
index 000000000..321951031
--- /dev/null
+++ b/fs/nfs/enfs/enfs_proc.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Client-side ENFS PROC.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#ifndef ENFS_PROC_H
+#define ENFS_PROC_H
+
+struct rpc_clnt;
+struct rpc_task;
+struct proc_dir_entry;
+
+int enfs_proc_init(void);
+void enfs_proc_exit(void);
+struct proc_dir_entry *enfs_get_proc_parent(void);
+int enfs_proc_create_clnt(struct rpc_clnt *clnt);
+void enfs_proc_delete_clnt(struct rpc_clnt *clnt);
+void enfs_count_iostat(struct rpc_task *task);
+
+#endif
diff --git a/fs/nfs/enfs/enfs_remount.c b/fs/nfs/enfs/enfs_remount.c
new file mode 100644
index 000000000..3b7d6a03d
--- /dev/null
+++ b/fs/nfs/enfs/enfs_remount.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Description: remount ip source file
+ * Create: 2023-08-12
+ */
+#include "enfs_remount.h"
+
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/spinlock.h>
+#include <linux/sunrpc/addr.h>
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/xprtmultipath.h>
+#include <linux/sunrpc/xprtsock.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_multipath.h"
+#include "enfs_multipath_parse.h"
+#include "enfs_path.h"
+#include "enfs_proc.h"
+#include "enfs_multipath_client.h"
+
+static bool enfs_rpc_xprt_switch_need_delete_addr(
+	struct multipath_mount_options *enfs_option,
+	struct sockaddr *dstaddr, struct sockaddr *srcaddr)
+{
+	int i;
+	bool find_same_ip = false;
+	int32_t local_total;
+	int32_t remote_total;
+
+	local_total = enfs_option->local_ip_list->count;
+	remote_total = enfs_option->remote_ip_list->count;
+	if (local_total == 0 || remote_total == 0) {
+		pr_err("no ip list is present.\n");
+		return false;
+	}
+
+	for (i = 0; i < local_total; i++) {
+		find_same_ip =
+		rpc_cmp_addr((struct sockaddr *)
+		&enfs_option->local_ip_list->address[i],
+		srcaddr);
+		if (find_same_ip)
+			break;
+	}
+
+	if (find_same_ip == false)
+		return true;
+
+	find_same_ip = false;
+	for (i = 0; i < remote_total; i++) {
+		find_same_ip =
+		rpc_cmp_addr((struct sockaddr *)
+		&enfs_option->remote_ip_list->address[i],
+		dstaddr);
+		if (find_same_ip)
+			break;
+	}
+
+	if (find_same_ip == false)
+		return true;
+
+	return false;
+}
+
+// Used in rcu_lock
+static bool enfs_delete_xprt_from_switch(struct rpc_xprt *xprt,
+			void *enfs_option,
+			struct rpc_xprt_switch *xps)
+{
+	struct enfs_xprt_context *ctx = NULL;
+	struct multipath_mount_options *mopt =
+	(struct multipath_mount_options *)enfs_option;
+
+	if (enfs_is_main_xprt(xprt))
+		return true;
+
+	ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+	if (enfs_rpc_xprt_switch_need_delete_addr(mopt,
+		(struct sockaddr *)&xprt->addr,
+		(struct sockaddr *)&ctx->srcaddr)) {
+
+		print_enfs_multipath_addr((struct sockaddr *)&ctx->srcaddr,
+					(struct sockaddr *)&xprt->addr);
+		rpc_xprt_switch_remove_xprt(xps, xprt);
+		return true;
+	}
+
+	return false;
+}
+
+void enfs_clnt_delete_obsolete_xprts(struct nfs_client *nfs_client,
+			void *enfs_option)
+{
+	int xprt_count = 0;
+	struct rpc_xprt *pos = NULL;
+	struct rpc_xprt_switch *xps = NULL;
+
+	rcu_read_lock();
+	xps = xprt_switch_get(
+		rcu_dereference(
+		nfs_client->cl_rpcclient->cl_xpi.xpi_xpswitch));
+	if (xps == NULL) {
+		rcu_read_unlock();
+		xprt_switch_put(xps);
+		return;
+	}
+	list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+		if (xprt_count < MAX_XPRT_NUM_PER_CLIENT) {
+			if (enfs_delete_xprt_from_switch(
+				pos, enfs_option, xps) == false)
+				xprt_count++;
+		} else
+			rpc_xprt_switch_remove_xprt(xps, pos);
+	}
+	rcu_read_unlock();
+	xprt_switch_put(xps);
+}
+
+int enfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option)
+{
+	int errno = 0;
+	char servername[48];
+	struct multipath_mount_options *remount_lists =
+	(struct multipath_mount_options *)enfs_option;
+	struct multipath_client_info *client_info =
+	(struct multipath_client_info *)nfs_client->cl_multipath_data;
+	struct xprt_create xprtargs;
+	struct rpc_create_args args = {
+		.protocol = nfs_client->cl_proto,
+		.net = nfs_client->cl_net,
+		.addrsize = nfs_client->cl_addrlen,
+		.servername = nfs_client->cl_hostname,
+	};
+
+	memset(&xprtargs, 0, sizeof(struct xprt_create));
+
+	//mount is not use multipath
+	if (client_info == NULL || enfs_option == NULL) {
+		enfs_log_error(
+		"mount information or remount information is empty.\n");
+		return -EINVAL;
+	}
+
+	//remount : localaddrs and remoteaddrs are empty
+	if (remount_lists->local_ip_list->count == 0 &&
+		remount_lists->remote_ip_list->count == 0) {
+		enfs_log_info("remount local_ip_list and remote_ip_list are NULL\n");
+		return 0;
+	}
+
+	errno = enfs_config_xprt_create_args(&xprtargs,
+		&args, servername, sizeof(servername));
+
+	if (errno) {
+		enfs_log_error("config_xprt_create failed! errno:%d\n", errno);
+		return errno;
+	}
+
+	if (remount_lists->local_ip_list->count == 0) {
+		if (client_info->local_ip_list->count == 0) {
+			errno = rpc_localaddr(nfs_client->cl_rpcclient,
+				(struct sockaddr *)
+				&remount_lists->local_ip_list->address[0],
+				sizeof(struct sockaddr_storage));
+			if (errno) {
+				enfs_log_error("get clnt srcaddr errno:%d\n",
+				errno);
+				return errno;
+			}
+			remount_lists->local_ip_list->count = 1;
+		} else
+			memcpy(remount_lists->local_ip_list,
+			client_info->local_ip_list,
+			sizeof(struct nfs_ip_list));
+	}
+
+	if (remount_lists->remote_ip_list->count == 0) {
+		if (client_info->remote_ip_list->count == 0) {
+			errno = rpc_peeraddr(nfs_client->cl_rpcclient,
+				(struct sockaddr *)
+				&remount_lists->remote_ip_list->address[0],
+				sizeof(struct sockaddr_storage));
+			if (errno == 0) {
+				enfs_log_error("get clnt dstaddr errno:%d\n",
+				errno);
+				return errno;
+			}
+			remount_lists->remote_ip_list->count = 1;
+		} else
+			memcpy(remount_lists->remote_ip_list,
+				client_info->remote_ip_list,
+				sizeof(struct nfs_ip_list));
+	}
+
+	enfs_log_info("Remount creating new links...\n");
+	enfs_xprt_ippair_create(&xprtargs,
+				nfs_client->cl_rpcclient,
+				remount_lists);
+
+	enfs_log_info("Remount deleting obsolete links...\n");
+	enfs_clnt_delete_obsolete_xprts(nfs_client, remount_lists);
+
+	memcpy(client_info->local_ip_list,
+		remount_lists->local_ip_list,
+		sizeof(struct nfs_ip_list));
+	memcpy(client_info->remote_ip_list,
+		remount_lists->remote_ip_list,
+		sizeof(struct nfs_ip_list));
+
+	return 0;
+}
diff --git a/fs/nfs/enfs/enfs_remount.h b/fs/nfs/enfs/enfs_remount.h
new file mode 100644
index 000000000..c7a1dcbb6
--- /dev/null
+++ b/fs/nfs/enfs/enfs_remount.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Description: remount ip header file
+ * Create: 2023-08-12
+ */
+#ifndef _ENFS_REMOUNT_
+#define _ENFS_REMOUNT_
+#include <linux/string.h>
+#include "enfs.h"
+
+int enfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option);
+
+#endif
diff --git a/fs/nfs/enfs/enfs_roundrobin.c b/fs/nfs/enfs/enfs_roundrobin.c
new file mode 100644
index 000000000..4e4eda784
--- /dev/null
+++ b/fs/nfs/enfs/enfs_roundrobin.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/kref.h>
+#include <linux/rculist.h>
+#include <linux/types.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprtmultipath.h>
+#include "enfs_roundrobin.h"
+
+#include "enfs.h"
+#include "enfs_config.h"
+#include "pm_state.h"
+
+typedef struct rpc_xprt *(*enfs_xprt_switch_find_xprt_t)(
+	struct rpc_xprt_switch *xps, const struct rpc_xprt *cur);
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_roundrobin;
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_singular;
+
+static bool enfs_xprt_is_active(struct rpc_xprt *xprt)
+{
+	enum pm_path_state state;
+
+	if (kref_read(&xprt->kref) <= 0)
+		return false;
+
+	state = pm_get_path_state(xprt);
+	if (state == PM_STATE_NORMAL)
+		return true;
+
+	return false;
+}
+
+static struct rpc_xprt *enfs_lb_set_cursor_xprt(
+	struct rpc_xprt_switch *xps, struct rpc_xprt **cursor,
+	enfs_xprt_switch_find_xprt_t find_next)
+{
+	struct rpc_xprt *pos;
+	struct rpc_xprt *old;
+
+	old = smp_load_acquire(cursor); /* read latest cursor */
+	pos = find_next(xps, old);
+	smp_store_release(cursor, pos); /* let cursor point to pos */
+	return pos;
+}
+
+static
+struct rpc_xprt *enfs_lb_find_next_entry_roundrobin(
+		struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+	struct rpc_xprt *pos;
+	struct rpc_xprt *prev = NULL;
+	bool found = false;
+	struct rpc_xprt *min_queuelen_xprt = NULL;
+	unsigned long pos_xprt_queuelen;
+	unsigned long min_xprt_queuelen = 0;
+
+	unsigned long xps_queuelen = atomic_long_read(&xps->xps_queuelen);
+	// delete origin xprt
+	unsigned int multipath_nactive = READ_ONCE(xps->xps_nactive) - 1;
+
+	list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+		if (enfs_is_main_xprt(pos) || !enfs_xprt_is_active(pos)) {
+			prev = pos;
+			continue;
+		}
+
+		pos_xprt_queuelen = atomic_long_read(&pos->queuelen);
+		if (min_queuelen_xprt == NULL ||
+			pos_xprt_queuelen < min_xprt_queuelen) {
+
+			min_queuelen_xprt = pos;
+			min_xprt_queuelen = pos_xprt_queuelen;
+		}
+
+		if (cur == prev)
+			found = true;
+
+		if (found && pos_xprt_queuelen *
+			multipath_nactive <= xps_queuelen)
+			return pos;
+		prev = pos;
+	};
+
+	return min_queuelen_xprt;
+}
+
+struct rpc_xprt *enfs_lb_switch_find_first_active_xprt(
+			struct rpc_xprt_switch *xps)
+{
+	struct rpc_xprt *pos;
+
+	list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+		if (enfs_xprt_is_active(pos))
+			return pos;
+	};
+	return NULL;
+}
+
+struct rpc_xprt *enfs_lb_switch_get_main_xprt(struct rpc_xprt_switch *xps)
+{
+	return list_first_or_null_rcu(&xps->xps_xprt_list,
+				struct rpc_xprt, xprt_switch);
+}
+
+static struct rpc_xprt *enfs_lb_switch_get_next_xprt_roundrobin(
+			struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+	struct rpc_xprt *xprt;
+
+	// disable multipath
+	if (enfs_get_config_multipath_state())
+		return enfs_lb_switch_get_main_xprt(xps);
+
+	xprt = enfs_lb_find_next_entry_roundrobin(xps, cur);
+	if (xprt != NULL)
+		return xprt;
+
+	return enfs_lb_switch_get_main_xprt(xps);
+}
+
+static
+struct rpc_xprt *enfs_lb_iter_next_entry_roundrobin(struct rpc_xprt_iter *xpi)
+{
+	struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
+
+	if (xps == NULL)
+		return NULL;
+
+	return enfs_lb_set_cursor_xprt(xps, &xpi->xpi_cursor,
+				enfs_lb_switch_get_next_xprt_roundrobin);
+}
+
+static
+struct rpc_xprt *enfs_lb_switch_find_singular_entry(
+			struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+	struct rpc_xprt *pos;
+	bool found = false;
+
+	list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+		if (cur == pos)
+			found = true;
+
+		if (found && enfs_xprt_is_active(pos))
+			return pos;
+	}
+	return NULL;
+}
+
+struct rpc_xprt *enfs_lb_get_singular_xprt(
+			struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+	struct rpc_xprt *xprt;
+
+	if (xps == NULL)
+		return NULL;
+
+	// disable multipath
+	if (enfs_get_config_multipath_state())
+		return enfs_lb_switch_get_main_xprt(xps);
+
+	if (cur == NULL || xps->xps_nxprts < 2)
+		return enfs_lb_switch_find_first_active_xprt(xps);
+
+	xprt = enfs_lb_switch_find_singular_entry(xps, cur);
+	if (!xprt)
+		return enfs_lb_switch_get_main_xprt(xps);
+
+	return xprt;
+}
+
+static
+struct rpc_xprt *enfs_lb_iter_next_entry_sigular(struct rpc_xprt_iter *xpi)
+{
+	struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
+
+	if (xps == NULL)
+		return NULL;
+
+	return enfs_lb_set_cursor_xprt(xps, &xpi->xpi_cursor,
+				enfs_lb_get_singular_xprt);
+}
+
+static void enfs_lb_iter_default_rewind(struct rpc_xprt_iter *xpi)
+{
+	WRITE_ONCE(xpi->xpi_cursor, NULL);
+}
+
+static void enfs_lb_switch_set_roundrobin(struct rpc_clnt *clnt)
+{
+	struct rpc_xprt_switch *xps;
+
+	rcu_read_lock();
+	xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+	rcu_read_unlock();
+	if (clnt->cl_vers == 3) {
+
+		if (READ_ONCE(xps->xps_iter_ops) != &enfs_xprt_iter_roundrobin)
+			WRITE_ONCE(xps->xps_iter_ops,
+			&enfs_xprt_iter_roundrobin);
+
+		return;
+	}
+	if (READ_ONCE(xps->xps_iter_ops) != &enfs_xprt_iter_singular)
+		WRITE_ONCE(xps->xps_iter_ops, &enfs_xprt_iter_singular);
+}
+
+static
+struct rpc_xprt *enfs_lb_switch_find_current(struct list_head *head,
+			const struct rpc_xprt *cur)
+{
+	struct rpc_xprt *pos;
+
+	list_for_each_entry_rcu(pos, head, xprt_switch) {
+		if (cur == pos)
+			return pos;
+	}
+	return NULL;
+}
+
+static struct rpc_xprt *enfs_lb_iter_current_entry(struct rpc_xprt_iter *xpi)
+{
+	struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
+	struct list_head *head;
+
+	if (xps == NULL)
+		return NULL;
+	head = &xps->xps_xprt_list;
+	if (xpi->xpi_cursor == NULL || xps->xps_nxprts < 2)
+		return enfs_lb_switch_get_main_xprt(xps);
+	return enfs_lb_switch_find_current(head, xpi->xpi_cursor);
+}
+
+void enfs_lb_set_policy(struct rpc_clnt *clnt)
+{
+	enfs_lb_switch_set_roundrobin(clnt);
+}
+
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_roundrobin = {
+	.xpi_rewind = enfs_lb_iter_default_rewind,
+	.xpi_xprt = enfs_lb_iter_current_entry,
+	.xpi_next = enfs_lb_iter_next_entry_roundrobin,
+};
+
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_singular = {
+	.xpi_rewind = enfs_lb_iter_default_rewind,
+	.xpi_xprt = enfs_lb_iter_current_entry,
+	.xpi_next = enfs_lb_iter_next_entry_sigular,
+};
diff --git a/fs/nfs/enfs/enfs_roundrobin.h b/fs/nfs/enfs/enfs_roundrobin.h
new file mode 100644
index 000000000..b72b088a6
--- /dev/null
+++ b/fs/nfs/enfs/enfs_roundrobin.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#ifndef ENFS_ROUNDROBIN_H
+#define ENFS_ROUNDROBIN_H
+
+void enfs_lb_set_policy(struct rpc_clnt *clnt);
+#endif
