diff --git a/fs/nfs/enfs/Makefile b/fs/nfs/enfs/Makefile
new file mode 100644
index 000000000000..6e83eb23c668
--- /dev/null
+++ b/fs/nfs/enfs/Makefile
@@ -0,0 +1,18 @@
+obj-m   += enfs.o
+
+#EXTRA_CFLAGS += -I$(PWD)/..
+
+enfs-y := enfs_init.o
+enfs-y += enfs_config.o
+enfs-y += mgmt_init.o
+enfs-y += enfs_multipath_client.o
+enfs-y += enfs_multipath_parse.o
+enfs-y += failover_path.o
+enfs-y += failover_time.o
+enfs-y += enfs_roundrobin.o
+enfs-y += enfs_multipath.o
+enfs-y += enfs_path.o
+enfs-y += enfs_proc.o
+enfs-y += enfs_remount.o
+enfs-y += pm_ping.o
+enfs-y += pm_state.o
diff --git a/fs/nfs/enfs/enfs.h b/fs/nfs/enfs/enfs.h
new file mode 100644
index 000000000000..be3d95220088
--- /dev/null
+++ b/fs/nfs/enfs/enfs.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Client-side ENFS multipath adapt header.
+ *
+ *  Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+
+#ifndef _ENFS_H_
+#define _ENFS_H_
+#include <linux/atomic.h>
+#include <linux/nfs.h>
+#include <linux/nfs4.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include "../enfs_adapter.h"
+
+#define IP_ADDRESS_LEN_MAX 64
+#define MAX_IP_PAIR_PER_MOUNT 8
+#define MAX_IP_INDEX (MAX_IP_PAIR_PER_MOUNT)
+#define MAX_SUPPORTED_LOCAL_IP_COUNT 8
+#define MAX_SUPPORTED_REMOTE_IP_COUNT 32
+#define MAX_DNS_NAME_LEN 512
+#define MAX_DNS_SUPPORTED 2
+#define EXTEND_CMD_MAX_BUF_LEN 65356
+
+
+struct nfs_ip_list {
+	int count;
+	struct sockaddr_storage address[MAX_SUPPORTED_REMOTE_IP_COUNT];
+	size_t addrlen[MAX_SUPPORTED_REMOTE_IP_COUNT];
+};
+
+struct NFS_ROUTE_DNS_S {
+	char dnsname[MAX_DNS_NAME_LEN];  // valid only if dnsExist is true
+};
+
+struct NFS_ROUTE_DNS_INFO_S {
+	int dnsNameCount; // Count of DNS name in the list
+	// valid only if dnsExist is true
+	struct NFS_ROUTE_DNS_S routeRemoteDnsList[MAX_DNS_SUPPORTED];
+};
+
+struct rpc_iostats;
+struct enfs_xprt_context {
+	struct sockaddr_storage	srcaddr;
+	struct rpc_iostats *stats;
+	bool main;
+	atomic_t path_state;
+	atomic_t path_check_state;
+};
+
+static inline bool enfs_is_main_xprt(struct rpc_xprt *xprt)
+{
+	struct enfs_xprt_context *ctx = xprt->multipath_context;
+
+	if (!ctx)
+		return false;
+	return ctx->main;
+}
+
+#endif
diff --git a/fs/nfs/enfs/enfs_init.c b/fs/nfs/enfs/enfs_init.c
new file mode 100644
index 000000000000..4b55608191a7
--- /dev/null
+++ b/fs/nfs/enfs/enfs_init.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Client-side ENFS adapter.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs4.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include "enfs.h"
+#include "enfs_multipath_parse.h"
+#include "enfs_multipath_client.h"
+#include "enfs_remount.h"
+#include "init.h"
+#include "enfs_log.h"
+#include "enfs_multipath.h"
+#include "mgmt_init.h"
+
+struct enfs_adapter_ops enfs_adapter = {
+	.name				= "enfs",
+	.owner				= THIS_MODULE,
+	.parse_mount_options     = nfs_multipath_parse_options,
+	.free_mount_options      = nfs_multipath_free_options,
+	.client_info_init        = nfs_multipath_client_info_init,
+	.client_info_free        = nfs_multipath_client_info_free,
+	.client_info_match       = nfs_multipath_client_info_match,
+	.client_info_show        = nfs_multipath_client_info_show,
+	.remount_ip_list         = enfs_remount_iplist,
+};
+
+int32_t enfs_init(void)
+{
+	int err;
+
+	err = enfs_multipath_init();
+	if (err) {
+		enfs_log_error("init multipath failed.\n");
+		goto out;
+	}
+
+	err = mgmt_init();
+	if (err != 0) {
+		enfs_log_error("init mgmt failed.\n");
+		goto out_tp_exit;
+	}
+
+	return 0;
+
+out_tp_exit:
+	enfs_multipath_exit();
+out:
+	return err;
+}
+
+void enfs_fini(void)
+{
+	mgmt_fini();
+
+	enfs_multipath_exit();
+}
+
+static int __init init_enfs(void)
+{
+	int ret;
+
+	ret = enfs_adapter_register(&enfs_adapter);
+	if (ret) {
+		pr_err("regist enfs_adapter fail. ret %d\n", ret);
+		return -1;
+	}
+
+		ret = enfs_init();
+		if (ret) {
+			enfs_adapter_unregister(&enfs_adapter);
+		return -1;
+		}
+
+	return 0;
+}
+
+static void __exit exit_enfs(void)
+{
+	enfs_fini();
+	enfs_adapter_unregister(&enfs_adapter);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
+MODULE_DESCRIPTION("Nfs client router");
+MODULE_VERSION("1.0");
+
+module_init(init_enfs);
+module_exit(exit_enfs);
diff --git a/fs/nfs/enfs/enfs_multipath_client.c b/fs/nfs/enfs/enfs_multipath_client.c
new file mode 100644
index 000000000000..63c02898a42c
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath_client.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Client-side ENFS adapter.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#include <linux/types.h>
+#include <linux/nfs.h>
+#include <linux/nfs4.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
+#include "enfs_multipath_client.h"
+#include "enfs_multipath_parse.h"
+
+int
+nfs_multipath_client_mount_info_init(struct multipath_client_info *client_info,
+	const struct nfs_client_initdata *client_init_data)
+{
+	struct multipath_mount_options *mount_options =
+		(struct multipath_mount_options *)client_init_data->enfs_option;
+
+	if (mount_options->local_ip_list) {
+		client_info->local_ip_list =
+		kzalloc(sizeof(struct nfs_ip_list), GFP_KERNEL);
+
+		if (!client_info->local_ip_list)
+			return -ENOMEM;
+
+		memcpy(client_info->local_ip_list, mount_options->local_ip_list,
+			sizeof(struct nfs_ip_list));
+	}
+
+	if (mount_options->remote_ip_list) {
+
+		client_info->remote_ip_list =
+		kzalloc(sizeof(struct nfs_ip_list), GFP_KERNEL);
+
+		if (!client_info->remote_ip_list) {
+			kfree(client_info->local_ip_list);
+			client_info->local_ip_list = NULL;
+			return -ENOMEM;
+		}
+		memcpy(client_info->remote_ip_list,
+			mount_options->remote_ip_list,
+			sizeof(struct nfs_ip_list));
+	}
+
+	if (mount_options->pRemoteDnsInfo) {
+		client_info->pRemoteDnsInfo =
+		kzalloc(sizeof(struct NFS_ROUTE_DNS_INFO_S), GFP_KERNEL);
+
+		if (!client_info->pRemoteDnsInfo) {
+			kfree(client_info->local_ip_list);
+			client_info->local_ip_list = NULL;
+			kfree(client_info->remote_ip_list);
+			client_info->remote_ip_list = NULL;
+			return -ENOMEM;
+		}
+		memcpy(client_info->pRemoteDnsInfo,
+		mount_options->pRemoteDnsInfo,
+		sizeof(struct NFS_ROUTE_DNS_INFO_S));
+	}
+	return 0;
+}
+
+void nfs_multipath_client_info_free_work(struct work_struct *work)
+{
+
+	struct multipath_client_info *clp_info;
+
+	if (work == NULL)
+		return;
+
+	clp_info = container_of(work, struct multipath_client_info, work);
+
+	if (clp_info->local_ip_list != NULL) {
+		kfree(clp_info->local_ip_list);
+		clp_info->local_ip_list = NULL;
+	}
+	if (clp_info->remote_ip_list != NULL) {
+		kfree(clp_info->remote_ip_list);
+		clp_info->remote_ip_list = NULL;
+	}
+	kfree(clp_info);
+}
+
+void nfs_multipath_client_info_free(void *data)
+{
+	struct multipath_client_info *clp_info =
+		(struct multipath_client_info *)data;
+
+	if (clp_info == NULL)
+		return;
+	pr_info("free client info %p.\n", clp_info);
+	INIT_WORK(&clp_info->work, nfs_multipath_client_info_free_work);
+	schedule_work(&clp_info->work);
+}
+
+int nfs_multipath_client_info_init(void **data,
+			const struct nfs_client_initdata *cl_init)
+{
+	int rc;
+	struct multipath_client_info *info;
+	struct multipath_client_info **enfs_info;
+    /* no multi path info, no need do multipath init */
+	if (cl_init->enfs_option == NULL)
+		return 0;
+	enfs_info = (struct multipath_client_info **)data;
+	if (enfs_info == NULL)
+		return -EINVAL;
+
+	if (*enfs_info == NULL)
+		*enfs_info = kzalloc(sizeof(struct multipath_client_info),
+						GFP_KERNEL);
+
+	if (*enfs_info == NULL)
+		return -ENOMEM;
+
+	info = (struct multipath_client_info *)*enfs_info;
+	pr_info("init client info %p.\n", info);
+	rc = nfs_multipath_client_mount_info_init(info, cl_init);
+	if (rc) {
+		nfs_multipath_client_info_free((void *)info);
+		return rc;
+	}
+	return rc;
+}
+
+bool nfs_multipath_ip_list_info_match(const struct nfs_ip_list *ip_list_src,
+	const struct nfs_ip_list *ip_list_dst)
+{
+	int i;
+	int j;
+	bool is_find;
+	/* if both are equal or NULL, then return true. */
+	if (ip_list_src == ip_list_dst)
+		return true;
+
+	if ((ip_list_src == NULL || ip_list_dst == NULL))
+		return false;
+
+	if (ip_list_src->count != ip_list_dst->count)
+		return false;
+
+	for (i = 0; i < ip_list_src->count; i++) {
+		is_find = false;
+		for (j = 0; j < ip_list_src->count; j++) {
+			if (rpc_cmp_addr_port(
+				(const struct sockaddr *)
+				&ip_list_src->address[i],
+				(const struct sockaddr *)
+				&ip_list_dst->address[j])
+				) {
+				is_find = true;
+				break;
+			}
+		}
+		if (is_find == false)
+			return false;
+	}
+	return true;
+}
+
+int
+nfs_multipath_dns_list_info_match(
+			const struct NFS_ROUTE_DNS_INFO_S *pRemoteDnsInfoSrc,
+			const struct NFS_ROUTE_DNS_INFO_S *pRemoteDnsInfoDst)
+{
+	int i;
+
+	/* if both are equal or NULL, then return true. */
+	if (pRemoteDnsInfoSrc == pRemoteDnsInfoDst)
+		return true;
+
+	if ((pRemoteDnsInfoSrc == NULL || pRemoteDnsInfoDst == NULL))
+		return false;
+
+	if (pRemoteDnsInfoSrc->dnsNameCount != pRemoteDnsInfoDst->dnsNameCount)
+		return false;
+
+	for (i = 0; i < pRemoteDnsInfoSrc->dnsNameCount; i++) {
+		if (!strcmp(pRemoteDnsInfoSrc->routeRemoteDnsList[i].dnsname,
+		pRemoteDnsInfoDst->routeRemoteDnsList[i].dnsname))
+			return false;
+	}
+	return true;
+}
+
+int nfs_multipath_client_info_match(void *src, void *dst)
+{
+	int ret = true;
+
+	struct multipath_client_info *src_info;
+	struct multipath_mount_options *dst_info;
+
+	src_info = (struct multipath_client_info *)src;
+	dst_info = (struct multipath_mount_options *)dst;
+	pr_info("try match client .\n");
+	ret = nfs_multipath_ip_list_info_match(src_info->local_ip_list,
+				dst_info->local_ip_list);
+	if (ret == false) {
+		pr_err("local_ip not match.\n");
+		return ret;
+	}
+
+	ret = nfs_multipath_ip_list_info_match(src_info->remote_ip_list,
+			dst_info->remote_ip_list);
+	if (ret == false) {
+		pr_err("remote_ip not match.\n");
+		return ret;
+	}
+
+	ret = nfs_multipath_dns_list_info_match(src_info->pRemoteDnsInfo,
+			dst_info->pRemoteDnsInfo);
+	if (ret == false) {
+		pr_err("dns not match.\n");
+		return ret;
+	}
+	pr_info("try match client ret %d.\n", ret);
+	return ret;
+}
+
+void nfs_multipath_print_ip_info(struct seq_file *mount_option,
+			struct nfs_ip_list *ip_list,
+			const char *type)
+{
+	char buf[IP_ADDRESS_LEN_MAX + 1];
+	int len = 0;
+	int i = 0;
+
+	seq_printf(mount_option, ",%s=", type);
+	for (i = 0; i < ip_list->count; i++) {
+		len = rpc_ntop((struct sockaddr *)&ip_list->address[i],
+				buf, IP_ADDRESS_LEN_MAX);
+		if (len > 0 && len < IP_ADDRESS_LEN_MAX)
+			buf[len] = '\0';
+
+		if (i == 0)
+			seq_printf(mount_option, "%s", buf);
+		else
+			seq_printf(mount_option, "~%s", buf);
+		dfprintk(MOUNT,
+			"NFS:   show nfs mount option type:%s %s [%s]\n",
+			type, buf, __func__);
+	}
+}
+
+void nfs_multipath_print_dns_info(struct seq_file *mount_option,
+			struct NFS_ROUTE_DNS_INFO_S *pRemoteDnsInfo,
+			const char *type)
+{
+	int i = 0;
+
+	seq_printf(mount_option, ",%s=", type);
+	for (i = 0; i < pRemoteDnsInfo->dnsNameCount; i++) {
+		if (i == 0)
+			seq_printf(mount_option,
+			"[%s", pRemoteDnsInfo->routeRemoteDnsList[i].dnsname);
+		else if (i == pRemoteDnsInfo->dnsNameCount - 1)
+			seq_printf(mount_option, ",%s]",
+			pRemoteDnsInfo->routeRemoteDnsList[i].dnsname);
+		else
+			seq_printf(mount_option,
+			",%s", pRemoteDnsInfo->routeRemoteDnsList[i].dnsname);
+	}
+}
+
+
+static void multipath_print_sockaddr(struct seq_file *seq,
+			struct sockaddr *addr)
+{
+	switch (addr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+
+		seq_printf(seq, "%pI4", &sin->sin_addr);
+		return;
+	}
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+
+		seq_printf(seq, "%pI6", &sin6->sin6_addr);
+		return;
+	}
+	default:
+		break;
+	}
+	pr_err("unsupport family:%d\n", addr->sa_family);
+}
+
+static void multipath_print_enfs_info(struct seq_file *seq,
+			struct nfs_server *server)
+{
+	struct sockaddr_storage peeraddr;
+	struct rpc_clnt *next = server->client;
+
+	rpc_peeraddr(server->client,
+			(struct sockaddr *)&peeraddr, sizeof(peeraddr));
+	seq_puts(seq, ",enfs_info=");
+	multipath_print_sockaddr(seq, (struct sockaddr *)&peeraddr);
+
+	while (next->cl_parent) {
+		if (next == next->cl_parent)
+			break;
+		next = next->cl_parent;
+	}
+	seq_printf(seq, "_%u", next->cl_clid);
+}
+
+void nfs_multipath_client_info_show(struct seq_file *mount_option, void *data)
+{
+	struct nfs_server *server = data;
+	struct multipath_client_info *client_info =
+			server->nfs_client->cl_multipath_data;
+
+	dfprintk(MOUNT, "NFS:   show nfs mount option[%s]\n", __func__);
+	if ((client_info->remote_ip_list) &&
+		(client_info->remote_ip_list->count > 0))
+		nfs_multipath_print_ip_info(mount_option,
+			client_info->remote_ip_list,
+			"remoteaddrs");
+
+	if ((client_info->local_ip_list) &&
+		(client_info->local_ip_list->count > 0))
+		nfs_multipath_print_ip_info(mount_option,
+			client_info->local_ip_list,
+			"localaddrs");
+
+	if ((client_info->pRemoteDnsInfo) &&
+		(client_info->pRemoteDnsInfo->dnsNameCount > 0))
+		nfs_multipath_print_dns_info(mount_option,
+			client_info->pRemoteDnsInfo,
+			"remotednsname");
+
+	multipath_print_enfs_info(mount_option, server);
+}
diff --git a/fs/nfs/enfs/enfs_multipath_client.h b/fs/nfs/enfs/enfs_multipath_client.h
new file mode 100644
index 000000000000..208f7260690d
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath_client.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Client-side ENFS adapter.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#ifndef _ENFS_MULTIPATH_CLIENT_H_
+#define _ENFS_MULTIPATH_CLIENT_H_
+
+#include "enfs.h"
+
+struct multipath_client_info {
+	struct work_struct   work;
+	struct nfs_ip_list  *remote_ip_list;
+	struct nfs_ip_list  *local_ip_list;
+	struct NFS_ROUTE_DNS_INFO_S    *pRemoteDnsInfo;
+	s64 client_id;
+};
+
+int nfs_multipath_client_info_init(void **data,
+			const struct nfs_client_initdata *cl_init);
+void nfs_multipath_client_info_free(void *data);
+int nfs_multipath_client_info_match(void *src, void *dst);
+void nfs_multipath_client_info_show(struct seq_file *mount_option, void *data);
+
+#endif
diff --git a/fs/nfs/enfs/enfs_multipath_parse.c b/fs/nfs/enfs/enfs_multipath_parse.c
new file mode 100644
index 000000000000..868782b0a628
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath_parse.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Client-side ENFS adapter.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#include <linux/types.h>
+#include <linux/nfs.h>
+#include <linux/nfs4.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/kern_levels.h>
+#include <linux/sunrpc/addr.h>
+#include "enfs_multipath_parse.h"
+#include "enfs_log.h"
+
+#define NFSDBG_FACILITY NFSDBG_CLIENT
+
+void nfs_multipath_parse_ip_ipv6_add(struct sockaddr_in6 *sin6, int add_num)
+{
+	int i;
+
+	pr_info("NFS:  before %08x%08x%08x%08x  add_num: %d[%s]\n",
+		ntohl(sin6->sin6_addr.in6_u.u6_addr32[0]),
+		ntohl(sin6->sin6_addr.in6_u.u6_addr32[1]),
+		ntohl(sin6->sin6_addr.in6_u.u6_addr32[2]),
+		ntohl(sin6->sin6_addr.in6_u.u6_addr32[3]),
+		add_num, __func__);
+	for (i = 0; i < add_num; i++) {
+		sin6->sin6_addr.in6_u.u6_addr32[3] =
+			htonl(ntohl(sin6->sin6_addr.in6_u.u6_addr32[3]) + 1);
+
+		if (sin6->sin6_addr.in6_u.u6_addr32[3] != 0)
+			continue;
+
+		sin6->sin6_addr.in6_u.u6_addr32[2] =
+			htonl(ntohl(sin6->sin6_addr.in6_u.u6_addr32[2]) + 1);
+
+		if (sin6->sin6_addr.in6_u.u6_addr32[2] != 0)
+			continue;
+
+		sin6->sin6_addr.in6_u.u6_addr32[1] =
+			htonl(ntohl(sin6->sin6_addr.in6_u.u6_addr32[1]) + 1);
+
+		if (sin6->sin6_addr.in6_u.u6_addr32[1] != 0)
+			continue;
+
+		sin6->sin6_addr.in6_u.u6_addr32[0] =
+			htonl(ntohl(sin6->sin6_addr.in6_u.u6_addr32[0]) + 1);
+
+		if (sin6->sin6_addr.in6_u.u6_addr32[0] != 0)
+			continue;
+	}
+
+	return;
+
+}
+
+static int nfs_multipath_parse_ip_range(struct net *net_ns, const char *cursor,
+	struct nfs_ip_list *ip_list, enum nfs_multi_path_options type)
+{
+	struct sockaddr_storage addr;
+	struct sockaddr_storage tmp_addr;
+	int i;
+	size_t len;
+	int add_num = 1;
+	bool duplicate_flag = false;
+	bool is_complete = false;
+	struct sockaddr_in *sin4;
+	struct sockaddr_in6 *sin6;
+
+	pr_info("NFS:   parsing nfs mount option '%s' type: %d[%s]\n",
+			cursor, type, __func__);
+	len = rpc_pton(net_ns, cursor, strlen(cursor),
+				(struct sockaddr *)&addr, sizeof(addr));
+	if (!len)
+		return -EINVAL;
+
+	if (addr.ss_family != ip_list->address[ip_list->count - 1].ss_family) {
+		pr_info("NFS: %s  parsing nfs mount option type: %d fail.\n",
+				__func__, type);
+		return -EINVAL;
+	}
+
+	if (rpc_cmp_addr((const struct sockaddr *)
+		&ip_list->address[ip_list->count - 1],
+		(const struct sockaddr *)&addr)) {
+
+		pr_info("range ip is same ip.\n");
+		return 0;
+
+	}
+
+	while (true) {
+
+		tmp_addr = ip_list->address[ip_list->count - 1];
+
+		switch (addr.ss_family) {
+		case AF_INET:
+			sin4 = (struct sockaddr_in *)&tmp_addr;
+
+			sin4->sin_addr.s_addr =
+			htonl(ntohl(sin4->sin_addr.s_addr) + add_num);
+
+			pr_info("NFS: mount option ip%08x type: %d ipcont %d [%s]\n",
+					ntohl(sin4->sin_addr.s_addr),
+					type, ip_list->count, __func__);
+			break;
+		case AF_INET6:
+			sin6 = (struct sockaddr_in6 *)&tmp_addr;
+			nfs_multipath_parse_ip_ipv6_add(sin6, add_num);
+			pr_info("NFS: mount option ip %08x%08x%08x%08x type: %d ipcont %d [%s]\n",
+			ntohl(sin6->sin6_addr.in6_u.u6_addr32[0]),
+			ntohl(sin6->sin6_addr.in6_u.u6_addr32[1]),
+			ntohl(sin6->sin6_addr.in6_u.u6_addr32[2]),
+			ntohl(sin6->sin6_addr.in6_u.u6_addr32[3]),
+			type, ip_list->count, __func__);
+			break;
+			// return -EOPNOTSUPP;
+		default:
+			return -EOPNOTSUPP;
+			}
+
+		if (rpc_cmp_addr((const struct sockaddr *)&tmp_addr,
+			(const struct sockaddr *)&addr)) {
+			is_complete = true;
+		}
+		// delete duplicate ip, continuosly repeat, skip it
+		for (i = 0; i < ip_list->count; i++) {
+			duplicate_flag = false;
+			if (rpc_cmp_addr((const struct sockaddr *)
+				&ip_list->address[i],
+				(const struct sockaddr *)&tmp_addr)) {
+				add_num++;
+				duplicate_flag = true;
+				break;
+			}
+		}
+
+		if (duplicate_flag == false) {
+			pr_info("this ip not duplicate;");
+			add_num = 1;
+			// if not repeat but omit limit return false
+		if ((type == LOCALADDR &&
+			ip_list->count >= MAX_SUPPORTED_LOCAL_IP_COUNT) ||
+			(type == REMOTEADDR &&
+			ip_list->count >= MAX_SUPPORTED_REMOTE_IP_COUNT)) {
+
+			pr_info("[MULTIPATH:%s] iplist for type %d reached %d, more than supported limit %d\n",
+			__func__, type, ip_list->count,
+			type == LOCALADDR ?
+			MAX_SUPPORTED_LOCAL_IP_COUNT :
+			MAX_SUPPORTED_REMOTE_IP_COUNT);
+			ip_list->count = 0;
+			return -ENOSPC;
+		}
+			ip_list->address[ip_list->count] = tmp_addr;
+
+			ip_list->addrlen[ip_list->count] =
+			ip_list->addrlen[ip_list->count - 1];
+
+			ip_list->count += 1;
+		}
+		if (is_complete == true)
+			break;
+	}
+	return 0;
+}
+
+int nfs_multipath_parse_ip_list_inter(struct nfs_ip_list *ip_list,
+			struct net *net_ns,
+			char *cursor, enum nfs_multi_path_options type)
+{
+	int i = 0;
+	struct sockaddr_storage addr;
+	struct sockaddr_storage swap;
+	int len;
+
+	pr_info("NFS:   parsing nfs mount option '%s' type: %d[%s]\n",
+			cursor, type, __func__);
+
+	len = rpc_pton(net_ns, cursor,
+				strlen(cursor),
+				(struct sockaddr *)&addr, sizeof(addr));
+	if (!len)
+		return -EINVAL;
+
+	// check repeated ip
+	for (i = 0; i < ip_list->count; i++) {
+		if (rpc_cmp_addr((const struct sockaddr *)
+			&ip_list->address[i],
+			(const struct sockaddr *)&addr)) {
+
+			pr_info("NFS: mount option '%s' type:%d index %d same as before index %d [%s]\n",
+			cursor, type, ip_list->count, i, __func__);
+			// prevent this ip is beginning
+			// if repeated take it to the end of list
+			swap = ip_list->address[i];
+
+			ip_list->address[i] =
+				ip_list->address[ip_list->count-1];
+
+			ip_list->address[ip_list->count-1] = swap;
+			return 0;
+		}
+	}
+	// if not repeated, check exceed limit
+		if ((type == LOCALADDR &&
+			ip_list->count >= MAX_SUPPORTED_LOCAL_IP_COUNT) ||
+			(type == REMOTEADDR &&
+			ip_list->count >= MAX_SUPPORTED_REMOTE_IP_COUNT)) {
+
+			pr_info("[MULTIPATH:%s] iplist for type %d reached %d, more than supported limit %d\n",
+			__func__, type, ip_list->count,
+			type == LOCALADDR ?
+			MAX_SUPPORTED_LOCAL_IP_COUNT :
+			MAX_SUPPORTED_REMOTE_IP_COUNT);
+
+			ip_list->count = 0;
+			return -ENOSPC;
+		}
+	ip_list->address[ip_list->count] = addr;
+	ip_list->addrlen[ip_list->count] = len;
+	ip_list->count++;
+
+	return 0;
+}
+
+char *nfs_multipath_parse_ip_list_get_cursor(char **buf_to_parse, bool *single)
+{
+	char *cursor = NULL;
+	const char *single_sep = strchr(*buf_to_parse, '~');
+	const char *range_sep = strchr(*buf_to_parse, '-');
+
+	*single = true;
+	if (range_sep) {
+		if (range_sep > single_sep) { // A-B or A~B-C
+			if (single_sep == NULL) {  // A-B
+				cursor = strsep(buf_to_parse, "-");
+				if (cursor)
+					*single = false;
+			} else// A~B-C
+				cursor = strsep(buf_to_parse, "~");
+		} else { // A-B~C
+			cursor = strsep(buf_to_parse, "-");
+			if (cursor)
+				*single = false;
+		}
+	} else { // A~B~C
+		cursor = strsep(buf_to_parse, "~");
+	}
+	return cursor;
+}
+
+bool nfs_multipath_parse_param_check(enum nfs_multi_path_options type,
+			struct multipath_mount_options *options)
+{
+	if (type == REMOUNTREMOTEADDR && options->remote_ip_list->count != 0) {
+		memset(options->remote_ip_list, 0, sizeof(struct nfs_ip_list));
+		return true;
+	}
+	if (type == REMOUNTLOCALADDR && options->local_ip_list->count != 0) {
+		memset(options->local_ip_list, 0, sizeof(struct nfs_ip_list));
+		return true;
+	}
+	if ((type == REMOTEADDR || type == REMOTEDNSNAME) &&
+		options->pRemoteDnsInfo->dnsNameCount != 0) {
+
+		pr_info("[MULTIPATH:%s] parse for %d ,already have dns\n",
+		__func__, type);
+		return false;
+	} else if ((type == REMOTEADDR || type == REMOTEDNSNAME) &&
+		options->remote_ip_list->count != 0) {
+
+		pr_info("[MULTIPATH:%s] parse for %d ,already have iplist\n",
+		__func__, type);
+		return false;
+	}
+	return true;
+}
+
+int nfs_multipath_parse_ip_list(char *buffer, struct net *net_ns,
+			struct multipath_mount_options *options,
+			enum nfs_multi_path_options type)
+{
+	char *buf_to_parse = NULL;
+	bool  prev_range = false;
+	int   ret    = 0;
+	char *cursor = NULL;
+	bool  single = true;
+	struct nfs_ip_list *ip_list_tmp = NULL;
+
+	if (!nfs_multipath_parse_param_check(type, options))
+		return -ENOTSUPP;
+
+	if (type == REMOUNTREMOTEADDR)
+		type = REMOTEADDR;
+
+	if (type == REMOUNTLOCALADDR)
+		type = LOCALADDR;
+
+	if (type == LOCALADDR)
+		ip_list_tmp = options->local_ip_list;
+	else
+		ip_list_tmp = options->remote_ip_list;
+
+	pr_info("NFS:   parsing nfs mount option '%s' type: %d[%s]\n",
+	buffer, type, __func__);
+
+	buf_to_parse = buffer;
+	while (buf_to_parse != NULL) {
+		cursor =
+		nfs_multipath_parse_ip_list_get_cursor(&buf_to_parse, &single);
+		if (!cursor)
+			break;
+
+		if (single == false && prev_range == true) {
+			pr_info("NFS: mount option type: %d fail. Multiple Range.[%s]\n",
+			type, __func__);
+
+			ret = -EINVAL;
+			goto out;
+		}
+
+		if (prev_range == false) {
+			ret = nfs_multipath_parse_ip_list_inter(ip_list_tmp,
+				net_ns, cursor, type);
+			if (ret)
+				goto out;
+			if (single == false)
+				prev_range = true;
+		} else {
+			ret = nfs_multipath_parse_ip_range(net_ns, cursor,
+				ip_list_tmp, type);
+			if (ret != 0)
+				goto out;
+			prev_range = false;
+		}
+	}
+
+out:
+	if (ret)
+		memset(ip_list_tmp, 0, sizeof(struct nfs_ip_list));
+
+	return ret;
+}
+
+int nfs_multipath_parse_dns_list(char *buffer, struct net *net_ns,
+			struct multipath_mount_options *options)
+{
+	struct NFS_ROUTE_DNS_INFO_S *dns_name_list_tmp = NULL;
+	char *cursor = NULL;
+	char *bufToParse;
+
+	if (!nfs_multipath_parse_param_check(REMOTEDNSNAME, options))
+		return -ENOTSUPP;
+
+	pr_info("[MULTIPATH:%s] buffer %s\n", __func__, buffer);
+	// freed in nfs_free_parsed_mount_data
+	dns_name_list_tmp = kmalloc(sizeof(struct NFS_ROUTE_DNS_INFO_S),
+							GFP_KERNEL);
+	if (!dns_name_list_tmp)
+		return -ENOMEM;
+
+	dns_name_list_tmp->dnsNameCount = 0;
+	bufToParse = buffer;
+	while (bufToParse) {
+		if (dns_name_list_tmp->dnsNameCount >= MAX_DNS_SUPPORTED) {
+			pr_err("%s: dnsname for %s reached %d,more than supported limit %d\n",
+				__func__, cursor,
+				dns_name_list_tmp->dnsNameCount,
+				MAX_DNS_SUPPORTED);
+			dns_name_list_tmp->dnsNameCount = 0;
+			return -ENOSPC;
+		}
+		cursor = strsep(&bufToParse, "~");
+		if (!cursor)
+			break;
+
+		strcpy(dns_name_list_tmp->routeRemoteDnsList
+		[dns_name_list_tmp->dnsNameCount].dnsname,
+		cursor);
+		dns_name_list_tmp->dnsNameCount++;
+	}
+	if (dns_name_list_tmp->dnsNameCount == 0)
+		return -EINVAL;
+	options->pRemoteDnsInfo = dns_name_list_tmp;
+	return 0;
+}
+
+int nfs_multipath_parse_options_check_ipv4_valid(struct sockaddr_in *addr)
+{
+	if (addr->sin_addr.s_addr == 0 || addr->sin_addr.s_addr == 0xffffffff)
+		return -EINVAL;
+	return 0;
+}
+
+int nfs_multipath_parse_options_check_ipv6_valid(struct sockaddr_in6 *addr)
+{
+	if (addr->sin6_addr.in6_u.u6_addr32[0] == 0 &&
+		addr->sin6_addr.in6_u.u6_addr32[1] == 0 &&
+		addr->sin6_addr.in6_u.u6_addr32[2] == 0 &&
+		addr->sin6_addr.in6_u.u6_addr32[3] == 0)
+		return -EINVAL;
+
+	if (addr->sin6_addr.in6_u.u6_addr32[0] == 0xffffffff &&
+		addr->sin6_addr.in6_u.u6_addr32[1] == 0xffffffff &&
+		addr->sin6_addr.in6_u.u6_addr32[2] == 0xffffffff &&
+		addr->sin6_addr.in6_u.u6_addr32[3] == 0xffffffff)
+		return -EINVAL;
+	return 0;
+}
+
+int nfs_multipath_parse_options_check_ip_valid(struct sockaddr_storage *address)
+{
+	int rc = 0;
+
+	if (address->ss_family == AF_INET)
+		rc = nfs_multipath_parse_options_check_ipv4_valid(
+			(struct sockaddr_in *)address);
+	else if (address->ss_family == AF_INET6)
+		rc = nfs_multipath_parse_options_check_ipv6_valid(
+			(struct sockaddr_in6 *)address);
+	else
+		rc = -EINVAL;
+
+	return rc;
+}
+
+int nfs_multipath_parse_options_check_valid(
+			struct multipath_mount_options *options)
+{
+	int rc;
+	int i;
+
+	if (options == NULL)
+		return 0;
+
+	for (i = 0; i < options->local_ip_list->count; i++) {
+		rc = nfs_multipath_parse_options_check_ip_valid(
+			&options->local_ip_list->address[i]);
+		if (rc != 0)
+			return rc;
+	}
+
+	for (i = 0; i < options->remote_ip_list->count; i++) {
+		rc = nfs_multipath_parse_options_check_ip_valid(
+			&options->remote_ip_list->address[i]);
+		if (rc != 0)
+			return rc;
+	}
+
+	return 0;
+}
+int nfs_multipath_parse_options_check_duplicate(
+			struct multipath_mount_options *options)
+{
+	int i;
+	int j;
+
+	if (options == NULL ||
+		options->local_ip_list->count == 0 ||
+		options->remote_ip_list->count == 0)
+
+		return 0;
+
+	for (i = 0; i < options->local_ip_list->count; i++) {
+		for (j = 0; j < options->remote_ip_list->count; j++) {
+			if (rpc_cmp_addr((const struct sockaddr *)
+				&options->local_ip_list->address[i],
+				(const struct sockaddr *)
+				&options->remote_ip_list->address[j]))
+				return -ENOTSUPP;
+		}
+	}
+	return 0;
+}
+
+int nfs_multipath_parse_options_check(struct multipath_mount_options *options)
+{
+	int rc = 0;
+
+	rc = nfs_multipath_parse_options_check_valid(options);
+
+	if (rc != 0) {
+		pr_err("has invaild ip.\n");
+		return rc;
+	}
+
+	rc = nfs_multipath_parse_options_check_duplicate(options);
+	if (rc != 0)
+		return rc;
+	return rc;
+}
+
+int nfs_multipath_alloc_options(void **enfs_option)
+{
+	struct multipath_mount_options *options = NULL;
+
+	options = kzalloc(sizeof(struct multipath_mount_options), GFP_KERNEL);
+
+	if (options == NULL)
+		return -ENOMEM;
+
+	options->local_ip_list =
+	kzalloc(sizeof(struct nfs_ip_list), GFP_KERNEL);
+	if (options->local_ip_list == NULL) {
+		kfree(options);
+		return -ENOMEM;
+	}
+
+	options->remote_ip_list =
+	kzalloc(sizeof(struct nfs_ip_list), GFP_KERNEL);
+	if (options->remote_ip_list == NULL) {
+		kfree(options->local_ip_list);
+		kfree(options);
+		return -ENOMEM;
+	}
+
+	options->pRemoteDnsInfo = kzalloc(sizeof(struct NFS_ROUTE_DNS_INFO_S),
+				GFP_KERNEL);
+	if (options->pRemoteDnsInfo == NULL) {
+		kfree(options->remote_ip_list);
+		kfree(options->local_ip_list);
+		kfree(options);
+		return -ENOMEM;
+	}
+
+	*enfs_option = options;
+	return 0;
+}
+
+int nfs_multipath_parse_options(enum nfs_multi_path_options type,
+			char *str, void **enfs_option, struct net *net_ns)
+{
+	int rc;
+	struct multipath_mount_options *options = NULL;
+
+	if ((str == NULL) || (enfs_option == NULL) || (net_ns == NULL))
+		return -EINVAL;
+
+	if (*enfs_option == NULL) {
+		rc = nfs_multipath_alloc_options(enfs_option);
+		if (rc != 0) {
+			enfs_log_error(
+				"alloc enfs_options failed! errno:%d\n", rc);
+			return rc;
+		}
+	}
+
+	options = (struct multipath_mount_options *)*enfs_option;
+
+	if (type == LOCALADDR  || type == REMOUNTLOCALADDR ||
+		type == REMOTEADDR || type == REMOUNTREMOTEADDR) {
+		rc = nfs_multipath_parse_ip_list(str, net_ns, options, type);
+	} else if (type == REMOTEDNSNAME) {
+		/* alloc and release need to modify */
+		rc = nfs_multipath_parse_dns_list(str, net_ns, options);
+	}  else {
+		rc = -EOPNOTSUPP;
+	}
+
+	// after parsing cmd, need checking local and remote
+	// IP is same. if not means illegal cmd
+	if (rc == 0)
+		rc = nfs_multipath_parse_options_check_duplicate(options);
+
+	if (rc == 0)
+		rc = nfs_multipath_parse_options_check(options);
+
+	return rc;
+}
+
+void nfs_multipath_free_options(void **enfs_option)
+{
+	struct multipath_mount_options *options;
+
+	if (enfs_option == NULL || *enfs_option == NULL)
+		return;
+
+	options = (struct multipath_mount_options *)*enfs_option;
+
+	if (options->remote_ip_list != NULL) {
+		kfree(options->remote_ip_list);
+		options->remote_ip_list = NULL;
+	}
+
+	if (options->local_ip_list != NULL) {
+		kfree(options->local_ip_list);
+		options->local_ip_list = NULL;
+	}
+
+	if (options->pRemoteDnsInfo != NULL) {
+		kfree(options->pRemoteDnsInfo);
+		options->pRemoteDnsInfo = NULL;
+	}
+
+	kfree(options);
+	*enfs_option = NULL;
+}
diff --git a/fs/nfs/enfs/enfs_multipath_parse.h b/fs/nfs/enfs/enfs_multipath_parse.h
new file mode 100644
index 000000000000..d5a36b2eb63b
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath_parse.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Client-side ENFS adapter.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#ifndef _ENFS_MULTIPATH_PARSE_H_
+#define _ENFS_MULTIPATH_PARSE_H_
+
+#include "enfs.h"
+
+struct multipath_mount_options {
+	struct nfs_ip_list  *remote_ip_list;
+	struct nfs_ip_list  *local_ip_list;
+	struct NFS_ROUTE_DNS_INFO_S    *pRemoteDnsInfo;
+};
+
+int nfs_multipath_parse_options(enum nfs_multi_path_options type,
+			char *str, void **enfs_option, struct net *net_ns);
+void nfs_multipath_free_options(void **enfs_option);
+
+#endif
