Kernel
Threads by month
- ----- 2025 -----
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
September 2023
- 56 participants
- 248 discussions

[PATCH openEuler-1.0-LTS] netfilter: ipset: add the missing IP_SET_HASH_WITH_NET0 macro for ip_set_hash_netportnet.c
by Lu Wei 26 Sep '23
by Lu Wei 26 Sep '23
26 Sep '23
From: Kyle Zeng <zengyhkyle(a)gmail.com>
mainline inclusion
from mainline-v6.6-rc1
commit 050d91c03b28ca479df13dfb02bcd2c60dd6a878
category: bugfix
bugzilla: 189250
CVE: CVE-2023-42753
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
---------------------------
The missing IP_SET_HASH_WITH_NET0 macro in ip_set_hash_netportnet can
lead to the use of wrong `CIDR_POS(c)` for calculating array offsets,
which can lead to integer underflow. As a result, it leads to slab
out-of-bound access.
This patch adds back the IP_SET_HASH_WITH_NET0 macro to
ip_set_hash_netportnet to address the issue.
Fixes: 886503f34d63 ("netfilter: ipset: actually allow allowable CIDR 0 in hash:net,port,net")
Suggested-by: Jozsef Kadlecsik <kadlec(a)netfilter.org>
Signed-off-by: Kyle Zeng <zengyhkyle(a)gmail.com>
Acked-by: Jozsef Kadlecsik <kadlec(a)netfilter.org>
Signed-off-by: Florian Westphal <fw(a)strlen.de>
Signed-off-by: Lu Wei <luwei32(a)huawei.com>
---
net/netfilter/ipset/ip_set_hash_netportnet.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 613e18e720a4..9290a4d7b862 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -39,6 +39,7 @@ MODULE_ALIAS("ip_set_hash:net,port,net");
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define IPSET_NET_COUNT 2
+#define IP_SET_HASH_WITH_NET0
/* IPv4 variant */
--
2.34.1
2
1

[PATCH openEuler-1.0-LTS,v2] netfilter: ipset: add the missing IP_SET_HASH_WITH_NET0 macro for ip_set_hash_netportnet.c
by Lu Wei 26 Sep '23
by Lu Wei 26 Sep '23
26 Sep '23
From: Kyle Zeng <zengyhkyle(a)gmail.com>
mainline inclusion
from mainline-v6.6-rc1
commit 050d91c03b28ca479df13dfb02bcd2c60dd6a878
category: bugfix
bugzilla: 189250
CVE: CVE-2023-42753
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
---------------------------
The missing IP_SET_HASH_WITH_NET0 macro in ip_set_hash_netportnet can
lead to the use of wrong `CIDR_POS(c)` for calculating array offsets,
which can lead to integer underflow. As a result, it leads to slab
out-of-bound access.
This patch adds back the IP_SET_HASH_WITH_NET0 macro to
ip_set_hash_netportnet to address the issue.
Fixes: 886503f34d63 ("netfilter: ipset: actually allow allowable CIDR 0 in hash:net,port,net")
Suggested-by: Jozsef Kadlecsik <kadlec(a)netfilter.org>
Signed-off-by: Kyle Zeng <zengyhkyle(a)gmail.com>
Acked-by: Jozsef Kadlecsik <kadlec(a)netfilter.org>
Signed-off-by: Florian Westphal <fw(a)strlen.de>
Signed-off-by: Lu Wei <luwei32(a)huawei.com>
---
net/netfilter/ipset/ip_set_hash_netportnet.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 613e18e720a4..9290a4d7b862 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -39,6 +39,7 @@ MODULE_ALIAS("ip_set_hash:net,port,net");
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define IPSET_NET_COUNT 2
+#define IP_SET_HASH_WITH_NET0
/* IPv4 variant */
--
2.34.1
1
0

[PATCH openEuler-1.0-LTS] [just for review!!!!]Add feature: eNFS - nfs multipath to improve performance and reliability
by mingqian218472 25 Sep '23
by mingqian218472 25 Sep '23
25 Sep '23
From: 闫海涛 <yanhaitao2(a)huawei.com>
driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I7SVH7
---------------------------------
Currently, the NFS client can use only one server IP address at a single mount point. As a result, the hardware capability of multiple storage nodes and NICs cannot be fully utilized. In multiple financial sites, the performance cannot meet service requirements. In addition, when a single link is faulty, services are suspended. The reliability problem needs to be solved.
OpenEuler-based commercial OS vendors hope that the eNFS feature will be integrated into 20.03 SP4 to resolve performance and reliability problems.
When user mount one NFS share, can input localaddrs/remoteaddrs these two optional Parameters to use eNFS multipath. If these optional parameters are not used, NFS will behave as before. For example,
mount -t nfs -o [localaddrs=127.17.0.1-127.17.0.4],[remoteaddrs=127.17.1.1-127.17.1.4] xx.xx.xx.xx:/test /mnt/test
Changes in eNFS are as follows:
1. patch 0001:
At the NFS layer, the eNFS registration function is called back when the mount command parses parameters. The eNFS parses and saves the IP address list entered by users.
2. patch 0002:
At the sunrpc layer, the eNFS registration function is called back When the NFS uses sunrpc to create rpc_clnt, the eNFS combines the IP address list entered for mount to generate multiple xprts. When the I/O times out, the callback function of the eNFS is called back so that the eNFS switches to an available link for retry.
3. patch 0003:
The eNFS module registers the interface for parsing the mount command. During the mount process, the NFS invokes the eNFS interface to enable the eNFS to parse the mounting parameters of UltraPath. The eNFS module saves the mounting parameters to the context of nfs_client.
4. patch 0004:
When the NFS invokes the SunRPC to create rpc_clnt, the eNFS interface is called back. The eNFS creates multiple xprts based on the output IP address list. When NFS V3 I/Os are delivered, eNFS distributes I/Os to available links based on the link status, improving performance through load balancing.
5. patch 0005:
When sending I/Os from the SunRPC module to the NFS server times out, the SunRPC module calls back the eNFS module to reselect a link. The eNFS module distributes I/Os to other available links, preventing service interruption caused by a single link failure.
6. patch 0006:
The eNFS compilation option and makefile are added. By default, the eNFS compilation is not performed.
Signed-off-by: mingqian218472 <zhangmingqian.zhang(a)huawei.com>
---
...-nfs-multipath-to-improve-performanc.patch | 6148 +++++++++++++++++
...enfs_registe_and_handle_mount_option.patch | 757 ++
...nd_create_multipath_then_dispatch_IO.patch | 805 +++
...add_enfs_module_for_nfs_mount_option.patch | 1209 ++++
...dd_enfs_module_for_sunrpc_multipatch.patch | 1581 +++++
...le_for_sunrpc_failover_and_configure.patch | 1607 +++++
0006-add_enfs_compile_option.patch | 70 +
7 files changed, 12177 insertions(+)
create mode 100644 0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch
create mode 100644 0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
create mode 100644 0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
create mode 100644 0003-add_enfs_module_for_nfs_mount_option.patch
create mode 100644 0004-add_enfs_module_for_sunrpc_multipatch.patch
create mode 100644 0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
create mode 100644 0006-add_enfs_compile_option.patch
diff --git a/0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch b/0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch
new file mode 100644
index 0000000..2974c5f
--- /dev/null
+++ b/0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch
@@ -0,0 +1,6148 @@
+From 53f616b0a649494e33d30b250d06c4049ccb88be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=E9=97=AB=E6=B5=B7=E6=B6=9B?= <yanhaitao2(a)huawei.com>
+Date: Mon, 25 Sep 2023 19:19:15 +0800
+Subject: [PATCH openEuler-20.03-LTS-SP3] Add feature: eNFS - nfs multipath to
+ improve performance and reliability
+
+driver inclusion
+category: feature
+bugzilla: https://gitee.com/openeuler/release-management/issues/I7U0W0
+
+---------------------------------
+
+Currently, the NFS client can use only one server IP address at a single mount point. As a result, the hardware capability of multiple storage nodes and NICs cannot be fully utilized. In multiple financial sites, the performance cannot meet service requirements. In addition, when a single link is faulty, services are suspended. The reliability problem needs to be solved.
+OpenEuler-based commercial OS vendors hope that the eNFS feature will be integrated into 20.03 SP4 to resolve performance and reliability problems.
+
+When user mount one NFS share, can input localaddrs/remoteaddrs these two optional Parameters to use eNFS multipath. If these optional parameters are not used, NFS will behave as before. For example,
+mount -t nfs -o [localaddrs=127.17.0.1-127.17.0.4],[remoteaddrs=127.17.1.1-127.17.1.4] xx.xx.xx.xx:/test /mnt/test
+
+Changes in eNFS are as follows:
+1. patch 0001:
+At the NFS layer, the eNFS registration function is called back when the mount command parses parameters. The eNFS parses and saves the IP address list entered by users.
+2. patch 0002:
+At the sunrpc layer, the eNFS registration function is called back When the NFS uses sunrpc to create rpc_clnt, the eNFS combines the IP address list entered for mount to generate multiple xprts. When the I/O times out, the callback function of the eNFS is called back so that the eNFS switches to an available link for retry.
+3. patch 0003:
+The eNFS module registers the interface for parsing the mount command. During the mount process, the NFS invokes the eNFS interface to enable the eNFS to parse the mounting parameters of UltraPath. The eNFS module saves the mounting parameters to the context of nfs_client.
+4. patch 0004:
+When the NFS invokes the SunRPC to create rpc_clnt, the eNFS interface is called back. The eNFS creates multiple xprts based on the output IP address list. When NFS V3 I/Os are delivered, eNFS distributes I/Os to available links based on the link status, improving performance through load balancing.
+5. patch 0005:
+When sending I/Os from the SunRPC module to the NFS server times out, the SunRPC module calls back the eNFS module to reselect a link. The eNFS module distributes I/Os to other available links, preventing service interruption caused by a single link failure.
+6. patch 0006:
+The eNFS compilation option and makefile are added. By default, the eNFS compilation is not performed.
+
+Signed-off-by: mingqian218472 <zhangmingqian.zhang(a)huawei.com>
+---
+ ...enfs_registe_and_handle_mount_option.patch | 757 ++++++++
+ ...nd_create_multipath_then_dispatch_IO.patch | 805 +++++++++
+ ...add_enfs_module_for_nfs_mount_option.patch | 1209 +++++++++++++
+ ...dd_enfs_module_for_sunrpc_multipatch.patch | 1581 ++++++++++++++++
+ ...le_for_sunrpc_failover_and_configure.patch | 1607 +++++++++++++++++
+ 0006-add_enfs_compile_option.patch | 70 +
+ kernel.spec | 13 +
+ 7 files changed, 6042 insertions(+)
+ create mode 100644 0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
+ create mode 100644 0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
+ create mode 100644 0003-add_enfs_module_for_nfs_mount_option.patch
+ create mode 100644 0004-add_enfs_module_for_sunrpc_multipatch.patch
+ create mode 100644 0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
+ create mode 100644 0006-add_enfs_compile_option.patch
+
+diff --git a/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
+new file mode 100644
+index 0000000..38e57a9
+--- /dev/null
++++ b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
+@@ -0,0 +1,757 @@
++diff --git a/fs/nfs/client.c b/fs/nfs/client.c
++index 7d02dc52209d..50820a8a684a 100644
++--- a/fs/nfs/client.c
+++++ b/fs/nfs/client.c
++@@ -48,7 +48,7 @@
++ #include "callback.h"
++ #include "delegation.h"
++ #include "iostat.h"
++-#include "internal.h"
+++#include "enfs_adapter.h"
++ #include "fscache.h"
++ #include "pnfs.h"
++ #include "nfs.h"
++@@ -255,6 +255,7 @@ void nfs_free_client(struct nfs_client *clp)
++ put_nfs_version(clp->cl_nfs_mod);
++ kfree(clp->cl_hostname);
++ kfree(clp->cl_acceptor);
+++ nfs_free_multi_path_client(clp);
++ kfree(clp);
++ }
++ EXPORT_SYMBOL_GPL(nfs_free_client);
++@@ -330,6 +331,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
++ sap))
++ continue;
++
+++ if (!nfs_multipath_client_match(clp, data))
+++ continue;
+++
++ refcount_inc(&clp->cl_count);
++ return clp;
++ }
++@@ -512,6 +516,9 @@ int nfs_create_rpc_client(struct nfs_client *clp,
++ .program = &nfs_program,
++ .version = clp->rpc_ops->version,
++ .authflavor = flavor,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .multipath_option = cl_init->enfs_option,
+++#endif
++ };
++
++ if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
++@@ -634,6 +641,13 @@ struct nfs_client *nfs_init_client(struct nfs_client *clp,
++ /* the client is already initialised */
++ if (clp->cl_cons_state == NFS_CS_READY)
++ return clp;
+++ error = nfs_create_multi_path_client(clp, cl_init);
+++ if (error < 0) {
+++ dprintk("%s: create failed.%d!\n", __func__, error);
+++ nfs_put_client(clp);
+++ clp = ERR_PTR(error);
+++ return clp;
+++ }
++
++ /*
++ * Create a client RPC handle for doing FSSTAT with UNIX auth only
++@@ -666,6 +680,9 @@ static int nfs_init_server(struct nfs_server *server,
++ .net = data->net,
++ .timeparms = &timeparms,
++ .init_flags = (1UL << NFS_CS_REUSEPORT),
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .enfs_option = data->enfs_option,
+++#endif
++ };
++ struct nfs_client *clp;
++ int error;
++diff --git a/fs/nfs/enfs_adapter.c b/fs/nfs/enfs_adapter.c
++new file mode 100644
++index 000000000000..7f471f2072c4
++--- /dev/null
+++++ b/fs/nfs/enfs_adapter.c
++@@ -0,0 +1,230 @@
+++// 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/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 <linux/sunrpc/sched.h>
+++#include <linux/nfs_iostat.h>
+++#include "enfs_adapter.h"
+++#include "iostat.h"
+++
+++struct enfs_adapter_ops __rcu *enfs_adapter;
+++
+++int enfs_adapter_register(struct enfs_adapter_ops *ops)
+++{
+++ struct enfs_adapter_ops *old;
+++
+++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, NULL, ops);
+++ if (old == NULL || old == ops)
+++ return 0;
+++ pr_err("regist %s ops %p failed. old %p\n", __func__, ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(enfs_adapter_register);
+++
+++int enfs_adapter_unregister(struct enfs_adapter_ops *ops)
+++{
+++ struct enfs_adapter_ops *old;
+++
+++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, ops, NULL);
+++ if (old == ops || old == NULL)
+++ return 0;
+++ pr_err("unregist %s ops %p failed. old %p\n", __func__, ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(enfs_adapter_unregister);
+++
+++struct enfs_adapter_ops *nfs_multipath_router_get(void)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ rcu_read_lock();
+++ ops = rcu_dereference(enfs_adapter);
+++ if (ops == NULL) {
+++ rcu_read_unlock();
+++ return NULL;
+++ }
+++ if (!try_module_get(ops->owner))
+++ ops = NULL;
+++ rcu_read_unlock();
+++ return ops;
+++}
+++
+++void nfs_multipath_router_put(struct enfs_adapter_ops *ops)
+++{
+++ if (ops)
+++ module_put(ops->owner);
+++}
+++
+++bool is_valid_option(enum nfsmultipathoptions option)
+++{
+++ if (option < REMOTEADDR || option >= INVALID_OPTION) {
+++ pr_warn("%s: ENFS invalid option %d\n", __func__, option);
+++ return false;
+++ }
+++
+++ return true;
+++}
+++
+++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
+++ struct nfs_parsed_mount_data *mnt)
+++{
+++
+++ //parseMultiPathOptions(getNfsMultiPathOpt(token), string, mnt);
+++
+++ int rc;
+++ struct enfs_adapter_ops *ops;
+++
+++ ops = nfs_multipath_router_get();
+++ if ((ops == NULL) || (ops->parse_mount_options == NULL) ||
+++ !is_valid_option(option)) {
+++ nfs_multipath_router_put(ops);
+++ dfprintk(MOUNT,
+++ "NFS: parsing nfs mount option enfs not load[%s]\n"
+++ , __func__);
+++ return -EOPNOTSUPP;
+++ }
+++ // nfs_multipath_parse_options
+++ dfprintk(MOUNT, "NFS: parsing nfs mount option '%s' type: %d[%s]\n"
+++ , str, option, __func__);
+++ rc = ops->parse_mount_options(option, str, &mnt->enfs_option, mnt->net);
+++ nfs_multipath_router_put(ops);
+++ return rc;
+++}
+++
+++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ if (data->enfs_option == NULL)
+++ return;
+++
+++ ops = nfs_multipath_router_get();
+++ if ((ops == NULL) || (ops->free_mount_options == NULL)) {
+++ nfs_multipath_router_put(ops);
+++ return;
+++ }
+++ ops->free_mount_options((void *)&data->enfs_option);
+++ nfs_multipath_router_put(ops);
+++}
+++
+++int nfs_create_multi_path_client(struct nfs_client *client,
+++ const struct nfs_client_initdata *cl_init)
+++{
+++ int ret = 0;
+++ struct enfs_adapter_ops *ops;
+++
+++ if (cl_init->enfs_option == NULL)
+++ return 0;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_init != NULL)
+++ ret = ops->client_info_init(
+++ (void *)&client->cl_multipath_data, cl_init);
+++ nfs_multipath_router_put(ops);
+++
+++ return ret;
+++}
+++EXPORT_SYMBOL_GPL(nfs_create_multi_path_client);
+++
+++void nfs_free_multi_path_client(struct nfs_client *clp)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ if (clp->cl_multipath_data == NULL)
+++ return;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_free != NULL)
+++ ops->client_info_free(clp->cl_multipath_data);
+++ nfs_multipath_router_put(ops);
+++}
+++
+++int nfs_multipath_client_match(struct nfs_client *clp,
+++ const struct nfs_client_initdata *sap)
+++{
+++ int ret = true;
+++ struct enfs_adapter_ops *ops;
+++
+++ pr_info("%s src %p dst %p\n.", __func__,
+++ clp->cl_multipath_data, sap->enfs_option);
+++
+++ if (clp->cl_multipath_data == NULL && sap->enfs_option == NULL)
+++ return true;
+++
+++ if ((clp->cl_multipath_data == NULL && sap->enfs_option) ||
+++ (clp->cl_multipath_data && sap->enfs_option == NULL)) {
+++ pr_err("not match client src %p dst %p\n.",
+++ clp->cl_multipath_data, sap->enfs_option);
+++ return false;
+++ }
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_match != NULL)
+++ ret = ops->client_info_match(clp->cl_multipath_data,
+++ sap->enfs_option);
+++ nfs_multipath_router_put(ops);
+++
+++ return ret;
+++}
+++
+++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst)
+++{
+++ int ret = true;
+++ struct enfs_adapter_ops *ops;
+++
+++ if (src->cl_multipath_data == NULL && dst->cl_multipath_data == NULL)
+++ return true;
+++
+++ if (src->cl_multipath_data == NULL || dst->cl_multipath_data == NULL)
+++ return false;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->nfs4_client_info_match != NULL)
+++ ret = ops->nfs4_client_info_match(src->cl_multipath_data,
+++ src->cl_multipath_data);
+++ nfs_multipath_router_put(ops);
+++
+++ return ret;
+++}
+++EXPORT_SYMBOL_GPL(nfs4_multipath_client_match);
+++
+++void nfs_multipath_show_client_info(struct seq_file *mount_option,
+++ struct nfs_server *server)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ if (mount_option == NULL || server == NULL ||
+++ server->client == NULL ||
+++ server->nfs_client->cl_multipath_data == NULL)
+++ return;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_show != NULL)
+++ ops->client_info_show(mount_option, server);
+++ nfs_multipath_router_put(ops);
+++}
+++
+++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option)
+++{
+++ int ret = 0;
+++ struct enfs_adapter_ops *ops;
+++
+++ if (nfs_client == NULL || nfs_client->cl_rpcclient == NULL)
+++ return 0;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->remount_ip_list != NULL)
+++ ret = ops->remount_ip_list(nfs_client, enfs_option);
+++ nfs_multipath_router_put(ops);
+++ return ret;
+++}
+++EXPORT_SYMBOL_GPL(nfs_remount_iplist);
++diff --git a/fs/nfs/enfs_adapter.h b/fs/nfs/enfs_adapter.h
++new file mode 100644
++index 000000000000..752544e18056
++--- /dev/null
+++++ b/fs/nfs/enfs_adapter.h
++@@ -0,0 +1,101 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Client-side ENFS adapt header.
+++ *
+++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+++ */
+++#ifndef _NFS_MULTIPATH_H_
+++#define _NFS_MULTIPATH_H_
+++
+++#include "internal.h"
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++enum nfsmultipathoptions {
+++ REMOTEADDR,
+++ LOCALADDR,
+++ REMOTEDNSNAME,
+++ REMOUNTREMOTEADDR,
+++ REMOUNTLOCALADDR,
+++ INVALID_OPTION
+++};
+++
+++
+++struct enfs_adapter_ops {
+++ const char *name;
+++ struct module *owner;
+++ int (*parse_mount_options)(enum nfsmultipathoptions option,
+++ char *str, void **enfs_option, struct net *net_ns);
+++
+++ void (*free_mount_options)(void **data);
+++
+++ int (*client_info_init)(void **data,
+++ const struct nfs_client_initdata *cl_init);
+++ void (*client_info_free)(void *data);
+++ int (*client_info_match)(void *src, void *dst);
+++ int (*nfs4_client_info_match)(void *src, void *dst);
+++ void (*client_info_show)(struct seq_file *mount_option, void *data);
+++ int (*remount_ip_list)(struct nfs_client *nfs_client,
+++ void *enfs_option);
+++};
+++
+++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
+++ struct nfs_parsed_mount_data *mnt);
+++void enfs_free_mount_options(struct nfs_parsed_mount_data *data);
+++int nfs_create_multi_path_client(struct nfs_client *client,
+++ const struct nfs_client_initdata *cl_init);
+++void nfs_free_multi_path_client(struct nfs_client *clp);
+++int nfs_multipath_client_match(struct nfs_client *clp,
+++ const struct nfs_client_initdata *sap);
+++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst);
+++void nfs_multipath_show_client_info(struct seq_file *mount_option,
+++ struct nfs_server *server);
+++int enfs_adapter_register(struct enfs_adapter_ops *ops);
+++int enfs_adapter_unregister(struct enfs_adapter_ops *ops);
+++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option);
+++int nfs4_create_multi_path(struct nfs_server *server,
+++ struct nfs_parsed_mount_data *data,
+++ const struct rpc_timeout *timeparms);
+++
+++#else
+++static inline
+++void nfs_free_multi_path_client(struct nfs_client *clp)
+++{
+++
+++}
+++
+++static inline
+++int nfs_multipath_client_match(struct nfs_client *clp,
+++ const struct nfs_client_initdata *sap)
+++{
+++ return 1;
+++}
+++
+++static inline
+++int nfs_create_multi_path_client(struct nfs_client *client,
+++ const struct nfs_client_initdata *cl_init)
+++{
+++ return 0;
+++}
+++
+++static inline
+++void nfs_multipath_show_client_info(struct seq_file *mount_option,
+++ struct nfs_server *server)
+++{
+++
+++}
+++
+++static inline
+++int nfs4_multipath_client_match(struct nfs_client *src,
+++ struct nfs_client *dst)
+++{
+++ return 1;
+++}
+++
+++static inline
+++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
+++{
+++
+++}
+++
+++#endif // CONFIG_ENFS
+++#endif // _NFS_MULTIPATH_H_
++diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
++index 0ce5a90640c4..c696693edc7b 100644
++--- a/fs/nfs/internal.h
+++++ b/fs/nfs/internal.h
++@@ -93,6 +93,9 @@ struct nfs_client_initdata {
++ u32 minorversion;
++ struct net *net;
++ const struct rpc_timeout *timeparms;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ void *enfs_option; /* struct multipath_mount_options * */
+++#endif
++ };
++
++ /*
++@@ -135,6 +138,9 @@ struct nfs_parsed_mount_data {
++
++ struct security_mnt_opts lsm_opts;
++ struct net *net;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ void *enfs_option; /* struct multipath_mount_options * */
+++#endif
++ };
++
++ /* mount_clnt.c */
++diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
++index 1350ea673672..4aa6e1f961f7 100644
++--- a/fs/nfs/nfs4client.c
+++++ b/fs/nfs/nfs4client.c
++@@ -10,7 +10,7 @@
++ #include <linux/sunrpc/xprt.h>
++ #include <linux/sunrpc/bc_xprt.h>
++ #include <linux/sunrpc/rpc_pipe_fs.h>
++-#include "internal.h"
+++#include "enfs_adapter.h"
++ #include "callback.h"
++ #include "delegation.h"
++ #include "nfs4session.h"
++@@ -225,6 +225,16 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
++ __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
++ __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ err = nfs_create_multi_path_client(clp, cl_init);
+++ if (err < 0) {
+++ dprintk("%s: create failed.%d\n", __func__, err);
+++ nfs_put_client(clp);
+++ clp = ERR_PTR(err);
+++ return clp;
+++ }
+++#endif
+++
++ /*
++ * Set up the connection to the server before we add add to the
++ * global list.
++@@ -529,6 +539,9 @@ static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new,
++ if (!nfs4_match_client_owner_id(pos, new))
++ return 1;
++
+++ if (!nfs4_multipath_client_match(pos, new))
+++ return 1;
+++
++ return 0;
++ }
++
++@@ -860,7 +873,7 @@ static int nfs4_set_client(struct nfs_server *server,
++ const size_t addrlen,
++ const char *ip_addr,
++ int proto, const struct rpc_timeout *timeparms,
++- u32 minorversion, struct net *net)
+++ u32 minorversion, struct net *net, void *enfs_option)
++ {
++ struct nfs_client_initdata cl_init = {
++ .hostname = hostname,
++@@ -872,6 +885,9 @@ static int nfs4_set_client(struct nfs_server *server,
++ .minorversion = minorversion,
++ .net = net,
++ .timeparms = timeparms,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .enfs_option = enfs_option,
+++#endif
++ };
++ struct nfs_client *clp;
++
++@@ -1042,6 +1058,30 @@ static int nfs4_server_common_setup(struct nfs_server *server,
++ return error;
++ }
++
+++int nfs4_create_multi_path(struct nfs_server *server,
+++ struct nfs_parsed_mount_data *data,
+++ const struct rpc_timeout *timeparms)
+++{
+++ struct nfs_client_initdata cl_init = {
+++ .hostname = data->nfs_server.hostname,
+++ .addr = (const struct sockaddr *)&data->nfs_server.address,
+++ .addrlen = data->nfs_server.addrlen,
+++ .ip_addr = data->client_address,
+++ .nfs_mod = &nfs_v4,
+++ .proto = data->nfs_server.protocol,
+++ .minorversion = data->minorversion,
+++ .net = data->net,
+++ .timeparms = timeparms,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .enfs_option = data->enfs_option,
+++#endif // CONFIG_ENFS
+++ };
+++
+++ return nfs_create_multi_path_client(server->nfs_client, &cl_init);
+++
+++}
+++EXPORT_SYMBOL_GPL(nfs4_create_multi_path);
+++
++ /*
++ * Create a version 4 volume record
++ */
++@@ -1050,6 +1090,7 @@ static int nfs4_init_server(struct nfs_server *server,
++ {
++ struct rpc_timeout timeparms;
++ int error;
+++ void *enfs_option = NULL;
++
++ nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
++ data->timeo, data->retrans);
++@@ -1067,6 +1108,10 @@ static int nfs4_init_server(struct nfs_server *server,
++ else
++ data->selected_flavor = RPC_AUTH_UNIX;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ enfs_option = data->enfs_option;
+++#endif
+++
++ /* Get a client record */
++ error = nfs4_set_client(server,
++ data->nfs_server.hostname,
++@@ -1076,7 +1121,7 @@ static int nfs4_init_server(struct nfs_server *server,
++ data->nfs_server.protocol,
++ &timeparms,
++ data->minorversion,
++- data->net);
+++ data->net, enfs_option);
++ if (error < 0)
++ return error;
++
++@@ -1161,7 +1206,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
++ XPRT_TRANSPORT_RDMA,
++ parent_server->client->cl_timeout,
++ parent_client->cl_mvops->minor_version,
++- parent_client->cl_net);
+++ parent_client->cl_net, NULL);
++ if (!error)
++ goto init_server;
++ #endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
++@@ -1174,7 +1219,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
++ XPRT_TRANSPORT_TCP,
++ parent_server->client->cl_timeout,
++ parent_client->cl_mvops->minor_version,
++- parent_client->cl_net);
+++ parent_client->cl_net, NULL);
++ if (error < 0)
++ goto error;
++
++@@ -1269,7 +1314,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
++ set_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
++ error = nfs4_set_client(server, hostname, sap, salen, buf,
++ clp->cl_proto, clnt->cl_timeout,
++- clp->cl_minorversion, net);
+++ clp->cl_minorversion, net, NULL);
++ clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
++ if (error != 0) {
++ nfs_server_insert_lists(server);
++diff --git a/fs/nfs/super.c b/fs/nfs/super.c
++index a05e1eb2c3fd..83cd294aca15 100644
++--- a/fs/nfs/super.c
+++++ b/fs/nfs/super.c
++@@ -61,7 +61,7 @@
++ #include "callback.h"
++ #include "delegation.h"
++ #include "iostat.h"
++-#include "internal.h"
+++#include "enfs_adapter.h"
++ #include "fscache.h"
++ #include "nfs4session.h"
++ #include "pnfs.h"
++@@ -113,6 +113,12 @@ enum {
++
++ /* Special mount options */
++ Opt_userspace, Opt_deprecated, Opt_sloppy,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ Opt_remote_iplist,
+++ Opt_local_iplist,
+++ Opt_remote_dnslist,
+++ Opt_enfs_info,
+++#endif
++
++ Opt_err
++ };
++@@ -183,6 +189,13 @@ static const match_table_t nfs_mount_option_tokens = {
++ { Opt_fscache_uniq, "fsc=%s" },
++ { Opt_local_lock, "local_lock=%s" },
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ { Opt_remote_iplist, "remoteaddrs=%s" },
+++ { Opt_local_iplist, "localaddrs=%s" },
+++ { Opt_remote_dnslist, "remotednsname=%s" },
+++ { Opt_enfs_info, "enfs_info=%s" },
+++#endif
+++
++ /* The following needs to be listed after all other options */
++ { Opt_nfsvers, "v%s" },
++
++@@ -365,6 +378,21 @@ static struct shrinker acl_shrinker = {
++ .seeks = DEFAULT_SEEKS,
++ };
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++enum nfsmultipathoptions getNfsMultiPathOpt(int token)
+++{
+++ switch (token) {
+++ case Opt_remote_iplist:
+++ return REMOUNTREMOTEADDR;
+++ case Opt_local_iplist:
+++ return REMOUNTLOCALADDR;
+++ case Opt_remote_dnslist:
+++ return REMOTEDNSNAME;
+++ }
+++ return INVALID_OPTION;
+++}
+++#endif
+++
++ /*
++ * Register the NFS filesystems
++ */
++@@ -758,6 +786,9 @@ int nfs_show_options(struct seq_file *m, struct dentry *root)
++ seq_printf(m, ",addr=%s",
++ rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient,
++ RPC_DISPLAY_ADDR));
+++
+++ nfs_multipath_show_client_info(m, nfss);
+++
++ rcu_read_unlock();
++
++ return 0;
++@@ -853,6 +884,8 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
++ seq_puts(m, root->d_sb->s_flags & SB_NODIRATIME ? ",nodiratime" : "");
++ nfs_show_mount_options(m, nfss, 1);
++
+++ nfs_multipath_show_client_info(m, nfss);
+++
++ seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
++
++ show_implementation_id(m, nfss);
++@@ -977,6 +1010,7 @@ static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data)
++ kfree(data->nfs_server.export_path);
++ kfree(data->nfs_server.hostname);
++ kfree(data->fscache_uniq);
+++ enfs_free_mount_options(data);
++ security_free_mnt_opts(&data->lsm_opts);
++ kfree(data);
++ }
++@@ -1641,7 +1675,34 @@ static int nfs_parse_mount_options(char *raw,
++ return 0;
++ };
++ break;
++-
+++#if IS_ENABLED(CONFIG_ENFS)
+++ case Opt_remote_iplist:
+++ case Opt_local_iplist:
+++ case Opt_remote_dnslist:
+++ string = match_strdup(args);
+++ if (string == NULL)
+++ goto out_nomem;
+++ rc = enfs_parse_mount_options(getNfsMultiPathOpt(token),
+++ string, mnt);
+++ kfree(string);
+++ switch (rc) {
+++ case 0:
+++ break;
+++ case -ENOMEM:
+++ goto out_nomem;
+++ case -ENOSPC:
+++ goto out_limit;
+++ case -EINVAL:
+++ goto out_invalid_address;
+++ case -ENOTSUPP:
+++ goto out_invalid_address;
+++ case -EOPNOTSUPP:
+++ goto out_invalid_address;
+++ }
+++ break;
+++ case Opt_enfs_info:
+++ break;
+++#endif
++ /*
++ * Special options
++ */
++@@ -1720,6 +1781,11 @@ static int nfs_parse_mount_options(char *raw,
++ free_secdata(secdata);
++ printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
++ return 0;
+++#if IS_ENABLED(CONFIG_ENFS)
+++out_limit:
+++ dprintk("NFS: param is more than supported limit: %d\n", rc);
+++ return 0;
+++#endif
++ }
++
++ /*
++@@ -2335,6 +2401,14 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
++ if (!nfs_parse_mount_options((char *)options, data))
++ goto out;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ if (data->enfs_option) {
+++ error = nfs_remount_iplist(nfss->nfs_client, data->enfs_option);
+++ if (error)
+++ goto out;
+++ }
+++#endif
+++
++ /*
++ * noac is a special case. It implies -o sync, but that's not
++ * necessarily reflected in the mtab options. do_remount_sb
++@@ -2347,6 +2421,11 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
++ /* compare new mount options with old ones */
++ error = nfs_compare_remount_data(nfss, data);
++ out:
+++#if IS_ENABLED(CONFIG_ENFS)
+++ /* release remount option member */
+++ if (data->enfs_option)
+++ enfs_free_mount_options(data);
+++#endif
++ nfs_free_parsed_mount_data(data);
++ return error;
++ }
++diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
++index 7023ae64e3d7..2c19678afe8d 100644
++--- a/include/linux/nfs_fs_sb.h
+++++ b/include/linux/nfs_fs_sb.h
++@@ -123,6 +123,11 @@ struct nfs_client {
++
++ struct net *cl_net;
++ struct list_head pending_cb_stateids;
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ /* multi path private structure (struct multipath_client_info *) */
+++ void *cl_multipath_data;
+++#endif
++ };
++
++ /*
+diff --git a/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
+new file mode 100644
+index 0000000..540a2ce
+--- /dev/null
++++ b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
+@@ -0,0 +1,805 @@
++diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
++index 8aa865bce4f6..89178f78de8c 100644
++--- a/include/linux/sunrpc/clnt.h
+++++ b/include/linux/sunrpc/clnt.h
++@@ -70,6 +70,10 @@ struct rpc_clnt {
++ struct dentry *cl_debugfs; /* debugfs directory */
++ #endif
++ struct rpc_xprt_iter cl_xpi;
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ bool cl_enfs;
+++#endif
++ };
++
++ /*
++@@ -124,6 +128,9 @@ struct rpc_create_args {
++ unsigned long flags;
++ char *client_name;
++ struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */
+++#if IS_ENABLED(CONFIG_ENFS)
+++ void *multipath_option;
+++#endif
++ };
++
++ struct rpc_add_xprt_test {
++@@ -221,6 +228,12 @@ bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
++ const struct sockaddr *sap);
++ void rpc_cleanup_clids(void);
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++int
+++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+++ const struct rpc_call_ops *ops, void *data, int flags);
+++#endif /* CONFIG_ENFS */
+++
++ static inline int rpc_reply_expected(struct rpc_task *task)
++ {
++ return (task->tk_msg.rpc_proc != NULL) &&
++diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
++index ad2e243f3f03..124f5a0faf3e 100644
++--- a/include/linux/sunrpc/sched.h
+++++ b/include/linux/sunrpc/sched.h
++@@ -90,6 +90,9 @@ struct rpc_task {
++ tk_garb_retry : 2,
++ tk_cred_retry : 2,
++ tk_rebind_retry : 2;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ unsigned long tk_major_timeo; /* major timeout ticks */
+++#endif
++ };
++
++ typedef void (*rpc_action)(struct rpc_task *);
++@@ -118,6 +121,9 @@ struct rpc_task_setup {
++ */
++ #define RPC_TASK_ASYNC 0x0001 /* is an async task */
++ #define RPC_TASK_SWAPPER 0x0002 /* is swapping in/out */
+++#if IS_ENABLED(CONFIG_ENFS)
+++#define RPC_TASK_FIXED 0x0004 /* detect xprt status task */
+++#endif
++ #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */
++ #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */
++ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
++@@ -257,6 +263,9 @@ void rpc_destroy_mempool(void);
++ extern struct workqueue_struct *rpciod_workqueue;
++ extern struct workqueue_struct *xprtiod_workqueue;
++ void rpc_prepare_task(struct rpc_task *task);
+++#if IS_ENABLED(CONFIG_ENFS)
+++void rpc_init_task_retry_counters(struct rpc_task *task);
+++#endif
++
++ static inline int rpc_wait_for_completion_task(struct rpc_task *task)
++ {
++diff --git a/include/linux/sunrpc/sunrpc_enfs_adapter.h b/include/linux/sunrpc/sunrpc_enfs_adapter.h
++new file mode 100644
++index 000000000000..28abedcf5cf6
++--- /dev/null
+++++ b/include/linux/sunrpc/sunrpc_enfs_adapter.h
++@@ -0,0 +1,128 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/* Client-side SUNRPC ENFS adapter header.
+++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+++ */
+++#ifndef _SUNRPC_ENFS_ADAPTER_H_
+++#define _SUNRPC_ENFS_ADAPTER_H_
+++#include <linux/sunrpc/clnt.h>
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++
+++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
+++{
+++ xps->xps_nactive--;
+++}
+++
+++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
+++{
+++ xps->xps_nactive--;
+++}
+++
+++struct rpc_xprt *rpc_task_get_xprt
+++(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
+++
+++struct rpc_multipath_ops {
+++ struct module *owner;
+++ void (*create_clnt)(struct rpc_create_args *args,
+++ struct rpc_clnt *clnt);
+++ void (*releas_clnt)(struct rpc_clnt *clnt);
+++ void (*create_xprt)(struct rpc_xprt *xprt);
+++ void (*destroy_xprt)(struct rpc_xprt *xprt);
+++ void (*xprt_iostat)(struct rpc_task *task);
+++ void (*failover_handle)(struct rpc_task *task);
+++ bool (*task_need_call_start_again)(struct rpc_task *task);
+++ void (*adjust_task_timeout)(struct rpc_task *task, void *condition);
+++ void (*init_task_req)(struct rpc_task *task, struct rpc_rqst *req);
+++ bool (*prepare_transmit)(struct rpc_task *task);
+++};
+++
+++extern struct rpc_multipath_ops __rcu *multipath_ops;
+++void rpc_init_task_retry_counters(struct rpc_task *task);
+++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops);
+++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops);
+++struct rpc_multipath_ops *rpc_multipath_ops_get(void);
+++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops);
+++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
+++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
+++ struct rpc_clnt *clnt);
+++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt);
+++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt);
+++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt);
+++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task);
+++void rpc_multipath_ops_failover_handle(struct rpc_task *task);
+++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task);
+++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
+++ void *condition);
+++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
+++ struct rpc_rqst *req);
+++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task);
+++
+++#else
+++static inline struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt)
+++{
+++ return NULL;
+++}
+++
+++static inline void rpc_task_release_xprt(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt)
+++{
+++}
+++
+++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
+++{
+++}
+++
+++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_create_clnt
+++(struct rpc_create_args *args, struct rpc_clnt *clnt)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
+++{
+++}
+++
+++static inline bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
+++{
+++ return false;
+++}
+++
+++static inline void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_failover_handle(struct rpc_task *task)
+++{
+++}
+++
+++static inline
+++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
+++{
+++ return false;
+++}
+++
+++static inline void
+++rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task, void *condition)
+++{
+++}
+++
+++static inline void
+++rpc_multipath_ops_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
+++{
+++}
+++
+++static inline bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
+++{
+++ return false;
+++}
+++
+++#endif
+++#endif // _SUNRPC_ENFS_ADAPTER_H_
++diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
++index ccfacca1eba9..2e47b3577947 100644
++--- a/include/linux/sunrpc/xprt.h
+++++ b/include/linux/sunrpc/xprt.h
++@@ -279,6 +279,10 @@ struct rpc_xprt {
++ atomic_t inject_disconnect;
++ #endif
++ struct rcu_head rcu;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ atomic_long_t queuelen;
+++ void *multipath_context;
+++#endif
++ };
++
++ #if defined(CONFIG_SUNRPC_BACKCHANNEL)
++diff --git a/include/linux/sunrpc/xprtmultipath.h b/include/linux/sunrpc/xprtmultipath.h
++index af1257c030d2..d54e4dbbbf34 100644
++--- a/include/linux/sunrpc/xprtmultipath.h
+++++ b/include/linux/sunrpc/xprtmultipath.h
++@@ -22,6 +22,10 @@ struct rpc_xprt_switch {
++ const struct rpc_xprt_iter_ops *xps_iter_ops;
++
++ struct rcu_head xps_rcu;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ unsigned int xps_nactive;
+++ atomic_long_t xps_queuelen;
+++#endif
++ };
++
++ struct rpc_xprt_iter {
++@@ -69,4 +73,8 @@ extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
++
++ extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
++ const struct sockaddr *sap);
+++#if IS_ENABLED(CONFIG_ENFS)
+++extern void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+++ struct rpc_xprt *xprt);
+++#endif
++ #endif
++diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
++index 0fc540b0d183..d7ffee637148 100644
++--- a/net/sunrpc/clnt.c
+++++ b/net/sunrpc/clnt.c
++@@ -37,6 +37,7 @@
++ #include <linux/sunrpc/rpc_pipe_fs.h>
++ #include <linux/sunrpc/metrics.h>
++ #include <linux/sunrpc/bc_xprt.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++ #include <trace/events/sunrpc.h>
++
++ #include "sunrpc.h"
++@@ -490,6 +491,8 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
++ }
++ }
++
+++ rpc_multipath_ops_create_clnt(args, clnt);
+++
++ clnt->cl_softrtry = 1;
++ if (args->flags & RPC_CLNT_CREATE_HARDRTRY)
++ clnt->cl_softrtry = 0;
++@@ -869,6 +872,8 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
++ list_empty(&clnt->cl_tasks), 1*HZ);
++ }
++
+++ rpc_multipath_ops_releas_clnt(clnt);
+++
++ rpc_release_client(clnt);
++ }
++ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
++@@ -981,7 +986,13 @@ void rpc_task_release_transport(struct rpc_task *task)
++
++ if (xprt) {
++ task->tk_xprt = NULL;
++- xprt_put(xprt);
+++#if IS_ENABLED(CONFIG_ENFS)
+++ if (task->tk_client) {
+++ rpc_task_release_xprt(task->tk_client, xprt);
+++ return;
+++ }
+++#endif
+++ xprt_put(xprt);
++ }
++ }
++ EXPORT_SYMBOL_GPL(rpc_task_release_transport);
++@@ -990,6 +1001,10 @@ void rpc_task_release_client(struct rpc_task *task)
++ {
++ struct rpc_clnt *clnt = task->tk_client;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ rpc_task_release_transport(task);
+++#endif
+++
++ if (clnt != NULL) {
++ /* Remove from client task list */
++ spin_lock(&clnt->cl_lock);
++@@ -999,14 +1014,29 @@ void rpc_task_release_client(struct rpc_task *task)
++
++ rpc_release_client(clnt);
++ }
+++#if IS_ENABLED(CONFIG_ENFS)
+++#else
++ rpc_task_release_transport(task);
+++#endif
++ }
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++static struct rpc_xprt *
+++rpc_task_get_next_xprt(struct rpc_clnt *clnt)
+++{
+++ return rpc_task_get_xprt(clnt, xprt_iter_get_next(&clnt->cl_xpi));
+++}
+++#endif
+++
++ static
++ void rpc_task_set_transport(struct rpc_task *task, struct rpc_clnt *clnt)
++ {
++ if (!task->tk_xprt)
+++#if IS_ENABLED(CONFIG_ENFS)
+++ task->tk_xprt = rpc_task_get_next_xprt(clnt);
+++#else
++ task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
+++#endif
++ }
++
++ static
++@@ -1597,6 +1627,14 @@ call_reserveresult(struct rpc_task *task)
++ return;
++ case -EIO: /* probably a shutdown */
++ break;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ case -ETIMEDOUT: /* woken up; restart */
+++ if (rpc_multipath_ops_task_need_call_start_again(task)) {
+++ rpc_task_release_transport(task);
+++ task->tk_action = call_start;
+++ return;
+++ }
+++#endif
++ default:
++ printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
++ __func__, status);
++@@ -1962,6 +2000,10 @@ call_transmit(struct rpc_task *task)
++ return;
++ if (!xprt_prepare_transmit(task))
++ return;
+++
+++ if (rpc_multipath_ops_prepare_transmit(task))
+++ return;
+++
++ task->tk_action = call_transmit_status;
++ /* Encode here so that rpcsec_gss can use correct sequence number. */
++ if (rpc_task_need_encode(task)) {
++@@ -2277,6 +2319,9 @@ call_timeout(struct rpc_task *task)
++
++ retry:
++ task->tk_action = call_bind;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ rpc_multipath_ops_failover_handle(task);
+++#endif
++ task->tk_status = 0;
++ }
++
++@@ -2961,3 +3006,30 @@ rpc_clnt_swap_deactivate(struct rpc_clnt *clnt)
++ }
++ EXPORT_SYMBOL_GPL(rpc_clnt_swap_deactivate);
++ #endif /* CONFIG_SUNRPC_SWAP */
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++/* rpc_clnt_test_xprt - Test and add a new transport to a rpc_clnt
+++ * @clnt: pointer to struct rpc_clnt
+++ * @xprt: pointer struct rpc_xprt
+++ * @ops: async operation
+++ */
+++int
+++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+++ const struct rpc_call_ops *ops, void *data, int flags)
+++{
+++ struct rpc_cred *cred;
+++ struct rpc_task *task;
+++
+++ cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+++ task = rpc_call_null_helper(clnt, xprt, cred,
+++ RPC_TASK_SOFT | RPC_TASK_SOFTCONN | flags,
+++ ops, data);
+++ put_rpccred(cred);
+++ if (IS_ERR(task))
+++ return PTR_ERR(task);
+++
+++ rpc_put_task(task);
+++ return 1;
+++}
+++EXPORT_SYMBOL_GPL(rpc_clnt_test_xprt);
+++#endif
++diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
++index a873c92a4898..2254fea0e863 100644
++--- a/net/sunrpc/sched.c
+++++ b/net/sunrpc/sched.c
++@@ -20,7 +20,7 @@
++ #include <linux/mutex.h>
++ #include <linux/freezer.h>
++
++-#include <linux/sunrpc/clnt.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++ #include "sunrpc.h"
++
++@@ -962,7 +962,12 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
++ /* Initialize workqueue for async tasks */
++ task->tk_workqueue = task_setup_data->workqueue;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ task->tk_xprt = rpc_task_get_xprt(task_setup_data->rpc_client,
+++ xprt_get(task_setup_data->rpc_xprt));
+++#else
++ task->tk_xprt = xprt_get(task_setup_data->rpc_xprt);
+++#endif
++
++ if (task->tk_ops->rpc_call_prepare != NULL)
++ task->tk_action = rpc_prepare_task;
++diff --git a/net/sunrpc/sunrpc_enfs_adapter.c b/net/sunrpc/sunrpc_enfs_adapter.c
++new file mode 100644
++index 000000000000..c1543545c6de
++--- /dev/null
+++++ b/net/sunrpc/sunrpc_enfs_adapter.c
++@@ -0,0 +1,214 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/* Client-side SUNRPC ENFS adapter header.
+++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+++ */
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+++
+++struct rpc_multipath_ops __rcu *multipath_ops;
+++
+++void rpc_init_task_retry_counters(struct rpc_task *task)
+++{
+++ /* Initialize retry counters */
+++ task->tk_garb_retry = 2;
+++ task->tk_cred_retry = 2;
+++ task->tk_rebind_retry = 2;
+++}
+++EXPORT_SYMBOL_GPL(rpc_init_task_retry_counters);
+++
+++struct rpc_xprt *
+++rpc_task_get_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+++{
+++ struct rpc_xprt_switch *xps;
+++
+++ if (!xprt)
+++ return NULL;
+++ rcu_read_lock();
+++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+++ atomic_long_inc(&xps->xps_queuelen);
+++ rcu_read_unlock();
+++ atomic_long_inc(&xprt->queuelen);
+++
+++ return xprt;
+++}
+++
+++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops)
+++{
+++ struct rpc_multipath_ops *old;
+++
+++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, NULL, ops);
+++ if (!old || old == ops)
+++ return 0;
+++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_register);
+++
+++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops)
+++{
+++ struct rpc_multipath_ops *old;
+++
+++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, ops, NULL);
+++ if (!old || old == ops)
+++ return 0;
+++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_unregister);
+++
+++struct rpc_multipath_ops *rpc_multipath_ops_get(void)
+++{
+++ struct rpc_multipath_ops *ops;
+++
+++ rcu_read_lock();
+++ ops = rcu_dereference(multipath_ops);
+++ if (!ops) {
+++ rcu_read_unlock();
+++ return NULL;
+++ }
+++ if (!try_module_get(ops->owner))
+++ ops = NULL;
+++ rcu_read_unlock();
+++ return ops;
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_get);
+++
+++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops)
+++{
+++ if (ops)
+++ module_put(ops->owner);
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_put);
+++
+++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+++{
+++ struct rpc_xprt_switch *xps;
+++
+++ atomic_long_dec(&xprt->queuelen);
+++ rcu_read_lock();
+++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+++ atomic_long_dec(&xps->xps_queuelen);
+++ rcu_read_unlock();
+++
+++ xprt_put(xprt);
+++}
+++
+++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
+++ struct rpc_clnt *clnt)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ if (args->multipath_option) {
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->create_clnt)
+++ mops->create_clnt(args, clnt);
+++ rpc_multipath_ops_put(mops);
+++ }
+++}
+++
+++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->releas_clnt)
+++ mops->releas_clnt(clnt);
+++
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->create_xprt) {
+++ mops->create_xprt(xprt);
+++ if (!xprt->multipath_context) {
+++ rpc_multipath_ops_put(mops);
+++ return true;
+++ }
+++ }
+++ rpc_multipath_ops_put(mops);
+++ return false;
+++}
+++
+++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ if (xprt->multipath_context) {
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->destroy_xprt)
+++ mops->destroy_xprt(xprt);
+++ rpc_multipath_ops_put(mops);
+++ }
+++}
+++
+++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (task->tk_client && mops && mops->xprt_iostat)
+++ mops->xprt_iostat(task);
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++void rpc_multipath_ops_failover_handle(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mpath_ops = NULL;
+++
+++ mpath_ops = rpc_multipath_ops_get();
+++ if (mpath_ops && mpath_ops->failover_handle)
+++ mpath_ops->failover_handle(task);
+++ rpc_multipath_ops_put(mpath_ops);
+++}
+++
+++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mpath_ops = NULL;
+++ bool ret = false;
+++
+++ mpath_ops = rpc_multipath_ops_get();
+++ if (mpath_ops && mpath_ops->task_need_call_start_again)
+++ ret = mpath_ops->task_need_call_start_again(task);
+++ rpc_multipath_ops_put(mpath_ops);
+++ return ret;
+++}
+++
+++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
+++ void *condition)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->adjust_task_timeout)
+++ mops->adjust_task_timeout(task, NULL);
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
+++ struct rpc_rqst *req)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->init_task_req)
+++ mops->init_task_req(task, req);
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->prepare_transmit) {
+++ if (!(mops->prepare_transmit(task))) {
+++ rpc_multipath_ops_put(mops);
+++ return true;
+++ }
+++ }
+++ rpc_multipath_ops_put(mops);
+++ return false;
+++}
++diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
++index c912bf20faa2..c2b63b3d5217 100644
++--- a/net/sunrpc/xprt.c
+++++ b/net/sunrpc/xprt.c
++@@ -48,6 +48,7 @@
++ #include <linux/sunrpc/clnt.h>
++ #include <linux/sunrpc/metrics.h>
++ #include <linux/sunrpc/bc_xprt.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++ #include <linux/rcupdate.h>
++
++ #include <trace/events/sunrpc.h>
++@@ -259,6 +260,9 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
++ dprintk("RPC: %5u failed to lock transport %p\n",
++ task->tk_pid, xprt);
++ task->tk_timeout = 0;
+++
+++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
+++
++ task->tk_status = -EAGAIN;
++ if (req == NULL)
++ priority = RPC_PRIORITY_LOW;
++@@ -560,6 +564,9 @@ void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action)
++ struct rpc_xprt *xprt = req->rq_xprt;
++
++ task->tk_timeout = RPC_IS_SOFT(task) ? req->rq_timeout : 0;
+++
+++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
+++
++ rpc_sleep_on(&xprt->pending, task, action);
++ }
++ EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space);
++@@ -1347,6 +1354,9 @@ xprt_request_init(struct rpc_task *task)
++ req->rq_rcv_buf.buflen = 0;
++ req->rq_release_snd_buf = NULL;
++ xprt_reset_majortimeo(req);
+++
+++ rpc_multipath_ops_init_task_req(task, req);
+++
++ dprintk("RPC: %5u reserved req %p xid %08x\n", task->tk_pid,
++ req, ntohl(req->rq_xid));
++ }
++@@ -1427,6 +1437,9 @@ void xprt_release(struct rpc_task *task)
++ task->tk_ops->rpc_count_stats(task, task->tk_calldata);
++ else if (task->tk_client)
++ rpc_count_iostats(task, task->tk_client->cl_metrics);
+++
+++ rpc_multipath_ops_xprt_iostat(task);
+++
++ spin_lock(&xprt->recv_lock);
++ if (!list_empty(&req->rq_list)) {
++ list_del_init(&req->rq_list);
++@@ -1455,6 +1468,7 @@ void xprt_release(struct rpc_task *task)
++ else
++ xprt_free_bc_request(req);
++ }
+++EXPORT_SYMBOL_GPL(xprt_release);
++
++ static void xprt_init(struct rpc_xprt *xprt, struct net *net)
++ {
++@@ -1528,6 +1542,10 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
++ return ERR_PTR(-ENOMEM);
++ }
++
+++if (rpc_multipath_ops_create_xprt(xprt)) {
+++ xprt_destroy(xprt);
+++ return ERR_PTR(-ENOMEM);
+++}
++ rpc_xprt_debugfs_register(xprt);
++
++ dprintk("RPC: created transport %p with %u slots\n", xprt,
++@@ -1547,6 +1565,9 @@ static void xprt_destroy_cb(struct work_struct *work)
++ rpc_destroy_wait_queue(&xprt->sending);
++ rpc_destroy_wait_queue(&xprt->backlog);
++ kfree(xprt->servername);
+++
+++ rpc_multipath_ops_destroy_xprt(xprt);
+++
++ /*
++ * Tear down transport state and free the rpc_xprt
++ */
++diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c
++index 6ebaa58b4eff..6202a0be1327 100644
++--- a/net/sunrpc/xprtmultipath.c
+++++ b/net/sunrpc/xprtmultipath.c
++@@ -18,6 +18,7 @@
++ #include <linux/sunrpc/xprt.h>
++ #include <linux/sunrpc/addr.h>
++ #include <linux/sunrpc/xprtmultipath.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++ typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct list_head *head,
++ const struct rpc_xprt *cur);
++@@ -26,8 +27,8 @@ static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular;
++ static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin;
++ static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall;
++
++-static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++- struct rpc_xprt *xprt)
+++void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+++ struct rpc_xprt *xprt)
++ {
++ if (unlikely(xprt_get(xprt) == NULL))
++ return;
++@@ -36,7 +37,9 @@ static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++ if (xps->xps_nxprts == 0)
++ xps->xps_net = xprt->xprt_net;
++ xps->xps_nxprts++;
+++ rpc_xps_nactive_add_one(xps);
++ }
+++EXPORT_SYMBOL(xprt_switch_add_xprt_locked);
++
++ /**
++ * rpc_xprt_switch_add_xprt - Add a new rpc_xprt to an rpc_xprt_switch
++@@ -63,6 +66,7 @@ static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps,
++ if (unlikely(xprt == NULL))
++ return;
++ xps->xps_nxprts--;
+++ rpc_xps_nactive_sub_one(xps);
++ if (xps->xps_nxprts == 0)
++ xps->xps_net = NULL;
++ smp_wmb();
++@@ -84,7 +88,7 @@ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
++ spin_unlock(&xps->xps_lock);
++ xprt_put(xprt);
++ }
++-
+++EXPORT_SYMBOL(rpc_xprt_switch_remove_xprt);
++ /**
++ * xprt_switch_alloc - Allocate a new struct rpc_xprt_switch
++ * @xprt: pointer to struct rpc_xprt
++@@ -102,7 +106,13 @@ struct rpc_xprt_switch *xprt_switch_alloc(struct rpc_xprt *xprt,
++ if (xps != NULL) {
++ spin_lock_init(&xps->xps_lock);
++ kref_init(&xps->xps_kref);
+++#if IS_ENABLED(CONFIG_ENFS)
+++ xps->xps_nxprts = 0;
+++ xps->xps_nactive = 0;
+++ atomic_long_set(&xps->xps_queuelen, 0);
+++#else
++ xps->xps_nxprts = 0;
+++#endif
++ INIT_LIST_HEAD(&xps->xps_xprt_list);
++ xps->xps_iter_ops = &rpc_xprt_iter_singular;
++ xprt_switch_add_xprt_locked(xps, xprt);
++@@ -148,6 +158,7 @@ struct rpc_xprt_switch *xprt_switch_get(struct rpc_xprt_switch *xps)
++ return xps;
++ return NULL;
++ }
+++EXPORT_SYMBOL(xprt_switch_get);
++
++ /**
++ * xprt_switch_put - Release a reference to a rpc_xprt_switch
++@@ -160,6 +171,7 @@ void xprt_switch_put(struct rpc_xprt_switch *xps)
++ if (xps != NULL)
++ kref_put(&xps->xps_kref, xprt_switch_free);
++ }
+++EXPORT_SYMBOL(xprt_switch_put);
++
++ /**
++ * rpc_xprt_switch_set_roundrobin - Set a round-robin policy on rpc_xprt_switch
+diff --git a/0003-add_enfs_module_for_nfs_mount_option.patch b/0003-add_enfs_module_for_nfs_mount_option.patch
+new file mode 100644
+index 0000000..70753b5
+--- /dev/null
++++ b/0003-add_enfs_module_for_nfs_mount_option.patch
+@@ -0,0 +1,1209 @@
++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..9c4c6c1880b6
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_multipath_parse.c
++@@ -0,0 +1,601 @@
+++// 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/parser.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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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..6f3e8703e3e2
++--- /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 nfsmultipathoptions type,
+++ char *str, void **enfs_option, struct net *net_ns);
+++void nfs_multipath_free_options(void **enfs_option);
+++
+++#endif
+diff --git a/0004-add_enfs_module_for_sunrpc_multipatch.patch b/0004-add_enfs_module_for_sunrpc_multipatch.patch
+new file mode 100644
+index 0000000..2c0fcc7
+--- /dev/null
++++ b/0004-add_enfs_module_for_sunrpc_multipatch.patch
+@@ -0,0 +1,1581 @@
++diff --git a/fs/nfs/enfs/enfs_multipath.h b/fs/nfs/enfs/enfs_multipath.h
++new file mode 100644
++index 000000000000..e064c2929ced
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_multipath.h
++@@ -0,0 +1,24 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: enfs multipath
+++ * Author:
+++ * 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_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_path.c b/fs/nfs/enfs/enfs_path.c
++new file mode 100644
++index 000000000000..7355f8c2f672
++--- /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 000000000000..97b1ef3730b8
++--- /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 000000000000..53fa1a07642f
++--- /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 000000000000..321951031c2e
++--- /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 000000000000..2c3fe125c735
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_remount.c
++@@ -0,0 +1,221 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: remount ip source file
+++ * Author: y00583252
+++ * 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 000000000000..a663ed257004
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_remount.h
++@@ -0,0 +1,15 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: remount ip header file
+++ * Author: y00583252
+++ * 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 000000000000..4e4eda784a3e
++--- /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 000000000000..b72b088a6258
++--- /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
+diff --git a/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
+new file mode 100644
+index 0000000..cc6b677
+--- /dev/null
++++ b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
+@@ -0,0 +1,1607 @@
++diff --git a/fs/nfs/enfs/enfs_config.c b/fs/nfs/enfs/enfs_config.c
++new file mode 100644
++index 000000000000..11aa7a00385b
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_config.c
++@@ -0,0 +1,378 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ */
+++#include <linux/cdev.h>
+++#include <linux/errno.h>
+++#include <linux/fcntl.h>
+++#include <linux/fs.h>
+++#include <linux/kernel.h>
+++#include <linux/kthread.h>
+++#include <linux/slab.h>
+++#include <linux/string.h>
+++#include <linux/uaccess.h>
+++#include <linux/delay.h>
+++
+++#include "enfs_errcode.h"
+++#include "enfs_log.h"
+++#include "enfs_config.h"
+++
+++#define MAX_FILE_SIZE 8192
+++#define STRING_BUF_SIZE 128
+++#define CONFIG_FILE_PATH "/etc/enfs/config.ini"
+++#define ENFS_NOTIFY_FILE_PERIOD 1000UL
+++
+++#define MAX_PATH_DETECT_INTERVAL 300
+++#define MIN_PATH_DETECT_INTERVAL 5
+++#define MAX_PATH_DETECT_TIMEOUT 60
+++#define MIN_PATH_DETECT_TIMEOUT 1
+++#define MAX_MULTIPATH_TIMEOUT 60
+++#define MIN_MULTIPATH_TIMEOUT 0
+++#define MAX_MULTIPATH_STATE ENFS_MULTIPATH_DISABLE
+++#define MIN_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
+++
+++#define DEFAULT_PATH_DETECT_INTERVAL 10
+++#define DEFAULT_PATH_DETECT_TIMEOUT 5
+++#define DEFAULT_MULTIPATH_TIMEOUT 0
+++#define DEFAULT_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
+++#define DEFAULT_LOADBALANCE_MODE ENFS_LOADBALANCE_RR
+++
+++typedef int (*check_and_assign_func)(char *, char *, int, int);
+++
+++struct enfs_config_info {
+++ int32_t path_detect_interval;
+++ int32_t path_detect_timeout;
+++ int32_t multipath_timeout;
+++ int32_t loadbalance_mode;
+++ int32_t multipath_state;
+++};
+++
+++struct check_and_assign_value {
+++ char *field_name;
+++ check_and_assign_func func;
+++ int min_value;
+++ int max_value;
+++};
+++
+++static struct enfs_config_info g_enfs_config_info;
+++static struct timespec64 modify_time;
+++static struct task_struct *thread;
+++
+++static int enfs_check_config_value(char *value, int min_value, int max_value)
+++{
+++ unsigned long num_value;
+++ int ret;
+++
+++ ret = kstrtol(value, 10, &num_value);
+++ if (ret != 0) {
+++ enfs_log_error("Failed to convert string to int\n");
+++ return -EINVAL;
+++ }
+++
+++ if (num_value < min_value || num_value > max_value)
+++ return -EINVAL;
+++
+++ return num_value;
+++}
+++
+++static int32_t enfs_check_and_assign_int_value(char *field_name, char *value,
+++ int min_value, int max_value)
+++{
+++ int int_value = enfs_check_config_value(value, min_value, max_value);
+++
+++ if (int_value < 0)
+++ return -EINVAL;
+++
+++ if (strcmp(field_name, "path_detect_interval") == 0) {
+++ g_enfs_config_info.path_detect_interval = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ if (strcmp(field_name, "path_detect_timeout") == 0) {
+++ g_enfs_config_info.path_detect_timeout = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ if (strcmp(field_name, "multipath_timeout") == 0) {
+++ g_enfs_config_info.multipath_timeout = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ if (strcmp(field_name, "multipath_disable") == 0) {
+++ g_enfs_config_info.multipath_state = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ return -EINVAL;
+++}
+++
+++static int32_t enfs_check_and_assign_loadbalance_mode(char *field_name,
+++ char *value,
+++ int min_value,
+++ int max_value)
+++{
+++ if (value == NULL)
+++ return -EINVAL;
+++
+++ if (strcmp(field_name, "multipath_select_policy") == 0) {
+++ if (strcmp(value, "roundrobin") == 0) {
+++ g_enfs_config_info.loadbalance_mode
+++ = ENFS_LOADBALANCE_RR;
+++ return ENFS_RET_OK;
+++ }
+++ }
+++ return -EINVAL;
+++}
+++
+++static const struct check_and_assign_value g_check_and_assign_value[] = {
+++ {"path_detect_interval", enfs_check_and_assign_int_value,
+++ MIN_PATH_DETECT_INTERVAL, MAX_PATH_DETECT_INTERVAL},
+++ {"path_detect_timeout", enfs_check_and_assign_int_value,
+++ MIN_PATH_DETECT_TIMEOUT, MAX_PATH_DETECT_TIMEOUT},
+++ {"multipath_timeout", enfs_check_and_assign_int_value,
+++ MIN_MULTIPATH_TIMEOUT, MAX_MULTIPATH_TIMEOUT},
+++ {"multipath_disable", enfs_check_and_assign_int_value,
+++ MIN_MULTIPATH_STATE, MAX_MULTIPATH_STATE},
+++ {"multipath_select_policy", enfs_check_and_assign_loadbalance_mode,
+++ 0, 0},
+++};
+++
+++static int32_t enfs_read_config_file(char *buffer, char *file_path)
+++{
+++ int ret;
+++ struct file *filp = NULL;
+++ loff_t f_pos = 0;
+++ mm_segment_t fs;
+++
+++
+++ filp = filp_open(file_path, O_RDONLY, 0);
+++
+++ if (IS_ERR(filp)) {
+++ enfs_log_error("Failed to open file %s\n", CONFIG_FILE_PATH);
+++ ret = -ENOENT;
+++ return ret;
+++ }
+++
+++ fs = get_fs();
+++ set_fs(get_ds());
+++ kernel_read(filp, buffer, MAX_FILE_SIZE, &f_pos);
+++ set_fs(fs);
+++
+++ ret = filp_close(filp, NULL);
+++ if (ret) {
+++ enfs_log_error("Close File:%s failed:%d.\n",
+++ CONFIG_FILE_PATH, ret);
+++ return -EINVAL;
+++ }
+++ return ENFS_RET_OK;
+++}
+++
+++static int32_t enfs_deal_with_comment_line(char *buffer)
+++{
+++ int ret;
+++ char *pos = strchr(buffer, '\n');
+++
+++ if (pos != NULL)
+++ ret = strlen(buffer) - strlen(pos);
+++ else
+++ ret = strlen(buffer);
+++
+++ return ret;
+++}
+++
+++static int32_t enfs_parse_key_value_from_config(char *buffer, char *key,
+++ char *value, int keyLen,
+++ int valueLen)
+++{
+++ char *line;
+++ char *tokenPtr;
+++ int len;
+++ char *tem;
+++ char *pos = strchr(buffer, '\n');
+++
+++ if (pos != NULL)
+++ len = strlen(buffer) - strlen(pos);
+++ else
+++ len = strlen(buffer);
+++
+++ line = kmalloc(len + 1, GFP_KERNEL);
+++ if (!line) {
+++ enfs_log_error("Failed to allocate memory.\n");
+++ return -ENOMEM;
+++ }
+++ line[len] = '\0';
+++ strncpy(line, buffer, len);
+++
+++ tem = line;
+++ tokenPtr = strsep(&tem, "=");
+++ if (tokenPtr == NULL || tem == NULL) {
+++ kfree(line);
+++ return len;
+++ }
+++ strncpy(key, strim(tokenPtr), keyLen);
+++ strncpy(value, strim(tem), valueLen);
+++
+++ kfree(line);
+++ return len;
+++}
+++
+++static int32_t enfs_get_value_from_config_file(char *buffer, char *field_name,
+++ char *value, int valueLen)
+++{
+++ int ret;
+++ char key[STRING_BUF_SIZE + 1] = {0};
+++ char val[STRING_BUF_SIZE + 1] = {0};
+++
+++ while (buffer[0] != '\0') {
+++ if (buffer[0] == '\n') {
+++ buffer++;
+++ } else if (buffer[0] == '#') {
+++ ret = enfs_deal_with_comment_line(buffer);
+++ if (ret > 0)
+++ buffer += ret;
+++ } else {
+++ ret = enfs_parse_key_value_from_config(buffer, key, val,
+++ STRING_BUF_SIZE,
+++ STRING_BUF_SIZE);
+++ if (ret < 0) {
+++ enfs_log_error("failed parse key value, %d\n"
+++ , ret);
+++ return ret;
+++ }
+++ key[STRING_BUF_SIZE] = '\0';
+++ val[STRING_BUF_SIZE] = '\0';
+++
+++ buffer += ret;
+++
+++ if (strcmp(field_name, key) == 0) {
+++ strncpy(value, val, valueLen);
+++ return ENFS_RET_OK;
+++ }
+++ }
+++ }
+++ enfs_log_error("can not find value which matched field_name: %s.\n",
+++ field_name);
+++ return -EINVAL;
+++}
+++
+++int32_t enfs_config_load(void)
+++{
+++ char value[STRING_BUF_SIZE + 1];
+++ int ret;
+++ int table_len;
+++ int min;
+++ int max;
+++ int i;
+++ char *buffer;
+++
+++ buffer = kmalloc(MAX_FILE_SIZE, GFP_KERNEL);
+++ if (!buffer) {
+++ enfs_log_error("Failed to allocate memory.\n");
+++ return -ENOMEM;
+++ }
+++ memset(buffer, 0, MAX_FILE_SIZE);
+++
+++ g_enfs_config_info.path_detect_interval = DEFAULT_PATH_DETECT_INTERVAL;
+++ g_enfs_config_info.path_detect_timeout = DEFAULT_PATH_DETECT_TIMEOUT;
+++ g_enfs_config_info.multipath_timeout = DEFAULT_MULTIPATH_TIMEOUT;
+++ g_enfs_config_info.multipath_state = DEFAULT_MULTIPATH_STATE;
+++ g_enfs_config_info.loadbalance_mode = DEFAULT_LOADBALANCE_MODE;
+++
+++ table_len = sizeof(g_check_and_assign_value) /
+++ sizeof(g_check_and_assign_value[0]);
+++
+++ ret = enfs_read_config_file(buffer, CONFIG_FILE_PATH);
+++ if (ret != 0) {
+++ kfree(buffer);
+++ return ret;
+++ }
+++
+++ for (i = 0; i < table_len; i++) {
+++ ret = enfs_get_value_from_config_file(buffer,
+++ g_check_and_assign_value[i].field_name,
+++ value, STRING_BUF_SIZE);
+++ if (ret < 0)
+++ continue;
+++
+++ value[STRING_BUF_SIZE] = '\0';
+++ min = g_check_and_assign_value[i].min_value;
+++ max = g_check_and_assign_value[i].max_value;
+++ if (g_check_and_assign_value[i].func != NULL)
+++ (*g_check_and_assign_value[i].func)(
+++ g_check_and_assign_value[i].field_name,
+++ value, min, max);
+++ }
+++
+++ kfree(buffer);
+++ return ENFS_RET_OK;
+++}
+++
+++int32_t enfs_get_config_path_detect_interval(void)
+++{
+++ return g_enfs_config_info.path_detect_interval;
+++}
+++
+++int32_t enfs_get_config_path_detect_timeout(void)
+++{
+++ return g_enfs_config_info.path_detect_timeout;
+++}
+++
+++int32_t enfs_get_config_multipath_timeout(void)
+++{
+++ return g_enfs_config_info.multipath_timeout;
+++}
+++
+++int32_t enfs_get_config_multipath_state(void)
+++{
+++ return g_enfs_config_info.multipath_state;
+++}
+++
+++int32_t enfs_get_config_loadbalance_mode(void)
+++{
+++ return g_enfs_config_info.loadbalance_mode;
+++}
+++
+++static bool enfs_file_changed(const char *filename)
+++{
+++ int err;
+++ struct kstat file_stat;
+++
+++ err = vfs_stat(filename, &file_stat);
+++ if (err) {
+++ pr_err("failed to open file:%s err:%d\n", filename, err);
+++ return false;
+++ }
+++
+++ if (timespec64_compare(&modify_time, &file_stat.mtime) == -1) {
+++ modify_time = file_stat.mtime;
+++ pr_info("file change: %lld %lld\n", modify_time.tv_sec,
+++ file_stat.mtime.tv_sec);
+++ return true;
+++ }
+++
+++ return false;
+++}
+++
+++static int enfs_thread_func(void *data)
+++{
+++ while (!kthread_should_stop()) {
+++ if (enfs_file_changed(CONFIG_FILE_PATH))
+++ enfs_config_load();
+++
+++ msleep(ENFS_NOTIFY_FILE_PERIOD);
+++ }
+++ return 0;
+++}
+++
+++int enfs_config_timer_init(void)
+++{
+++ thread = kthread_run(enfs_thread_func, NULL, "enfs_notiy_file_thread");
+++ if (IS_ERR(thread)) {
+++ pr_err("Failed to create kernel thread\n");
+++ return PTR_ERR(thread);
+++ }
+++ return 0;
+++}
+++
+++void enfs_config_timer_exit(void)
+++{
+++ pr_info("enfs_notify_file_exit\n");
+++ if (thread)
+++ kthread_stop(thread);
+++}
++diff --git a/fs/nfs/enfs/enfs_config.h b/fs/nfs/enfs/enfs_config.h
++new file mode 100644
++index 000000000000..752710129170
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_config.h
++@@ -0,0 +1,32 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs configuration
+++ * Author: y00583252
+++ * Create: 2023-07-27
+++ */
+++
+++#ifndef ENFS_CONFIG_H
+++#define ENFS_CONFIG_H
+++
+++#include <linux/types.h>
+++
+++enum enfs_multipath_state {
+++ ENFS_MULTIPATH_ENABLE = 0,
+++ ENFS_MULTIPATH_DISABLE = 1,
+++};
+++
+++enum enfs_loadbalance_mode {
+++ ENFS_LOADBALANCE_RR,
+++};
+++
+++
+++int32_t enfs_get_config_path_detect_interval(void);
+++int32_t enfs_get_config_path_detect_timeout(void);
+++int32_t enfs_get_config_multipath_timeout(void);
+++int32_t enfs_get_config_multipath_state(void);
+++int32_t enfs_get_config_loadbalance_mode(void);
+++int32_t enfs_config_load(void);
+++int32_t enfs_config_timer_init(void);
+++void enfs_config_timer_exit(void);
+++#endif // ENFS_CONFIG_H
++diff --git a/fs/nfs/enfs/enfs_errcode.h b/fs/nfs/enfs/enfs_errcode.h
++new file mode 100644
++index 000000000000..cca47ab9a191
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_errcode.h
++@@ -0,0 +1,17 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs errocode
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#ifndef ENFS_ERRCODE_H
+++#define ENFS_ERRCODE_H
+++
+++enum {
+++ ENFS_RET_OK = 0,
+++ ENFS_RET_FAIL
+++};
+++
+++#endif // ENFS_ERRCODE_H
++diff --git a/fs/nfs/enfs/enfs_log.h b/fs/nfs/enfs/enfs_log.h
++new file mode 100644
++index 000000000000..177b404f05df
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_log.h
++@@ -0,0 +1,25 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: enfs log
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++#ifndef ENFS_LOG_H
+++#define ENFS_LOG_H
+++
+++#include <linux/printk.h>
+++
+++#define enfs_log_info(fmt, ...) \
+++ pr_info("enfs:[%s]" pr_fmt(fmt), \
+++ __func__, ##__VA_ARGS__)
+++
+++#define enfs_log_error(fmt, ...) \
+++ pr_err("enfs:[%s]" pr_fmt(fmt), \
+++ __func__, ##__VA_ARGS__)
+++
+++#define enfs_log_debug(fmt, ...) \
+++ pr_debug("enfs:[%s]" pr_fmt(fmt), \
+++ __func__, ##__VA_ARGS__)
+++
+++#endif // ENFS_ERRCODE_H
++diff --git a/fs/nfs/enfs/failover_com.h b/fs/nfs/enfs/failover_com.h
++new file mode 100644
++index 000000000000..c52940da232e
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_com.h
++@@ -0,0 +1,23 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: failover time commont header file
+++ * Create: 2023-08-02
+++ */
+++#ifndef FAILOVER_COMMON_H
+++#define FAILOVER_COMMON_H
+++
+++static inline bool failover_is_enfs_clnt(struct rpc_clnt *clnt)
+++{
+++ struct rpc_clnt *next = clnt->cl_parent;
+++
+++ while (next) {
+++ if (next == next->cl_parent)
+++ break;
+++ next = next->cl_parent;
+++ }
+++
+++ return next != NULL ? next->cl_enfs : clnt->cl_enfs;
+++}
+++
+++#endif // FAILOVER_COMMON_H
++diff --git a/fs/nfs/enfs/failover_path.c b/fs/nfs/enfs/failover_path.c
++new file mode 100644
++index 000000000000..93b454de29d1
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_path.c
++@@ -0,0 +1,207 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs path failover file
+++ * Author: y00583252
+++ * Create: 2023-08-02
+++ */
+++
+++#include "failover_path.h"
+++#include <linux/nfs.h>
+++#include <linux/nfs3.h>
+++#include <linux/nfs4.h>
+++#include <linux/sunrpc/clnt.h>
+++#include <linux/sunrpc/sched.h>
+++#include <linux/sunrpc/xprt.h>
+++#include "enfs_config.h"
+++#include "enfs_log.h"
+++#include "failover_com.h"
+++#include "pm_state.h"
+++#include "pm_ping.h"
+++
+++enum failover_policy_t {
+++ FAILOVER_NOACTION = 1,
+++ FAILOVER_RETRY,
+++ FAILOVER_RETRY_DELAY,
+++};
+++
+++static void failover_retry_path(struct rpc_task *task)
+++{
+++ xprt_release(task);
+++ rpc_init_task_retry_counters(task);
+++ rpc_task_release_transport(task);
+++ rpc_restart_call(task);
+++}
+++
+++static void failover_retry_path_delay(struct rpc_task *task, int32_t delay)
+++{
+++ failover_retry_path(task);
+++ rpc_delay(task, delay);
+++}
+++
+++static void failover_retry_path_by_policy(struct rpc_task *task,
+++ enum failover_policy_t policy)
+++{
+++ if (policy == FAILOVER_RETRY)
+++ failover_retry_path(task);
+++ else if (policy == FAILOVER_RETRY_DELAY)
+++ failover_retry_path_delay(task, 3 * HZ); // delay 3s
+++}
+++
+++static
+++enum failover_policy_t failover_get_nfs3_retry_policy(struct rpc_task *task)
+++{
+++ enum failover_policy_t policy = FAILOVER_NOACTION;
+++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
+++ u32 proc;
+++
+++ if (unlikely(procinfo == NULL)) {
+++ enfs_log_error("the task contains no valid proc.\n");
+++ return FAILOVER_NOACTION;
+++ }
+++
+++ proc = procinfo->p_proc;
+++
+++ switch (proc) {
+++ case NFS3PROC_CREATE:
+++ case NFS3PROC_MKDIR:
+++ case NFS3PROC_REMOVE:
+++ case NFS3PROC_RMDIR:
+++ case NFS3PROC_SYMLINK:
+++ case NFS3PROC_LINK:
+++ case NFS3PROC_SETATTR:
+++ case NFS3PROC_WRITE:
+++ policy = FAILOVER_RETRY_DELAY;
+++ default:
+++ policy = FAILOVER_RETRY;
+++ }
+++ return policy;
+++}
+++
+++static
+++enum failover_policy_t failover_get_nfs4_retry_policy(struct rpc_task *task)
+++{
+++ enum failover_policy_t policy = FAILOVER_NOACTION;
+++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
+++ u32 proc_idx;
+++
+++ if (unlikely(procinfo == NULL)) {
+++ enfs_log_error("the task contains no valid proc.\n");
+++ return FAILOVER_NOACTION;
+++ }
+++
+++ proc_idx = procinfo->p_statidx;
+++
+++ switch (proc_idx) {
+++ case NFSPROC4_CLNT_CREATE:
+++ case NFSPROC4_CLNT_REMOVE:
+++ case NFSPROC4_CLNT_LINK:
+++ case NFSPROC4_CLNT_SYMLINK:
+++ case NFSPROC4_CLNT_SETATTR:
+++ case NFSPROC4_CLNT_WRITE:
+++ case NFSPROC4_CLNT_RENAME:
+++ case NFSPROC4_CLNT_SETACL:
+++ policy = FAILOVER_RETRY_DELAY;
+++ default:
+++ policy = FAILOVER_RETRY;
+++ }
+++ return policy;
+++}
+++
+++static enum failover_policy_t failover_get_retry_policy(struct rpc_task *task)
+++{
+++ struct rpc_clnt *clnt = task->tk_client;
+++ u32 version = clnt->cl_vers;
+++ enum failover_policy_t policy = FAILOVER_NOACTION;
+++
+++ // 1. if the task meant to send to certain xprt, take no action
+++ if (task->tk_flags & RPC_TASK_FIXED)
+++ return FAILOVER_NOACTION;
+++
+++ // 2. get policy by different version of nfs protocal
+++ if (version == 3) // nfs v3
+++ policy = failover_get_nfs3_retry_policy(task);
+++ else if (version == 4) // nfs v4
+++ policy = failover_get_nfs4_retry_policy(task);
+++ else
+++ return FAILOVER_NOACTION;
+++
+++ // 3. if the task is not send to target, retry immediately
+++ if (!RPC_WAS_SENT(task))
+++ policy = FAILOVER_RETRY;
+++
+++ return policy;
+++}
+++
+++static int failover_check_task(struct rpc_task *task)
+++{
+++ struct rpc_clnt *clnt = NULL;
+++ int disable_mpath = enfs_get_config_multipath_state();
+++
+++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return -EINVAL;
+++ }
+++
+++ if (unlikely((task == NULL) || (task->tk_client == NULL))) {
+++ enfs_log_error("The task is not valid.\n");
+++ return -EINVAL;
+++ }
+++
+++ clnt = task->tk_client;
+++
+++ if (clnt->cl_prog != NFS_PROGRAM) {
+++ enfs_log_debug("The clnt is not prog{%u} type.\n",
+++ clnt->cl_prog);
+++ return -EINVAL;
+++ }
+++
+++ if (!failover_is_enfs_clnt(clnt)) {
+++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
+++ return -EINVAL;
+++ }
+++ return 0;
+++}
+++
+++void failover_handle(struct rpc_task *task)
+++{
+++ enum failover_policy_t policy;
+++ int ret;
+++
+++ ret = failover_check_task(task);
+++ if (ret != 0)
+++ return;
+++
+++ pm_set_path_state(task->tk_xprt, PM_STATE_FAULT);
+++
+++ policy = failover_get_retry_policy(task);
+++
+++ failover_retry_path_by_policy(task, policy);
+++}
+++
+++bool failover_task_need_call_start_again(struct rpc_task *task)
+++{
+++ int ret;
+++
+++ ret = failover_check_task(task);
+++ if (ret != 0)
+++ return false;
+++
+++ return true;
+++}
+++
+++bool failover_prepare_transmit(struct rpc_task *task)
+++{
+++ if (task->tk_flags & RPC_TASK_FIXED)
+++ return true;
+++
+++ if (pm_ping_is_test_xprt_task(task))
+++ return true;
+++
+++ if (pm_get_path_state(task->tk_xprt) == PM_STATE_FAULT) {
+++ task->tk_status = -ETIMEDOUT;
+++ return false;
+++ }
+++
+++ return true;
+++}
++diff --git a/fs/nfs/enfs/failover_path.h b/fs/nfs/enfs/failover_path.h
++new file mode 100644
++index 000000000000..6f1294829a6e
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_path.h
++@@ -0,0 +1,17 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs path failover header file
+++ * Author: y00583252
+++ * Create: 2023-08-02
+++ */
+++
+++#ifndef FAILOVER_PATH_H
+++#define FAILOVER_PATH_H
+++
+++#include <linux/sunrpc/sched.h>
+++
+++void failover_handle(struct rpc_task *task);
+++bool failover_prepare_transmit(struct rpc_task *task);
+++
+++#endif // FAILOVER_PATH_H
++diff --git a/fs/nfs/enfs/failover_time.c b/fs/nfs/enfs/failover_time.c
++new file mode 100644
++index 000000000000..866ea82d13fc
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_time.c
++@@ -0,0 +1,99 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: failover time file
+++ * Create: 2023-08-02
+++ */
+++
+++#include "failover_time.h"
+++#include <linux/jiffies.h>
+++#include <linux/sunrpc/clnt.h>
+++#include "enfs_config.h"
+++#include "enfs_log.h"
+++#include "failover_com.h"
+++#include "pm_ping.h"
+++
+++static unsigned long failover_get_mulitipath_timeout(struct rpc_clnt *clnt)
+++{
+++ unsigned long config_tmo = enfs_get_config_multipath_timeout() * HZ;
+++ unsigned long clnt_tmo = clnt->cl_timeout->to_initval;
+++
+++ if (config_tmo == 0)
+++ return clnt_tmo;
+++
+++ return config_tmo > clnt_tmo ? clnt_tmo : config_tmo;
+++}
+++
+++void failover_adjust_task_timeout(struct rpc_task *task, void *condition)
+++{
+++ struct rpc_clnt *clnt = NULL;
+++ unsigned long tmo;
+++ int disable_mpath = enfs_get_config_multipath_state();
+++
+++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return;
+++ }
+++
+++ clnt = task->tk_client;
+++ if (unlikely(clnt == NULL)) {
+++ enfs_log_error("task associate client is NULL.\n");
+++ return;
+++ }
+++
+++ if (!failover_is_enfs_clnt(clnt)) {
+++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
+++ return;
+++ }
+++
+++ tmo = failover_get_mulitipath_timeout(clnt);
+++ if (tmo == 0) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return;
+++ }
+++
+++ if (task->tk_timeout != 0)
+++ task->tk_timeout =
+++ task->tk_timeout < tmo ? task->tk_timeout : tmo;
+++ else
+++ task->tk_timeout = tmo;
+++}
+++
+++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
+++{
+++ struct rpc_clnt *clnt = NULL;
+++ int disable_mpath = enfs_get_config_multipath_state();
+++
+++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return;
+++ }
+++
+++ clnt = task->tk_client;
+++ if (unlikely(clnt == NULL)) {
+++ enfs_log_error("task associate client is NULL.\n");
+++ return;
+++ }
+++
+++ if (!failover_is_enfs_clnt(clnt)) {
+++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
+++ return;
+++ }
+++
+++ if (!pm_ping_is_test_xprt_task(task))
+++ req->rq_timeout = failover_get_mulitipath_timeout(clnt);
+++ else {
+++ req->rq_timeout = enfs_get_config_path_detect_timeout() * HZ;
+++ req->rq_majortimeo = req->rq_timeout + jiffies;
+++ }
+++
+++ /*
+++ * when task is retried, the req is new, we lost major-timeout times,
+++ * so we have to restore req major
+++ * timeouts from the task, if it is stored.
+++ */
+++ if (task->tk_major_timeo != 0)
+++ req->rq_majortimeo = task->tk_major_timeo;
+++ else
+++ task->tk_major_timeo = req->rq_majortimeo;
+++}
++diff --git a/fs/nfs/enfs/failover_time.h b/fs/nfs/enfs/failover_time.h
++new file mode 100644
++index 000000000000..ede25b577a2a
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_time.h
++@@ -0,0 +1,16 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: failover time header file
+++ * Create: 2023-08-02
+++ */
+++
+++#ifndef FAILOVER_TIME_H
+++#define FAILOVER_TIME_H
+++
+++#include <linux/sunrpc/sched.h>
+++
+++void failover_adjust_task_timeout(struct rpc_task *task, void *condition);
+++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req);
+++
+++#endif // FAILOVER_TIME_H
++diff --git a/fs/nfs/enfs/init.h b/fs/nfs/enfs/init.h
++new file mode 100644
++index 000000000000..fdabb9084e19
++--- /dev/null
+++++ b/fs/nfs/enfs/init.h
++@@ -0,0 +1,17 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs client init
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#ifndef ENFS_INIT_H
+++#define ENFS_INIT_H
+++
+++#include <linux/types.h>
+++
+++int32_t enfs_init(void);
+++void enfs_fini(void);
+++
+++#endif
++diff --git a/fs/nfs/enfs/mgmt_init.c b/fs/nfs/enfs/mgmt_init.c
++new file mode 100644
++index 000000000000..75a40c5e0f6c
++--- /dev/null
+++++ b/fs/nfs/enfs/mgmt_init.c
++@@ -0,0 +1,22 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: mgmt component init
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#include "mgmt_init.h"
+++#include <linux/printk.h>
+++#include "enfs_errcode.h"
+++#include "enfs_config.h"
+++
+++int32_t mgmt_init(void)
+++{
+++ return enfs_config_timer_init();
+++}
+++
+++void mgmt_fini(void)
+++{
+++ enfs_config_timer_exit();
+++}
++diff --git a/fs/nfs/enfs/mgmt_init.h b/fs/nfs/enfs/mgmt_init.h
++new file mode 100644
++index 000000000000..aa78303b9f01
++--- /dev/null
+++++ b/fs/nfs/enfs/mgmt_init.h
++@@ -0,0 +1,18 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: mgmt component init
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#ifndef MGMT_INIT_H
+++#define MGMT_INIT_H
+++
+++#include <linux/types.h>
+++
+++int32_t mgmt_init(void);
+++void mgmt_fini(void);
+++
+++
+++#endif // MGMT_INIT_H
++diff --git a/fs/nfs/enfs/pm_ping.c b/fs/nfs/enfs/pm_ping.c
++new file mode 100644
++index 000000000000..24153cd4c7f3
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_ping.c
++@@ -0,0 +1,421 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: path state header file
+++ * Author: x00833432
+++ * Create: 2023-08-21
+++ */
+++
+++#include "pm_ping.h"
+++#include <linux/err.h>
+++#include <linux/spinlock.h>
+++#include <linux/slab.h>
+++#include <linux/module.h>
+++#include <linux/printk.h>
+++#include <linux/kthread.h>
+++#include <linux/nfs.h>
+++#include <linux/errno.h>
+++#include <linux/rcupdate.h>
+++#include <linux/workqueue.h>
+++#include <net/netns/generic.h>
+++#include <linux/atomic.h>
+++#include <linux/sunrpc/clnt.h>
+++
+++#include "../../../net/sunrpc/netns.h"
+++#include "pm_state.h"
+++#include "enfs.h"
+++#include "enfs_log.h"
+++#include "enfs_config.h"
+++
+++#define SLEEP_INTERVAL 2
+++extern unsigned int sunrpc_net_id;
+++
+++static struct task_struct *pm_ping_timer_thread;
+++//protect pint_execute_workq
+++static spinlock_t ping_execute_workq_lock;
+++// timer for test xprt workqueue
+++static struct workqueue_struct *ping_execute_workq;
+++// count the ping xprt work on flight
+++static atomic_t check_xprt_count;
+++
+++struct ping_xprt_work {
+++ struct rpc_xprt *xprt; // use this specific xprt
+++ struct rpc_clnt *clnt; // use this specific rpc_client
+++ struct work_struct ping_work;
+++};
+++
+++struct pm_ping_async_callback {
+++ void *data;
+++ void (*func)(void *data);
+++};
+++
+++// set xprt's enum pm_check_state
+++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
+++ enum pm_check_state state)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++
+++ if (IS_ERR(xprt)) {
+++ enfs_log_error("The xprt ptr is not exist.\n");
+++ return;
+++ }
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ xprt_get(xprt);
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ xprt_put(xprt);
+++ return;
+++ }
+++
+++ atomic_set(&ctx->path_check_state, state);
+++ xprt_put(xprt);
+++}
+++
+++// get xprt's enum pm_check_state
+++static enum pm_check_state pm_ping_get_path_check_state(struct rpc_xprt *xprt)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++ enum pm_check_state state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return PM_CHECK_UNDEFINE;
+++ }
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ return PM_CHECK_UNDEFINE;
+++ }
+++
+++ state = atomic_read(&ctx->path_check_state);
+++
+++ return state;
+++}
+++
+++static void pm_ping_call_done_callback(void *data)
+++{
+++ struct pm_ping_async_callback *callback_data =
+++ (struct pm_ping_async_callback *)data;
+++
+++ if (callback_data == NULL)
+++ return;
+++
+++ callback_data->func(callback_data->data);
+++
+++ kfree(callback_data);
+++}
+++
+++// Default callback for async RPC calls
+++static void pm_ping_call_done(struct rpc_task *task, void *data)
+++{
+++ struct rpc_xprt *xprt = task->tk_xprt;
+++
+++ atomic_dec(&check_xprt_count);
+++ if (task->tk_status >= 0)
+++ pm_set_path_state(xprt, PM_STATE_NORMAL);
+++ else
+++ pm_set_path_state(xprt, PM_STATE_FAULT);
+++
+++ pm_ping_set_path_check_state(xprt, PM_CHECK_FINISH);
+++
+++ pm_ping_call_done_callback(data);
+++}
+++
+++// register func to rpc_call_done
+++static const struct rpc_call_ops pm_ping_set_status_ops = {
+++ .rpc_call_done = pm_ping_call_done,
+++};
+++
+++// execute work which in work_queue
+++static void pm_ping_execute_work(struct work_struct *work)
+++{
+++ int ret = 0;
+++
+++ // get the work information
+++ struct ping_xprt_work *work_info =
+++ container_of(work, struct ping_xprt_work, ping_work);
+++
+++ // if check state is pending
+++ if (pm_ping_get_path_check_state(work_info->xprt) == PM_CHECK_WAITING) {
+++
+++ pm_ping_set_path_check_state(work_info->xprt,
+++ PM_CHECK_CHECKING);
+++
+++ ret = rpc_clnt_test_xprt(work_info->clnt,
+++ work_info->xprt,
+++ &pm_ping_set_status_ops,
+++ NULL,
+++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
+++
+++ if (ret < 0) {
+++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
+++
+++ pm_ping_set_path_check_state(work_info->xprt,
+++ PM_CHECK_FINISH);
+++
+++ } else
+++ atomic_inc(&check_xprt_count);
+++
+++ }
+++
+++ atomic_dec(&work_info->clnt->cl_count);
+++ xprt_put(work_info->xprt);
+++ kfree(work_info);
+++ work_info = NULL;
+++}
+++
+++static bool pm_ping_workqueue_queue_work(struct work_struct *work)
+++{
+++ bool ret = false;
+++
+++ spin_lock(&ping_execute_workq_lock);
+++
+++ if (ping_execute_workq != NULL)
+++ ret = queue_work(ping_execute_workq, work);
+++
+++ spin_unlock(&ping_execute_workq_lock);
+++ return ret;
+++}
+++
+++// init test work and add this work to workqueue
+++static int pm_ping_add_work(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt, void *data)
+++{
+++ struct ping_xprt_work *work_info;
+++ bool ret = false;
+++
+++ if (IS_ERR(xprt) || xprt == NULL) {
+++ enfs_log_error("The xprt ptr is not exist.\n");
+++ return -EINVAL;
+++ }
+++
+++ if (IS_ERR(clnt) || clnt == NULL) {
+++ enfs_log_error("The clnt ptr is not exist.\n");
+++ return -EINVAL;
+++ }
+++
+++ if (!xprt->multipath_context) {
+++ enfs_log_error("multipath_context is null.\n");
+++ return -EINVAL;
+++ }
+++
+++ // check xprt pending status, if pending status equals Finish
+++ // means this xprt can inster to work queue
+++ if (pm_ping_get_path_check_state(xprt) ==
+++ PM_CHECK_FINISH ||
+++ pm_ping_get_path_check_state(xprt) ==
+++ PM_CHECK_INIT) {
+++
+++ enfs_log_debug("find xprt pointer. %p\n", xprt);
+++ work_info = kzalloc(sizeof(struct ping_xprt_work), GFP_ATOMIC);
+++ if (work_info == NULL)
+++ return -ENOMEM;
+++ work_info->clnt = clnt;
+++ atomic_inc(&clnt->cl_count);
+++ work_info->xprt = xprt;
+++ xprt_get(xprt);
+++ INIT_WORK(&work_info->ping_work, pm_ping_execute_work);
+++ pm_ping_set_path_check_state(xprt, PM_CHECK_WAITING);
+++
+++ ret = pm_ping_workqueue_queue_work(&work_info->ping_work);
+++ if (!ret) {
+++ atomic_dec(&work_info->clnt->cl_count);
+++ xprt_put(work_info->xprt);
+++ kfree(work_info);
+++ return -EINVAL;
+++ }
+++ }
+++ return 0;
+++}
+++
+++// encapsulate pm_ping_add_work()
+++static int pm_ping_execute_xprt_test(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt, void *data)
+++{
+++ pm_ping_add_work(clnt, xprt, NULL);
+++ // return 0 for rpc_clnt_iterate_for_each_xprt();
+++ // because negative value will stop iterate all xprt
+++ // and we need return negative value for debug
+++ // Therefore, we need this function to iterate all xprt
+++ return 0;
+++}
+++
+++// export to other module add ping work to workqueue
+++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+++{
+++ int ret;
+++
+++ ret = pm_ping_add_work(clnt, xprt, NULL);
+++ return ret;
+++}
+++
+++// iterate xprt in the client
+++static void pm_ping_loop_rpclnt(struct sunrpc_net *sn)
+++{
+++ struct rpc_clnt *clnt;
+++
+++ spin_lock(&sn->rpc_client_lock);
+++ list_for_each_entry_rcu(clnt, &sn->all_clients, cl_clients) {
+++ if (clnt->cl_enfs) {
+++ enfs_log_debug("find rpc_clnt. %p\n", clnt);
+++ rpc_clnt_iterate_for_each_xprt(clnt,
+++ pm_ping_execute_xprt_test, NULL);
+++ }
+++ }
+++ spin_unlock(&sn->rpc_client_lock);
+++}
+++
+++// iterate each clnt in the sunrpc_net
+++static void pm_ping_loop_sunrpc_net(void)
+++{
+++ struct net *net;
+++ struct sunrpc_net *sn;
+++
+++ rcu_read_lock();
+++ for_each_net_rcu(net) {
+++ sn = net_generic(net, sunrpc_net_id);
+++ if (sn == NULL)
+++ continue;
+++ pm_ping_loop_rpclnt(sn);
+++ }
+++ rcu_read_unlock();
+++}
+++
+++static int pm_ping_routine(void *data)
+++{
+++ while (!kthread_should_stop()) {
+++ // equale 0 means open multipath
+++ if (enfs_get_config_multipath_state() ==
+++ ENFS_MULTIPATH_ENABLE)
+++ pm_ping_loop_sunrpc_net();
+++
+++ msleep((unsigned int)
+++ enfs_get_config_path_detect_interval() * 1000);
+++ }
+++ return 0;
+++}
+++
+++// start thread to cycly ping
+++static int pm_ping_start(void)
+++{
+++ pm_ping_timer_thread =
+++ kthread_run(pm_ping_routine, NULL, "pm_ping_routine");
+++ if (IS_ERR(pm_ping_timer_thread)) {
+++ enfs_log_error("Failed to create kernel thread\n");
+++ return PTR_ERR(pm_ping_timer_thread);
+++ }
+++ return 0;
+++}
+++
+++// initialize workqueue
+++static int pm_ping_workqueue_init(void)
+++{
+++ struct workqueue_struct *queue = NULL;
+++
+++ queue = create_workqueue("pm_ping_workqueue");
+++
+++ if (queue == NULL) {
+++ enfs_log_error("create workqueue failed.\n");
+++ return -ENOMEM;
+++ }
+++
+++ spin_lock(&ping_execute_workq_lock);
+++ ping_execute_workq = queue;
+++ spin_unlock(&ping_execute_workq_lock);
+++ enfs_log_info("create workqueue succeeeded.\n");
+++ return 0;
+++}
+++
+++static void pm_ping_workqueue_fini(void)
+++{
+++ struct workqueue_struct *queue = NULL;
+++
+++ spin_lock(&ping_execute_workq_lock);
+++ queue = ping_execute_workq;
+++ ping_execute_workq = NULL;
+++ spin_unlock(&ping_execute_workq_lock);
+++
+++ enfs_log_info("delete work queue\n");
+++
+++ if (queue != NULL) {
+++ flush_workqueue(queue);
+++ destroy_workqueue(queue);
+++ }
+++}
+++
+++// module exit func
+++void pm_ping_fini(void)
+++{
+++ if (pm_ping_timer_thread)
+++ kthread_stop(pm_ping_timer_thread);
+++
+++ pm_ping_workqueue_fini();
+++
+++ while (atomic_read(&check_xprt_count) != 0)
+++ msleep(SLEEP_INTERVAL);
+++}
+++
+++// module init func
+++int pm_ping_init(void)
+++{
+++ int ret;
+++
+++ atomic_set(&check_xprt_count, 0);
+++ ret = pm_ping_workqueue_init();
+++ if (ret != 0) {
+++ enfs_log_error("PM_PING Module loading failed.\n");
+++ return ret;
+++ }
+++ ret = pm_ping_start();
+++ if (ret != 0) {
+++ enfs_log_error("PM_PING Module loading failed.\n");
+++ pm_ping_workqueue_fini();
+++ return ret;
+++ }
+++
+++ return ret;
+++}
+++
+++bool pm_ping_is_test_xprt_task(struct rpc_task *task)
+++{
+++ return task->tk_ops == &pm_ping_set_status_ops ? true : false;
+++}
+++
+++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt,
+++ void (*func)(void *data),
+++ void *data)
+++{
+++ int ret;
+++
+++ struct pm_ping_async_callback *callback_data =
+++ kzalloc(sizeof(struct pm_ping_async_callback), GFP_KERNEL);
+++
+++ if (callback_data == NULL) {
+++ enfs_log_error("failed to mzalloc mem\n");
+++ return -ENOMEM;
+++ }
+++
+++ callback_data->data = data;
+++ callback_data->func = func;
+++ atomic_inc(&check_xprt_count);
+++ ret = rpc_clnt_test_xprt(clnt, xprt,
+++ &pm_ping_set_status_ops,
+++ callback_data,
+++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
+++
+++ if (ret < 0) {
+++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
+++ atomic_dec(&check_xprt_count);
+++ }
+++
+++ return ret;
+++}
++diff --git a/fs/nfs/enfs/pm_ping.h b/fs/nfs/enfs/pm_ping.h
++new file mode 100644
++index 000000000000..6bcb94bfc836
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_ping.h
++@@ -0,0 +1,33 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs configuration
+++ * Author: x00833432
+++ * Create: 2023-07-27
+++ */
+++
+++#ifndef PM_PING_H
+++#define PM_PING_H
+++
+++#include <linux/sunrpc/clnt.h>
+++
+++enum pm_check_state {
+++ PM_CHECK_INIT, // this xprt never been queued
+++ PM_CHECK_WAITING, // this xprt waiting in the queue
+++ PM_CHECK_CHECKING, // this xprt is testing
+++ PM_CHECK_FINISH, // this xprt has been finished
+++ PM_CHECK_UNDEFINE, // undefine multipath struct
+++};
+++
+++int pm_ping_init(void);
+++void pm_ping_fini(void);
+++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
+++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
+++ enum pm_check_state state);
+++bool pm_ping_is_test_xprt_task(struct rpc_task *task);
+++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt,
+++ void (*func)(void *data),
+++ void *data);
+++
+++#endif // PM_PING_H
++diff --git a/fs/nfs/enfs/pm_state.c b/fs/nfs/enfs/pm_state.c
++new file mode 100644
++index 000000000000..220621a207a2
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_state.c
++@@ -0,0 +1,158 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: path state file
+++ * Author: y00583252
+++ * Create: 2023-08-12
+++ */
+++#include "pm_state.h"
+++#include <linux/sunrpc/xprt.h>
+++
+++#include "enfs.h"
+++#include "enfs_log.h"
+++
+++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++ enum pm_path_state state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return PM_STATE_UNDEFINED;
+++ }
+++
+++ xprt_get(xprt);
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ xprt_put(xprt);
+++ return PM_STATE_UNDEFINED;
+++ }
+++
+++ state = atomic_read(&ctx->path_state);
+++
+++ xprt_put(xprt);
+++
+++ return state;
+++}
+++
+++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++ enum pm_path_state cur_state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ xprt_get(xprt);
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ xprt_put(xprt);
+++ return;
+++ }
+++
+++ cur_state = atomic_read(&ctx->path_state);
+++ if (cur_state == state) {
+++ enfs_log_debug("The xprt is already {%d}.\n", state);
+++ xprt_put(xprt);
+++ return;
+++ }
+++
+++ atomic_set(&ctx->path_state, state);
+++ enfs_log_info("The xprt {%p} path state change from {%d} to {%d}.\n",
+++ xprt, cur_state, state);
+++
+++ xprt_put(xprt);
+++}
+++
+++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len)
+++{
+++ enum pm_path_state state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ if ((buf == NULL) || (len <= 0)) {
+++ enfs_log_error("Buffer is not valid, len=%d.\n", len);
+++ return;
+++ }
+++
+++ state = pm_get_path_state(xprt);
+++
+++ switch (state) {
+++ case PM_STATE_INIT:
+++ (void)snprintf(buf, len, "Init");
+++ break;
+++ case PM_STATE_NORMAL:
+++ (void)snprintf(buf, len, "Normal");
+++ break;
+++ case PM_STATE_FAULT:
+++ (void)snprintf(buf, len, "Fault");
+++ break;
+++ default:
+++ (void)snprintf(buf, len, "Unknown");
+++ break;
+++ }
+++}
+++
+++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len)
+++{
+++ int i;
+++ unsigned long state;
+++ static unsigned long xprt_mask[] = {
+++ XPRT_LOCKED, XPRT_CONNECTED,
+++ XPRT_CONNECTING, XPRT_CLOSE_WAIT,
+++ XPRT_BOUND, XPRT_BINDING, XPRT_CLOSING,
+++ XPRT_CONGESTED};
+++
+++ static const char *const xprt_state_desc[] = {
+++ "LOCKED", "CONNECTED", "CONNECTING",
+++ "CLOSE_WAIT", "BOUND", "BINDING",
+++ "CLOSING", "CONGESTED"};
+++ int pos = 0;
+++ int ret = 0;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ if ((buf == NULL) || (len <= 0)) {
+++ enfs_log_error(
+++ "Xprt state buffer is not valid, len=%d.\n",
+++ len);
+++ return;
+++ }
+++
+++ xprt_get(xprt);
+++ state = READ_ONCE(xprt->state);
+++ xprt_put(xprt);
+++
+++ for (i = 0; i < ARRAY_SIZE(xprt_mask); ++i) {
+++ if (pos >= len)
+++ break;
+++
+++ if (!test_bit(xprt_mask[i], &state))
+++ continue;
+++
+++ if (pos == 0)
+++ ret = snprintf(buf, len, "%s", xprt_state_desc[i]);
+++ else
+++ ret = snprintf(buf + pos, len - pos, "|%s",
+++ xprt_state_desc[i]);
+++
+++ if (ret < 0) {
+++ enfs_log_error("format state failed, ret %d.\n", ret);
+++ break;
+++ }
+++
+++ pos += ret;
+++ }
+++}
++diff --git a/fs/nfs/enfs/pm_state.h b/fs/nfs/enfs/pm_state.h
++new file mode 100644
++index 000000000000..f5f52e5ab91d
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_state.h
++@@ -0,0 +1,28 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: path state header file
+++ * Author: y00583252
+++ * Create: 2023-08-12
+++ */
+++
+++#ifndef PM_STATE_H
+++#define PM_STATE_H
+++
+++#include <linux/types.h>
+++#include <linux/sunrpc/xprt.h>
+++
+++enum pm_path_state {
+++ PM_STATE_INIT,
+++ PM_STATE_NORMAL,
+++ PM_STATE_FAULT,
+++ PM_STATE_UNDEFINED // xprt is not multipath xprt
+++};
+++
+++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state);
+++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt);
+++
+++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len);
+++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len);
+++
+++#endif // PM_STATE_H
+diff --git a/0006-add_enfs_compile_option.patch b/0006-add_enfs_compile_option.patch
+new file mode 100644
+index 0000000..ff3bc0e
+--- /dev/null
++++ b/0006-add_enfs_compile_option.patch
+@@ -0,0 +1,70 @@
++diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig
++index b04256636d4b..ae53510c0627 100644
++--- a/arch/arm64/configs/openeuler_defconfig
+++++ b/arch/arm64/configs/openeuler_defconfig
++@@ -5344,6 +5344,7 @@ CONFIG_LOCKD=m
++ CONFIG_LOCKD_V4=y
++ CONFIG_NFS_ACL_SUPPORT=m
++ CONFIG_NFS_COMMON=y
+++# CONFIG_ENFS is not set
++ CONFIG_SUNRPC=m
++ CONFIG_SUNRPC_GSS=m
++ CONFIG_SUNRPC_BACKCHANNEL=y
++diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig
++index 59baeb2973af..ccc317f7fdb2 100644
++--- a/arch/x86/configs/openeuler_defconfig
+++++ b/arch/x86/configs/openeuler_defconfig
++@@ -6825,6 +6825,7 @@ CONFIG_LOCKD=m
++ CONFIG_LOCKD_V4=y
++ CONFIG_NFS_ACL_SUPPORT=m
++ CONFIG_NFS_COMMON=y
+++# CONFIG_ENFS is not set
++ CONFIG_SUNRPC=m
++ CONFIG_SUNRPC_GSS=m
++ CONFIG_SUNRPC_BACKCHANNEL=y
++diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
++index e55f86713948..872c9b7671b1 100644
++--- a/fs/nfs/Kconfig
+++++ b/fs/nfs/Kconfig
++@@ -196,3 +196,14 @@ config NFS_DEBUG
++ depends on NFS_FS && SUNRPC_DEBUG
++ select CRC32
++ default y
+++
+++config ENFS
+++ tristate "NFS client support for ENFS"
+++ depends on NFS_FS
+++ default n
+++ help
+++ This option enables support multipath of the NFS protocol
+++ in the kernel's NFS client.
+++ This feature will improve performance and reliability.
+++
+++ If sure, say Y.
++diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
++index c587e3c4c6a6..19d0ac2ba3b8 100644
++--- a/fs/nfs/Makefile
+++++ b/fs/nfs/Makefile
++@@ -12,6 +12,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
++ nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
++ nfs-$(CONFIG_SYSCTL) += sysctl.o
++ nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
+++nfs-$(CONFIG_ENFS) += enfs_adapter.o
++
++ obj-$(CONFIG_NFS_V2) += nfsv2.o
++ nfsv2-y := nfs2super.o proc.o nfs2xdr.o
++@@ -34,3 +35,5 @@ nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o
++ obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
++ obj-$(CONFIG_PNFS_BLOCK) += blocklayout/
++ obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/
+++
+++obj-$(CONFIG_ENFS) += enfs/
++diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
++index 090658c3da12..fe4e3b28c5d1 100644
++--- a/net/sunrpc/Makefile
+++++ b/net/sunrpc/Makefile
++@@ -19,3 +19,4 @@ sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
++ sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o
++ sunrpc-$(CONFIG_PROC_FS) += stats.o
++ sunrpc-$(CONFIG_SYSCTL) += sysctl.o
+++sunrpc-$(CONFIG_ENFS) += sunrpc_enfs_adapter.o
+diff --git a/kernel.spec b/kernel.spec
+index 3215446..e242c00 100644
+--- a/kernel.spec
++++ b/kernel.spec
+@@ -60,6 +60,13 @@ Source9002: series.conf
+ Source9998: patches.tar.bz2
+ %endif
+
++Patch0001: 0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
++Patch0002: 0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
++Patch0003: 0003-add_enfs_module.patch
++Patch0004: 0004-add_enfs_module_for_sunrpc_multipatch.patch
++Patch0005: 0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
++Patch0006: 0006-add_enfs_compile_option.patch
++
+ #BuildRequires:
+ BuildRequires: module-init-tools, patch >= 2.5.4, bash >= 2.03, tar
+ BuildRequires: bzip2, xz, findutils, gzip, m4, perl, make >= 3.78, diffutils, gawk
+@@ -256,6 +263,12 @@ Applypatches()
+ Applypatches series.conf %{_builddir}/kernel-%{version}/linux-%{KernelVer}
+ %endif
+
++%patch0001 -p1
++%patch0002 -p1
++%patch0003 -p1
++%patch0004 -p1
++%patch0005 -p1
++%patch0006 -p1
+ touch .scmversion
+
+ find . \( -name "*.orig" -o -name "*~" \) -exec rm -f {} \; >/dev/null
+--
+2.25.0.windows.1
+
diff --git a/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
new file mode 100644
index 0000000..38e57a9
--- /dev/null
+++ b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
@@ -0,0 +1,757 @@
+diff --git a/fs/nfs/client.c b/fs/nfs/client.c
+index 7d02dc52209d..50820a8a684a 100644
+--- a/fs/nfs/client.c
++++ b/fs/nfs/client.c
+@@ -48,7 +48,7 @@
+ #include "callback.h"
+ #include "delegation.h"
+ #include "iostat.h"
+-#include "internal.h"
++#include "enfs_adapter.h"
+ #include "fscache.h"
+ #include "pnfs.h"
+ #include "nfs.h"
+@@ -255,6 +255,7 @@ void nfs_free_client(struct nfs_client *clp)
+ put_nfs_version(clp->cl_nfs_mod);
+ kfree(clp->cl_hostname);
+ kfree(clp->cl_acceptor);
++ nfs_free_multi_path_client(clp);
+ kfree(clp);
+ }
+ EXPORT_SYMBOL_GPL(nfs_free_client);
+@@ -330,6 +331,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
+ sap))
+ continue;
+
++ if (!nfs_multipath_client_match(clp, data))
++ continue;
++
+ refcount_inc(&clp->cl_count);
+ return clp;
+ }
+@@ -512,6 +516,9 @@ int nfs_create_rpc_client(struct nfs_client *clp,
+ .program = &nfs_program,
+ .version = clp->rpc_ops->version,
+ .authflavor = flavor,
++#if IS_ENABLED(CONFIG_ENFS)
++ .multipath_option = cl_init->enfs_option,
++#endif
+ };
+
+ if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
+@@ -634,6 +641,13 @@ struct nfs_client *nfs_init_client(struct nfs_client *clp,
+ /* the client is already initialised */
+ if (clp->cl_cons_state == NFS_CS_READY)
+ return clp;
++ error = nfs_create_multi_path_client(clp, cl_init);
++ if (error < 0) {
++ dprintk("%s: create failed.%d!\n", __func__, error);
++ nfs_put_client(clp);
++ clp = ERR_PTR(error);
++ return clp;
++ }
+
+ /*
+ * Create a client RPC handle for doing FSSTAT with UNIX auth only
+@@ -666,6 +680,9 @@ static int nfs_init_server(struct nfs_server *server,
+ .net = data->net,
+ .timeparms = &timeparms,
+ .init_flags = (1UL << NFS_CS_REUSEPORT),
++#if IS_ENABLED(CONFIG_ENFS)
++ .enfs_option = data->enfs_option,
++#endif
+ };
+ struct nfs_client *clp;
+ int error;
+diff --git a/fs/nfs/enfs_adapter.c b/fs/nfs/enfs_adapter.c
+new file mode 100644
+index 000000000000..7f471f2072c4
+--- /dev/null
++++ b/fs/nfs/enfs_adapter.c
+@@ -0,0 +1,230 @@
++// 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/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 <linux/sunrpc/sched.h>
++#include <linux/nfs_iostat.h>
++#include "enfs_adapter.h"
++#include "iostat.h"
++
++struct enfs_adapter_ops __rcu *enfs_adapter;
++
++int enfs_adapter_register(struct enfs_adapter_ops *ops)
++{
++ struct enfs_adapter_ops *old;
++
++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, NULL, ops);
++ if (old == NULL || old == ops)
++ return 0;
++ pr_err("regist %s ops %p failed. old %p\n", __func__, ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(enfs_adapter_register);
++
++int enfs_adapter_unregister(struct enfs_adapter_ops *ops)
++{
++ struct enfs_adapter_ops *old;
++
++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, ops, NULL);
++ if (old == ops || old == NULL)
++ return 0;
++ pr_err("unregist %s ops %p failed. old %p\n", __func__, ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(enfs_adapter_unregister);
++
++struct enfs_adapter_ops *nfs_multipath_router_get(void)
++{
++ struct enfs_adapter_ops *ops;
++
++ rcu_read_lock();
++ ops = rcu_dereference(enfs_adapter);
++ if (ops == NULL) {
++ rcu_read_unlock();
++ return NULL;
++ }
++ if (!try_module_get(ops->owner))
++ ops = NULL;
++ rcu_read_unlock();
++ return ops;
++}
++
++void nfs_multipath_router_put(struct enfs_adapter_ops *ops)
++{
++ if (ops)
++ module_put(ops->owner);
++}
++
++bool is_valid_option(enum nfsmultipathoptions option)
++{
++ if (option < REMOTEADDR || option >= INVALID_OPTION) {
++ pr_warn("%s: ENFS invalid option %d\n", __func__, option);
++ return false;
++ }
++
++ return true;
++}
++
++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
++ struct nfs_parsed_mount_data *mnt)
++{
++
++ //parseMultiPathOptions(getNfsMultiPathOpt(token), string, mnt);
++
++ int rc;
++ struct enfs_adapter_ops *ops;
++
++ ops = nfs_multipath_router_get();
++ if ((ops == NULL) || (ops->parse_mount_options == NULL) ||
++ !is_valid_option(option)) {
++ nfs_multipath_router_put(ops);
++ dfprintk(MOUNT,
++ "NFS: parsing nfs mount option enfs not load[%s]\n"
++ , __func__);
++ return -EOPNOTSUPP;
++ }
++ // nfs_multipath_parse_options
++ dfprintk(MOUNT, "NFS: parsing nfs mount option '%s' type: %d[%s]\n"
++ , str, option, __func__);
++ rc = ops->parse_mount_options(option, str, &mnt->enfs_option, mnt->net);
++ nfs_multipath_router_put(ops);
++ return rc;
++}
++
++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
++{
++ struct enfs_adapter_ops *ops;
++
++ if (data->enfs_option == NULL)
++ return;
++
++ ops = nfs_multipath_router_get();
++ if ((ops == NULL) || (ops->free_mount_options == NULL)) {
++ nfs_multipath_router_put(ops);
++ return;
++ }
++ ops->free_mount_options((void *)&data->enfs_option);
++ nfs_multipath_router_put(ops);
++}
++
++int nfs_create_multi_path_client(struct nfs_client *client,
++ const struct nfs_client_initdata *cl_init)
++{
++ int ret = 0;
++ struct enfs_adapter_ops *ops;
++
++ if (cl_init->enfs_option == NULL)
++ return 0;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_init != NULL)
++ ret = ops->client_info_init(
++ (void *)&client->cl_multipath_data, cl_init);
++ nfs_multipath_router_put(ops);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(nfs_create_multi_path_client);
++
++void nfs_free_multi_path_client(struct nfs_client *clp)
++{
++ struct enfs_adapter_ops *ops;
++
++ if (clp->cl_multipath_data == NULL)
++ return;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_free != NULL)
++ ops->client_info_free(clp->cl_multipath_data);
++ nfs_multipath_router_put(ops);
++}
++
++int nfs_multipath_client_match(struct nfs_client *clp,
++ const struct nfs_client_initdata *sap)
++{
++ int ret = true;
++ struct enfs_adapter_ops *ops;
++
++ pr_info("%s src %p dst %p\n.", __func__,
++ clp->cl_multipath_data, sap->enfs_option);
++
++ if (clp->cl_multipath_data == NULL && sap->enfs_option == NULL)
++ return true;
++
++ if ((clp->cl_multipath_data == NULL && sap->enfs_option) ||
++ (clp->cl_multipath_data && sap->enfs_option == NULL)) {
++ pr_err("not match client src %p dst %p\n.",
++ clp->cl_multipath_data, sap->enfs_option);
++ return false;
++ }
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_match != NULL)
++ ret = ops->client_info_match(clp->cl_multipath_data,
++ sap->enfs_option);
++ nfs_multipath_router_put(ops);
++
++ return ret;
++}
++
++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst)
++{
++ int ret = true;
++ struct enfs_adapter_ops *ops;
++
++ if (src->cl_multipath_data == NULL && dst->cl_multipath_data == NULL)
++ return true;
++
++ if (src->cl_multipath_data == NULL || dst->cl_multipath_data == NULL)
++ return false;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->nfs4_client_info_match != NULL)
++ ret = ops->nfs4_client_info_match(src->cl_multipath_data,
++ src->cl_multipath_data);
++ nfs_multipath_router_put(ops);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(nfs4_multipath_client_match);
++
++void nfs_multipath_show_client_info(struct seq_file *mount_option,
++ struct nfs_server *server)
++{
++ struct enfs_adapter_ops *ops;
++
++ if (mount_option == NULL || server == NULL ||
++ server->client == NULL ||
++ server->nfs_client->cl_multipath_data == NULL)
++ return;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_show != NULL)
++ ops->client_info_show(mount_option, server);
++ nfs_multipath_router_put(ops);
++}
++
++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option)
++{
++ int ret = 0;
++ struct enfs_adapter_ops *ops;
++
++ if (nfs_client == NULL || nfs_client->cl_rpcclient == NULL)
++ return 0;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->remount_ip_list != NULL)
++ ret = ops->remount_ip_list(nfs_client, enfs_option);
++ nfs_multipath_router_put(ops);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(nfs_remount_iplist);
+diff --git a/fs/nfs/enfs_adapter.h b/fs/nfs/enfs_adapter.h
+new file mode 100644
+index 000000000000..752544e18056
+--- /dev/null
++++ b/fs/nfs/enfs_adapter.h
+@@ -0,0 +1,101 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Client-side ENFS adapt header.
++ *
++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
++ */
++#ifndef _NFS_MULTIPATH_H_
++#define _NFS_MULTIPATH_H_
++
++#include "internal.h"
++
++#if IS_ENABLED(CONFIG_ENFS)
++enum nfsmultipathoptions {
++ REMOTEADDR,
++ LOCALADDR,
++ REMOTEDNSNAME,
++ REMOUNTREMOTEADDR,
++ REMOUNTLOCALADDR,
++ INVALID_OPTION
++};
++
++
++struct enfs_adapter_ops {
++ const char *name;
++ struct module *owner;
++ int (*parse_mount_options)(enum nfsmultipathoptions option,
++ char *str, void **enfs_option, struct net *net_ns);
++
++ void (*free_mount_options)(void **data);
++
++ int (*client_info_init)(void **data,
++ const struct nfs_client_initdata *cl_init);
++ void (*client_info_free)(void *data);
++ int (*client_info_match)(void *src, void *dst);
++ int (*nfs4_client_info_match)(void *src, void *dst);
++ void (*client_info_show)(struct seq_file *mount_option, void *data);
++ int (*remount_ip_list)(struct nfs_client *nfs_client,
++ void *enfs_option);
++};
++
++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
++ struct nfs_parsed_mount_data *mnt);
++void enfs_free_mount_options(struct nfs_parsed_mount_data *data);
++int nfs_create_multi_path_client(struct nfs_client *client,
++ const struct nfs_client_initdata *cl_init);
++void nfs_free_multi_path_client(struct nfs_client *clp);
++int nfs_multipath_client_match(struct nfs_client *clp,
++ const struct nfs_client_initdata *sap);
++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst);
++void nfs_multipath_show_client_info(struct seq_file *mount_option,
++ struct nfs_server *server);
++int enfs_adapter_register(struct enfs_adapter_ops *ops);
++int enfs_adapter_unregister(struct enfs_adapter_ops *ops);
++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option);
++int nfs4_create_multi_path(struct nfs_server *server,
++ struct nfs_parsed_mount_data *data,
++ const struct rpc_timeout *timeparms);
++
++#else
++static inline
++void nfs_free_multi_path_client(struct nfs_client *clp)
++{
++
++}
++
++static inline
++int nfs_multipath_client_match(struct nfs_client *clp,
++ const struct nfs_client_initdata *sap)
++{
++ return 1;
++}
++
++static inline
++int nfs_create_multi_path_client(struct nfs_client *client,
++ const struct nfs_client_initdata *cl_init)
++{
++ return 0;
++}
++
++static inline
++void nfs_multipath_show_client_info(struct seq_file *mount_option,
++ struct nfs_server *server)
++{
++
++}
++
++static inline
++int nfs4_multipath_client_match(struct nfs_client *src,
++ struct nfs_client *dst)
++{
++ return 1;
++}
++
++static inline
++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
++{
++
++}
++
++#endif // CONFIG_ENFS
++#endif // _NFS_MULTIPATH_H_
+diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
+index 0ce5a90640c4..c696693edc7b 100644
+--- a/fs/nfs/internal.h
++++ b/fs/nfs/internal.h
+@@ -93,6 +93,9 @@ struct nfs_client_initdata {
+ u32 minorversion;
+ struct net *net;
+ const struct rpc_timeout *timeparms;
++#if IS_ENABLED(CONFIG_ENFS)
++ void *enfs_option; /* struct multipath_mount_options * */
++#endif
+ };
+
+ /*
+@@ -135,6 +138,9 @@ struct nfs_parsed_mount_data {
+
+ struct security_mnt_opts lsm_opts;
+ struct net *net;
++#if IS_ENABLED(CONFIG_ENFS)
++ void *enfs_option; /* struct multipath_mount_options * */
++#endif
+ };
+
+ /* mount_clnt.c */
+diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
+index 1350ea673672..4aa6e1f961f7 100644
+--- a/fs/nfs/nfs4client.c
++++ b/fs/nfs/nfs4client.c
+@@ -10,7 +10,7 @@
+ #include <linux/sunrpc/xprt.h>
+ #include <linux/sunrpc/bc_xprt.h>
+ #include <linux/sunrpc/rpc_pipe_fs.h>
+-#include "internal.h"
++#include "enfs_adapter.h"
+ #include "callback.h"
+ #include "delegation.h"
+ #include "nfs4session.h"
+@@ -225,6 +225,16 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
+ __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
+ __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
+
++#if IS_ENABLED(CONFIG_ENFS)
++ err = nfs_create_multi_path_client(clp, cl_init);
++ if (err < 0) {
++ dprintk("%s: create failed.%d\n", __func__, err);
++ nfs_put_client(clp);
++ clp = ERR_PTR(err);
++ return clp;
++ }
++#endif
++
+ /*
+ * Set up the connection to the server before we add add to the
+ * global list.
+@@ -529,6 +539,9 @@ static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new,
+ if (!nfs4_match_client_owner_id(pos, new))
+ return 1;
+
++ if (!nfs4_multipath_client_match(pos, new))
++ return 1;
++
+ return 0;
+ }
+
+@@ -860,7 +873,7 @@ static int nfs4_set_client(struct nfs_server *server,
+ const size_t addrlen,
+ const char *ip_addr,
+ int proto, const struct rpc_timeout *timeparms,
+- u32 minorversion, struct net *net)
++ u32 minorversion, struct net *net, void *enfs_option)
+ {
+ struct nfs_client_initdata cl_init = {
+ .hostname = hostname,
+@@ -872,6 +885,9 @@ static int nfs4_set_client(struct nfs_server *server,
+ .minorversion = minorversion,
+ .net = net,
+ .timeparms = timeparms,
++#if IS_ENABLED(CONFIG_ENFS)
++ .enfs_option = enfs_option,
++#endif
+ };
+ struct nfs_client *clp;
+
+@@ -1042,6 +1058,30 @@ static int nfs4_server_common_setup(struct nfs_server *server,
+ return error;
+ }
+
++int nfs4_create_multi_path(struct nfs_server *server,
++ struct nfs_parsed_mount_data *data,
++ const struct rpc_timeout *timeparms)
++{
++ struct nfs_client_initdata cl_init = {
++ .hostname = data->nfs_server.hostname,
++ .addr = (const struct sockaddr *)&data->nfs_server.address,
++ .addrlen = data->nfs_server.addrlen,
++ .ip_addr = data->client_address,
++ .nfs_mod = &nfs_v4,
++ .proto = data->nfs_server.protocol,
++ .minorversion = data->minorversion,
++ .net = data->net,
++ .timeparms = timeparms,
++#if IS_ENABLED(CONFIG_ENFS)
++ .enfs_option = data->enfs_option,
++#endif // CONFIG_ENFS
++ };
++
++ return nfs_create_multi_path_client(server->nfs_client, &cl_init);
++
++}
++EXPORT_SYMBOL_GPL(nfs4_create_multi_path);
++
+ /*
+ * Create a version 4 volume record
+ */
+@@ -1050,6 +1090,7 @@ static int nfs4_init_server(struct nfs_server *server,
+ {
+ struct rpc_timeout timeparms;
+ int error;
++ void *enfs_option = NULL;
+
+ nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
+ data->timeo, data->retrans);
+@@ -1067,6 +1108,10 @@ static int nfs4_init_server(struct nfs_server *server,
+ else
+ data->selected_flavor = RPC_AUTH_UNIX;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ enfs_option = data->enfs_option;
++#endif
++
+ /* Get a client record */
+ error = nfs4_set_client(server,
+ data->nfs_server.hostname,
+@@ -1076,7 +1121,7 @@ static int nfs4_init_server(struct nfs_server *server,
+ data->nfs_server.protocol,
+ &timeparms,
+ data->minorversion,
+- data->net);
++ data->net, enfs_option);
+ if (error < 0)
+ return error;
+
+@@ -1161,7 +1206,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+ XPRT_TRANSPORT_RDMA,
+ parent_server->client->cl_timeout,
+ parent_client->cl_mvops->minor_version,
+- parent_client->cl_net);
++ parent_client->cl_net, NULL);
+ if (!error)
+ goto init_server;
+ #endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
+@@ -1174,7 +1219,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+ XPRT_TRANSPORT_TCP,
+ parent_server->client->cl_timeout,
+ parent_client->cl_mvops->minor_version,
+- parent_client->cl_net);
++ parent_client->cl_net, NULL);
+ if (error < 0)
+ goto error;
+
+@@ -1269,7 +1314,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
+ set_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
+ error = nfs4_set_client(server, hostname, sap, salen, buf,
+ clp->cl_proto, clnt->cl_timeout,
+- clp->cl_minorversion, net);
++ clp->cl_minorversion, net, NULL);
+ clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
+ if (error != 0) {
+ nfs_server_insert_lists(server);
+diff --git a/fs/nfs/super.c b/fs/nfs/super.c
+index a05e1eb2c3fd..83cd294aca15 100644
+--- a/fs/nfs/super.c
++++ b/fs/nfs/super.c
+@@ -61,7 +61,7 @@
+ #include "callback.h"
+ #include "delegation.h"
+ #include "iostat.h"
+-#include "internal.h"
++#include "enfs_adapter.h"
+ #include "fscache.h"
+ #include "nfs4session.h"
+ #include "pnfs.h"
+@@ -113,6 +113,12 @@ enum {
+
+ /* Special mount options */
+ Opt_userspace, Opt_deprecated, Opt_sloppy,
++#if IS_ENABLED(CONFIG_ENFS)
++ Opt_remote_iplist,
++ Opt_local_iplist,
++ Opt_remote_dnslist,
++ Opt_enfs_info,
++#endif
+
+ Opt_err
+ };
+@@ -183,6 +189,13 @@ static const match_table_t nfs_mount_option_tokens = {
+ { Opt_fscache_uniq, "fsc=%s" },
+ { Opt_local_lock, "local_lock=%s" },
+
++#if IS_ENABLED(CONFIG_ENFS)
++ { Opt_remote_iplist, "remoteaddrs=%s" },
++ { Opt_local_iplist, "localaddrs=%s" },
++ { Opt_remote_dnslist, "remotednsname=%s" },
++ { Opt_enfs_info, "enfs_info=%s" },
++#endif
++
+ /* The following needs to be listed after all other options */
+ { Opt_nfsvers, "v%s" },
+
+@@ -365,6 +378,21 @@ static struct shrinker acl_shrinker = {
+ .seeks = DEFAULT_SEEKS,
+ };
+
++#if IS_ENABLED(CONFIG_ENFS)
++enum nfsmultipathoptions getNfsMultiPathOpt(int token)
++{
++ switch (token) {
++ case Opt_remote_iplist:
++ return REMOUNTREMOTEADDR;
++ case Opt_local_iplist:
++ return REMOUNTLOCALADDR;
++ case Opt_remote_dnslist:
++ return REMOTEDNSNAME;
++ }
++ return INVALID_OPTION;
++}
++#endif
++
+ /*
+ * Register the NFS filesystems
+ */
+@@ -758,6 +786,9 @@ int nfs_show_options(struct seq_file *m, struct dentry *root)
+ seq_printf(m, ",addr=%s",
+ rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient,
+ RPC_DISPLAY_ADDR));
++
++ nfs_multipath_show_client_info(m, nfss);
++
+ rcu_read_unlock();
+
+ return 0;
+@@ -853,6 +884,8 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
+ seq_puts(m, root->d_sb->s_flags & SB_NODIRATIME ? ",nodiratime" : "");
+ nfs_show_mount_options(m, nfss, 1);
+
++ nfs_multipath_show_client_info(m, nfss);
++
+ seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
+
+ show_implementation_id(m, nfss);
+@@ -977,6 +1010,7 @@ static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data)
+ kfree(data->nfs_server.export_path);
+ kfree(data->nfs_server.hostname);
+ kfree(data->fscache_uniq);
++ enfs_free_mount_options(data);
+ security_free_mnt_opts(&data->lsm_opts);
+ kfree(data);
+ }
+@@ -1641,7 +1675,34 @@ static int nfs_parse_mount_options(char *raw,
+ return 0;
+ };
+ break;
+-
++#if IS_ENABLED(CONFIG_ENFS)
++ case Opt_remote_iplist:
++ case Opt_local_iplist:
++ case Opt_remote_dnslist:
++ string = match_strdup(args);
++ if (string == NULL)
++ goto out_nomem;
++ rc = enfs_parse_mount_options(getNfsMultiPathOpt(token),
++ string, mnt);
++ kfree(string);
++ switch (rc) {
++ case 0:
++ break;
++ case -ENOMEM:
++ goto out_nomem;
++ case -ENOSPC:
++ goto out_limit;
++ case -EINVAL:
++ goto out_invalid_address;
++ case -ENOTSUPP:
++ goto out_invalid_address;
++ case -EOPNOTSUPP:
++ goto out_invalid_address;
++ }
++ break;
++ case Opt_enfs_info:
++ break;
++#endif
+ /*
+ * Special options
+ */
+@@ -1720,6 +1781,11 @@ static int nfs_parse_mount_options(char *raw,
+ free_secdata(secdata);
+ printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
+ return 0;
++#if IS_ENABLED(CONFIG_ENFS)
++out_limit:
++ dprintk("NFS: param is more than supported limit: %d\n", rc);
++ return 0;
++#endif
+ }
+
+ /*
+@@ -2335,6 +2401,14 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
+ if (!nfs_parse_mount_options((char *)options, data))
+ goto out;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ if (data->enfs_option) {
++ error = nfs_remount_iplist(nfss->nfs_client, data->enfs_option);
++ if (error)
++ goto out;
++ }
++#endif
++
+ /*
+ * noac is a special case. It implies -o sync, but that's not
+ * necessarily reflected in the mtab options. do_remount_sb
+@@ -2347,6 +2421,11 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
+ /* compare new mount options with old ones */
+ error = nfs_compare_remount_data(nfss, data);
+ out:
++#if IS_ENABLED(CONFIG_ENFS)
++ /* release remount option member */
++ if (data->enfs_option)
++ enfs_free_mount_options(data);
++#endif
+ nfs_free_parsed_mount_data(data);
+ return error;
+ }
+diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
+index 7023ae64e3d7..2c19678afe8d 100644
+--- a/include/linux/nfs_fs_sb.h
++++ b/include/linux/nfs_fs_sb.h
+@@ -123,6 +123,11 @@ struct nfs_client {
+
+ struct net *cl_net;
+ struct list_head pending_cb_stateids;
++
++#if IS_ENABLED(CONFIG_ENFS)
++ /* multi path private structure (struct multipath_client_info *) */
++ void *cl_multipath_data;
++#endif
+ };
+
+ /*
diff --git a/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
new file mode 100644
index 0000000..540a2ce
--- /dev/null
+++ b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
@@ -0,0 +1,805 @@
+diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
+index 8aa865bce4f6..89178f78de8c 100644
+--- a/include/linux/sunrpc/clnt.h
++++ b/include/linux/sunrpc/clnt.h
+@@ -70,6 +70,10 @@ struct rpc_clnt {
+ struct dentry *cl_debugfs; /* debugfs directory */
+ #endif
+ struct rpc_xprt_iter cl_xpi;
++
++#if IS_ENABLED(CONFIG_ENFS)
++ bool cl_enfs;
++#endif
+ };
+
+ /*
+@@ -124,6 +128,9 @@ struct rpc_create_args {
+ unsigned long flags;
+ char *client_name;
+ struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */
++#if IS_ENABLED(CONFIG_ENFS)
++ void *multipath_option;
++#endif
+ };
+
+ struct rpc_add_xprt_test {
+@@ -221,6 +228,12 @@ bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+ const struct sockaddr *sap);
+ void rpc_cleanup_clids(void);
+
++#if IS_ENABLED(CONFIG_ENFS)
++int
++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
++ const struct rpc_call_ops *ops, void *data, int flags);
++#endif /* CONFIG_ENFS */
++
+ static inline int rpc_reply_expected(struct rpc_task *task)
+ {
+ return (task->tk_msg.rpc_proc != NULL) &&
+diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
+index ad2e243f3f03..124f5a0faf3e 100644
+--- a/include/linux/sunrpc/sched.h
++++ b/include/linux/sunrpc/sched.h
+@@ -90,6 +90,9 @@ struct rpc_task {
+ tk_garb_retry : 2,
+ tk_cred_retry : 2,
+ tk_rebind_retry : 2;
++#if IS_ENABLED(CONFIG_ENFS)
++ unsigned long tk_major_timeo; /* major timeout ticks */
++#endif
+ };
+
+ typedef void (*rpc_action)(struct rpc_task *);
+@@ -118,6 +121,9 @@ struct rpc_task_setup {
+ */
+ #define RPC_TASK_ASYNC 0x0001 /* is an async task */
+ #define RPC_TASK_SWAPPER 0x0002 /* is swapping in/out */
++#if IS_ENABLED(CONFIG_ENFS)
++#define RPC_TASK_FIXED 0x0004 /* detect xprt status task */
++#endif
+ #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */
+ #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */
+ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
+@@ -257,6 +263,9 @@ void rpc_destroy_mempool(void);
+ extern struct workqueue_struct *rpciod_workqueue;
+ extern struct workqueue_struct *xprtiod_workqueue;
+ void rpc_prepare_task(struct rpc_task *task);
++#if IS_ENABLED(CONFIG_ENFS)
++void rpc_init_task_retry_counters(struct rpc_task *task);
++#endif
+
+ static inline int rpc_wait_for_completion_task(struct rpc_task *task)
+ {
+diff --git a/include/linux/sunrpc/sunrpc_enfs_adapter.h b/include/linux/sunrpc/sunrpc_enfs_adapter.h
+new file mode 100644
+index 000000000000..28abedcf5cf6
+--- /dev/null
++++ b/include/linux/sunrpc/sunrpc_enfs_adapter.h
+@@ -0,0 +1,128 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Client-side SUNRPC ENFS adapter header.
++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
++ */
++#ifndef _SUNRPC_ENFS_ADAPTER_H_
++#define _SUNRPC_ENFS_ADAPTER_H_
++#include <linux/sunrpc/clnt.h>
++
++#if IS_ENABLED(CONFIG_ENFS)
++
++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
++{
++ xps->xps_nactive--;
++}
++
++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
++{
++ xps->xps_nactive--;
++}
++
++struct rpc_xprt *rpc_task_get_xprt
++(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
++
++struct rpc_multipath_ops {
++ struct module *owner;
++ void (*create_clnt)(struct rpc_create_args *args,
++ struct rpc_clnt *clnt);
++ void (*releas_clnt)(struct rpc_clnt *clnt);
++ void (*create_xprt)(struct rpc_xprt *xprt);
++ void (*destroy_xprt)(struct rpc_xprt *xprt);
++ void (*xprt_iostat)(struct rpc_task *task);
++ void (*failover_handle)(struct rpc_task *task);
++ bool (*task_need_call_start_again)(struct rpc_task *task);
++ void (*adjust_task_timeout)(struct rpc_task *task, void *condition);
++ void (*init_task_req)(struct rpc_task *task, struct rpc_rqst *req);
++ bool (*prepare_transmit)(struct rpc_task *task);
++};
++
++extern struct rpc_multipath_ops __rcu *multipath_ops;
++void rpc_init_task_retry_counters(struct rpc_task *task);
++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops);
++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops);
++struct rpc_multipath_ops *rpc_multipath_ops_get(void);
++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops);
++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
++ struct rpc_clnt *clnt);
++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt);
++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt);
++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt);
++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task);
++void rpc_multipath_ops_failover_handle(struct rpc_task *task);
++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task);
++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
++ void *condition);
++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
++ struct rpc_rqst *req);
++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task);
++
++#else
++static inline struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt)
++{
++ return NULL;
++}
++
++static inline void rpc_task_release_xprt(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt)
++{
++}
++
++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
++{
++}
++
++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
++{
++}
++
++static inline void rpc_multipath_ops_create_clnt
++(struct rpc_create_args *args, struct rpc_clnt *clnt)
++{
++}
++
++static inline void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
++{
++}
++
++static inline bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
++{
++ return false;
++}
++
++static inline void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
++{
++}
++
++static inline void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
++{
++}
++
++static inline void rpc_multipath_ops_failover_handle(struct rpc_task *task)
++{
++}
++
++static inline
++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
++{
++ return false;
++}
++
++static inline void
++rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task, void *condition)
++{
++}
++
++static inline void
++rpc_multipath_ops_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
++{
++}
++
++static inline bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
++{
++ return false;
++}
++
++#endif
++#endif // _SUNRPC_ENFS_ADAPTER_H_
+diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
+index ccfacca1eba9..2e47b3577947 100644
+--- a/include/linux/sunrpc/xprt.h
++++ b/include/linux/sunrpc/xprt.h
+@@ -279,6 +279,10 @@ struct rpc_xprt {
+ atomic_t inject_disconnect;
+ #endif
+ struct rcu_head rcu;
++#if IS_ENABLED(CONFIG_ENFS)
++ atomic_long_t queuelen;
++ void *multipath_context;
++#endif
+ };
+
+ #if defined(CONFIG_SUNRPC_BACKCHANNEL)
+diff --git a/include/linux/sunrpc/xprtmultipath.h b/include/linux/sunrpc/xprtmultipath.h
+index af1257c030d2..d54e4dbbbf34 100644
+--- a/include/linux/sunrpc/xprtmultipath.h
++++ b/include/linux/sunrpc/xprtmultipath.h
+@@ -22,6 +22,10 @@ struct rpc_xprt_switch {
+ const struct rpc_xprt_iter_ops *xps_iter_ops;
+
+ struct rcu_head xps_rcu;
++#if IS_ENABLED(CONFIG_ENFS)
++ unsigned int xps_nactive;
++ atomic_long_t xps_queuelen;
++#endif
+ };
+
+ struct rpc_xprt_iter {
+@@ -69,4 +73,8 @@ extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
+
+ extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
+ const struct sockaddr *sap);
++#if IS_ENABLED(CONFIG_ENFS)
++extern void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++ struct rpc_xprt *xprt);
++#endif
+ #endif
+diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
+index 0fc540b0d183..d7ffee637148 100644
+--- a/net/sunrpc/clnt.c
++++ b/net/sunrpc/clnt.c
+@@ -37,6 +37,7 @@
+ #include <linux/sunrpc/rpc_pipe_fs.h>
+ #include <linux/sunrpc/metrics.h>
+ #include <linux/sunrpc/bc_xprt.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+ #include <trace/events/sunrpc.h>
+
+ #include "sunrpc.h"
+@@ -490,6 +491,8 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
+ }
+ }
+
++ rpc_multipath_ops_create_clnt(args, clnt);
++
+ clnt->cl_softrtry = 1;
+ if (args->flags & RPC_CLNT_CREATE_HARDRTRY)
+ clnt->cl_softrtry = 0;
+@@ -869,6 +872,8 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
+ list_empty(&clnt->cl_tasks), 1*HZ);
+ }
+
++ rpc_multipath_ops_releas_clnt(clnt);
++
+ rpc_release_client(clnt);
+ }
+ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
+@@ -981,7 +986,13 @@ void rpc_task_release_transport(struct rpc_task *task)
+
+ if (xprt) {
+ task->tk_xprt = NULL;
+- xprt_put(xprt);
++#if IS_ENABLED(CONFIG_ENFS)
++ if (task->tk_client) {
++ rpc_task_release_xprt(task->tk_client, xprt);
++ return;
++ }
++#endif
++ xprt_put(xprt);
+ }
+ }
+ EXPORT_SYMBOL_GPL(rpc_task_release_transport);
+@@ -990,6 +1001,10 @@ void rpc_task_release_client(struct rpc_task *task)
+ {
+ struct rpc_clnt *clnt = task->tk_client;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ rpc_task_release_transport(task);
++#endif
++
+ if (clnt != NULL) {
+ /* Remove from client task list */
+ spin_lock(&clnt->cl_lock);
+@@ -999,14 +1014,29 @@ void rpc_task_release_client(struct rpc_task *task)
+
+ rpc_release_client(clnt);
+ }
++#if IS_ENABLED(CONFIG_ENFS)
++#else
+ rpc_task_release_transport(task);
++#endif
+ }
+
++#if IS_ENABLED(CONFIG_ENFS)
++static struct rpc_xprt *
++rpc_task_get_next_xprt(struct rpc_clnt *clnt)
++{
++ return rpc_task_get_xprt(clnt, xprt_iter_get_next(&clnt->cl_xpi));
++}
++#endif
++
+ static
+ void rpc_task_set_transport(struct rpc_task *task, struct rpc_clnt *clnt)
+ {
+ if (!task->tk_xprt)
++#if IS_ENABLED(CONFIG_ENFS)
++ task->tk_xprt = rpc_task_get_next_xprt(clnt);
++#else
+ task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
++#endif
+ }
+
+ static
+@@ -1597,6 +1627,14 @@ call_reserveresult(struct rpc_task *task)
+ return;
+ case -EIO: /* probably a shutdown */
+ break;
++#if IS_ENABLED(CONFIG_ENFS)
++ case -ETIMEDOUT: /* woken up; restart */
++ if (rpc_multipath_ops_task_need_call_start_again(task)) {
++ rpc_task_release_transport(task);
++ task->tk_action = call_start;
++ return;
++ }
++#endif
+ default:
+ printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
+ __func__, status);
+@@ -1962,6 +2000,10 @@ call_transmit(struct rpc_task *task)
+ return;
+ if (!xprt_prepare_transmit(task))
+ return;
++
++ if (rpc_multipath_ops_prepare_transmit(task))
++ return;
++
+ task->tk_action = call_transmit_status;
+ /* Encode here so that rpcsec_gss can use correct sequence number. */
+ if (rpc_task_need_encode(task)) {
+@@ -2277,6 +2319,9 @@ call_timeout(struct rpc_task *task)
+
+ retry:
+ task->tk_action = call_bind;
++#if IS_ENABLED(CONFIG_ENFS)
++ rpc_multipath_ops_failover_handle(task);
++#endif
+ task->tk_status = 0;
+ }
+
+@@ -2961,3 +3006,30 @@ rpc_clnt_swap_deactivate(struct rpc_clnt *clnt)
+ }
+ EXPORT_SYMBOL_GPL(rpc_clnt_swap_deactivate);
+ #endif /* CONFIG_SUNRPC_SWAP */
++
++#if IS_ENABLED(CONFIG_ENFS)
++/* rpc_clnt_test_xprt - Test and add a new transport to a rpc_clnt
++ * @clnt: pointer to struct rpc_clnt
++ * @xprt: pointer struct rpc_xprt
++ * @ops: async operation
++ */
++int
++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
++ const struct rpc_call_ops *ops, void *data, int flags)
++{
++ struct rpc_cred *cred;
++ struct rpc_task *task;
++
++ cred = authnull_ops.lookup_cred(NULL, NULL, 0);
++ task = rpc_call_null_helper(clnt, xprt, cred,
++ RPC_TASK_SOFT | RPC_TASK_SOFTCONN | flags,
++ ops, data);
++ put_rpccred(cred);
++ if (IS_ERR(task))
++ return PTR_ERR(task);
++
++ rpc_put_task(task);
++ return 1;
++}
++EXPORT_SYMBOL_GPL(rpc_clnt_test_xprt);
++#endif
+diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
+index a873c92a4898..2254fea0e863 100644
+--- a/net/sunrpc/sched.c
++++ b/net/sunrpc/sched.c
+@@ -20,7 +20,7 @@
+ #include <linux/mutex.h>
+ #include <linux/freezer.h>
+
+-#include <linux/sunrpc/clnt.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+
+ #include "sunrpc.h"
+
+@@ -962,7 +962,12 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
+ /* Initialize workqueue for async tasks */
+ task->tk_workqueue = task_setup_data->workqueue;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ task->tk_xprt = rpc_task_get_xprt(task_setup_data->rpc_client,
++ xprt_get(task_setup_data->rpc_xprt));
++#else
+ task->tk_xprt = xprt_get(task_setup_data->rpc_xprt);
++#endif
+
+ if (task->tk_ops->rpc_call_prepare != NULL)
+ task->tk_action = rpc_prepare_task;
+diff --git a/net/sunrpc/sunrpc_enfs_adapter.c b/net/sunrpc/sunrpc_enfs_adapter.c
+new file mode 100644
+index 000000000000..c1543545c6de
+--- /dev/null
++++ b/net/sunrpc/sunrpc_enfs_adapter.c
+@@ -0,0 +1,214 @@
++// SPDX-License-Identifier: GPL-2.0
++/* Client-side SUNRPC ENFS adapter header.
++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
++ */
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++struct rpc_multipath_ops __rcu *multipath_ops;
++
++void rpc_init_task_retry_counters(struct rpc_task *task)
++{
++ /* Initialize retry counters */
++ task->tk_garb_retry = 2;
++ task->tk_cred_retry = 2;
++ task->tk_rebind_retry = 2;
++}
++EXPORT_SYMBOL_GPL(rpc_init_task_retry_counters);
++
++struct rpc_xprt *
++rpc_task_get_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
++{
++ struct rpc_xprt_switch *xps;
++
++ if (!xprt)
++ return NULL;
++ rcu_read_lock();
++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
++ atomic_long_inc(&xps->xps_queuelen);
++ rcu_read_unlock();
++ atomic_long_inc(&xprt->queuelen);
++
++ return xprt;
++}
++
++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops)
++{
++ struct rpc_multipath_ops *old;
++
++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, NULL, ops);
++ if (!old || old == ops)
++ return 0;
++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_register);
++
++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops)
++{
++ struct rpc_multipath_ops *old;
++
++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, ops, NULL);
++ if (!old || old == ops)
++ return 0;
++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_unregister);
++
++struct rpc_multipath_ops *rpc_multipath_ops_get(void)
++{
++ struct rpc_multipath_ops *ops;
++
++ rcu_read_lock();
++ ops = rcu_dereference(multipath_ops);
++ if (!ops) {
++ rcu_read_unlock();
++ return NULL;
++ }
++ if (!try_module_get(ops->owner))
++ ops = NULL;
++ rcu_read_unlock();
++ return ops;
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_get);
++
++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops)
++{
++ if (ops)
++ module_put(ops->owner);
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_put);
++
++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
++{
++ struct rpc_xprt_switch *xps;
++
++ atomic_long_dec(&xprt->queuelen);
++ rcu_read_lock();
++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
++ atomic_long_dec(&xps->xps_queuelen);
++ rcu_read_unlock();
++
++ xprt_put(xprt);
++}
++
++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
++ struct rpc_clnt *clnt)
++{
++ struct rpc_multipath_ops *mops;
++
++ if (args->multipath_option) {
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->create_clnt)
++ mops->create_clnt(args, clnt);
++ rpc_multipath_ops_put(mops);
++ }
++}
++
++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
++{
++ struct rpc_multipath_ops *mops;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->releas_clnt)
++ mops->releas_clnt(clnt);
++
++ rpc_multipath_ops_put(mops);
++}
++
++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->create_xprt) {
++ mops->create_xprt(xprt);
++ if (!xprt->multipath_context) {
++ rpc_multipath_ops_put(mops);
++ return true;
++ }
++ }
++ rpc_multipath_ops_put(mops);
++ return false;
++}
++
++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
++{
++ struct rpc_multipath_ops *mops;
++
++ if (xprt->multipath_context) {
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->destroy_xprt)
++ mops->destroy_xprt(xprt);
++ rpc_multipath_ops_put(mops);
++ }
++}
++
++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mops;
++
++ mops = rpc_multipath_ops_get();
++ if (task->tk_client && mops && mops->xprt_iostat)
++ mops->xprt_iostat(task);
++ rpc_multipath_ops_put(mops);
++}
++
++void rpc_multipath_ops_failover_handle(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mpath_ops = NULL;
++
++ mpath_ops = rpc_multipath_ops_get();
++ if (mpath_ops && mpath_ops->failover_handle)
++ mpath_ops->failover_handle(task);
++ rpc_multipath_ops_put(mpath_ops);
++}
++
++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mpath_ops = NULL;
++ bool ret = false;
++
++ mpath_ops = rpc_multipath_ops_get();
++ if (mpath_ops && mpath_ops->task_need_call_start_again)
++ ret = mpath_ops->task_need_call_start_again(task);
++ rpc_multipath_ops_put(mpath_ops);
++ return ret;
++}
++
++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
++ void *condition)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->adjust_task_timeout)
++ mops->adjust_task_timeout(task, NULL);
++ rpc_multipath_ops_put(mops);
++}
++
++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
++ struct rpc_rqst *req)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->init_task_req)
++ mops->init_task_req(task, req);
++ rpc_multipath_ops_put(mops);
++}
++
++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->prepare_transmit) {
++ if (!(mops->prepare_transmit(task))) {
++ rpc_multipath_ops_put(mops);
++ return true;
++ }
++ }
++ rpc_multipath_ops_put(mops);
++ return false;
++}
+diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
+index c912bf20faa2..c2b63b3d5217 100644
+--- a/net/sunrpc/xprt.c
++++ b/net/sunrpc/xprt.c
+@@ -48,6 +48,7 @@
+ #include <linux/sunrpc/clnt.h>
+ #include <linux/sunrpc/metrics.h>
+ #include <linux/sunrpc/bc_xprt.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+ #include <linux/rcupdate.h>
+
+ #include <trace/events/sunrpc.h>
+@@ -259,6 +260,9 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
+ dprintk("RPC: %5u failed to lock transport %p\n",
+ task->tk_pid, xprt);
+ task->tk_timeout = 0;
++
++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
++
+ task->tk_status = -EAGAIN;
+ if (req == NULL)
+ priority = RPC_PRIORITY_LOW;
+@@ -560,6 +564,9 @@ void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action)
+ struct rpc_xprt *xprt = req->rq_xprt;
+
+ task->tk_timeout = RPC_IS_SOFT(task) ? req->rq_timeout : 0;
++
++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
++
+ rpc_sleep_on(&xprt->pending, task, action);
+ }
+ EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space);
+@@ -1347,6 +1354,9 @@ xprt_request_init(struct rpc_task *task)
+ req->rq_rcv_buf.buflen = 0;
+ req->rq_release_snd_buf = NULL;
+ xprt_reset_majortimeo(req);
++
++ rpc_multipath_ops_init_task_req(task, req);
++
+ dprintk("RPC: %5u reserved req %p xid %08x\n", task->tk_pid,
+ req, ntohl(req->rq_xid));
+ }
+@@ -1427,6 +1437,9 @@ void xprt_release(struct rpc_task *task)
+ task->tk_ops->rpc_count_stats(task, task->tk_calldata);
+ else if (task->tk_client)
+ rpc_count_iostats(task, task->tk_client->cl_metrics);
++
++ rpc_multipath_ops_xprt_iostat(task);
++
+ spin_lock(&xprt->recv_lock);
+ if (!list_empty(&req->rq_list)) {
+ list_del_init(&req->rq_list);
+@@ -1455,6 +1468,7 @@ void xprt_release(struct rpc_task *task)
+ else
+ xprt_free_bc_request(req);
+ }
++EXPORT_SYMBOL_GPL(xprt_release);
+
+ static void xprt_init(struct rpc_xprt *xprt, struct net *net)
+ {
+@@ -1528,6 +1542,10 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
+ return ERR_PTR(-ENOMEM);
+ }
+
++if (rpc_multipath_ops_create_xprt(xprt)) {
++ xprt_destroy(xprt);
++ return ERR_PTR(-ENOMEM);
++}
+ rpc_xprt_debugfs_register(xprt);
+
+ dprintk("RPC: created transport %p with %u slots\n", xprt,
+@@ -1547,6 +1565,9 @@ static void xprt_destroy_cb(struct work_struct *work)
+ rpc_destroy_wait_queue(&xprt->sending);
+ rpc_destroy_wait_queue(&xprt->backlog);
+ kfree(xprt->servername);
++
++ rpc_multipath_ops_destroy_xprt(xprt);
++
+ /*
+ * Tear down transport state and free the rpc_xprt
+ */
+diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c
+index 6ebaa58b4eff..6202a0be1327 100644
+--- a/net/sunrpc/xprtmultipath.c
++++ b/net/sunrpc/xprtmultipath.c
+@@ -18,6 +18,7 @@
+ #include <linux/sunrpc/xprt.h>
+ #include <linux/sunrpc/addr.h>
+ #include <linux/sunrpc/xprtmultipath.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+
+ typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct list_head *head,
+ const struct rpc_xprt *cur);
+@@ -26,8 +27,8 @@ static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular;
+ static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin;
+ static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall;
+
+-static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+- struct rpc_xprt *xprt)
++void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++ struct rpc_xprt *xprt)
+ {
+ if (unlikely(xprt_get(xprt) == NULL))
+ return;
+@@ -36,7 +37,9 @@ static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+ if (xps->xps_nxprts == 0)
+ xps->xps_net = xprt->xprt_net;
+ xps->xps_nxprts++;
++ rpc_xps_nactive_add_one(xps);
+ }
++EXPORT_SYMBOL(xprt_switch_add_xprt_locked);
+
+ /**
+ * rpc_xprt_switch_add_xprt - Add a new rpc_xprt to an rpc_xprt_switch
+@@ -63,6 +66,7 @@ static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps,
+ if (unlikely(xprt == NULL))
+ return;
+ xps->xps_nxprts--;
++ rpc_xps_nactive_sub_one(xps);
+ if (xps->xps_nxprts == 0)
+ xps->xps_net = NULL;
+ smp_wmb();
+@@ -84,7 +88,7 @@ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
+ spin_unlock(&xps->xps_lock);
+ xprt_put(xprt);
+ }
+-
++EXPORT_SYMBOL(rpc_xprt_switch_remove_xprt);
+ /**
+ * xprt_switch_alloc - Allocate a new struct rpc_xprt_switch
+ * @xprt: pointer to struct rpc_xprt
+@@ -102,7 +106,13 @@ struct rpc_xprt_switch *xprt_switch_alloc(struct rpc_xprt *xprt,
+ if (xps != NULL) {
+ spin_lock_init(&xps->xps_lock);
+ kref_init(&xps->xps_kref);
++#if IS_ENABLED(CONFIG_ENFS)
++ xps->xps_nxprts = 0;
++ xps->xps_nactive = 0;
++ atomic_long_set(&xps->xps_queuelen, 0);
++#else
+ xps->xps_nxprts = 0;
++#endif
+ INIT_LIST_HEAD(&xps->xps_xprt_list);
+ xps->xps_iter_ops = &rpc_xprt_iter_singular;
+ xprt_switch_add_xprt_locked(xps, xprt);
+@@ -148,6 +158,7 @@ struct rpc_xprt_switch *xprt_switch_get(struct rpc_xprt_switch *xps)
+ return xps;
+ return NULL;
+ }
++EXPORT_SYMBOL(xprt_switch_get);
+
+ /**
+ * xprt_switch_put - Release a reference to a rpc_xprt_switch
+@@ -160,6 +171,7 @@ void xprt_switch_put(struct rpc_xprt_switch *xps)
+ if (xps != NULL)
+ kref_put(&xps->xps_kref, xprt_switch_free);
+ }
++EXPORT_SYMBOL(xprt_switch_put);
+
+ /**
+ * rpc_xprt_switch_set_roundrobin - Set a round-robin policy on rpc_xprt_switch
diff --git a/0003-add_enfs_module_for_nfs_mount_option.patch b/0003-add_enfs_module_for_nfs_mount_option.patch
new file mode 100644
index 0000000..70753b5
--- /dev/null
+++ b/0003-add_enfs_module_for_nfs_mount_option.patch
@@ -0,0 +1,1209 @@
+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..9c4c6c1880b6
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_multipath_parse.c
+@@ -0,0 +1,601 @@
++// 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/parser.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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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..6f3e8703e3e2
+--- /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 nfsmultipathoptions type,
++ char *str, void **enfs_option, struct net *net_ns);
++void nfs_multipath_free_options(void **enfs_option);
++
++#endif
diff --git a/0004-add_enfs_module_for_sunrpc_multipatch.patch b/0004-add_enfs_module_for_sunrpc_multipatch.patch
new file mode 100644
index 0000000..2c0fcc7
--- /dev/null
+++ b/0004-add_enfs_module_for_sunrpc_multipatch.patch
@@ -0,0 +1,1581 @@
+diff --git a/fs/nfs/enfs/enfs_multipath.h b/fs/nfs/enfs/enfs_multipath.h
+new file mode 100644
+index 000000000000..e064c2929ced
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_multipath.h
+@@ -0,0 +1,24 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: enfs multipath
++ * Author:
++ * 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_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_path.c b/fs/nfs/enfs/enfs_path.c
+new file mode 100644
+index 000000000000..7355f8c2f672
+--- /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 000000000000..97b1ef3730b8
+--- /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 000000000000..53fa1a07642f
+--- /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 000000000000..321951031c2e
+--- /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 000000000000..2c3fe125c735
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_remount.c
+@@ -0,0 +1,221 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: remount ip source file
++ * Author: y00583252
++ * 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 000000000000..a663ed257004
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_remount.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: remount ip header file
++ * Author: y00583252
++ * 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 000000000000..4e4eda784a3e
+--- /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 000000000000..b72b088a6258
+--- /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
diff --git a/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
new file mode 100644
index 0000000..cc6b677
--- /dev/null
+++ b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
@@ -0,0 +1,1607 @@
+diff --git a/fs/nfs/enfs/enfs_config.c b/fs/nfs/enfs/enfs_config.c
+new file mode 100644
+index 000000000000..11aa7a00385b
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_config.c
+@@ -0,0 +1,378 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ */
++#include <linux/cdev.h>
++#include <linux/errno.h>
++#include <linux/fcntl.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/kthread.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/uaccess.h>
++#include <linux/delay.h>
++
++#include "enfs_errcode.h"
++#include "enfs_log.h"
++#include "enfs_config.h"
++
++#define MAX_FILE_SIZE 8192
++#define STRING_BUF_SIZE 128
++#define CONFIG_FILE_PATH "/etc/enfs/config.ini"
++#define ENFS_NOTIFY_FILE_PERIOD 1000UL
++
++#define MAX_PATH_DETECT_INTERVAL 300
++#define MIN_PATH_DETECT_INTERVAL 5
++#define MAX_PATH_DETECT_TIMEOUT 60
++#define MIN_PATH_DETECT_TIMEOUT 1
++#define MAX_MULTIPATH_TIMEOUT 60
++#define MIN_MULTIPATH_TIMEOUT 0
++#define MAX_MULTIPATH_STATE ENFS_MULTIPATH_DISABLE
++#define MIN_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
++
++#define DEFAULT_PATH_DETECT_INTERVAL 10
++#define DEFAULT_PATH_DETECT_TIMEOUT 5
++#define DEFAULT_MULTIPATH_TIMEOUT 0
++#define DEFAULT_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
++#define DEFAULT_LOADBALANCE_MODE ENFS_LOADBALANCE_RR
++
++typedef int (*check_and_assign_func)(char *, char *, int, int);
++
++struct enfs_config_info {
++ int32_t path_detect_interval;
++ int32_t path_detect_timeout;
++ int32_t multipath_timeout;
++ int32_t loadbalance_mode;
++ int32_t multipath_state;
++};
++
++struct check_and_assign_value {
++ char *field_name;
++ check_and_assign_func func;
++ int min_value;
++ int max_value;
++};
++
++static struct enfs_config_info g_enfs_config_info;
++static struct timespec64 modify_time;
++static struct task_struct *thread;
++
++static int enfs_check_config_value(char *value, int min_value, int max_value)
++{
++ unsigned long num_value;
++ int ret;
++
++ ret = kstrtol(value, 10, &num_value);
++ if (ret != 0) {
++ enfs_log_error("Failed to convert string to int\n");
++ return -EINVAL;
++ }
++
++ if (num_value < min_value || num_value > max_value)
++ return -EINVAL;
++
++ return num_value;
++}
++
++static int32_t enfs_check_and_assign_int_value(char *field_name, char *value,
++ int min_value, int max_value)
++{
++ int int_value = enfs_check_config_value(value, min_value, max_value);
++
++ if (int_value < 0)
++ return -EINVAL;
++
++ if (strcmp(field_name, "path_detect_interval") == 0) {
++ g_enfs_config_info.path_detect_interval = int_value;
++ return ENFS_RET_OK;
++ }
++ if (strcmp(field_name, "path_detect_timeout") == 0) {
++ g_enfs_config_info.path_detect_timeout = int_value;
++ return ENFS_RET_OK;
++ }
++ if (strcmp(field_name, "multipath_timeout") == 0) {
++ g_enfs_config_info.multipath_timeout = int_value;
++ return ENFS_RET_OK;
++ }
++ if (strcmp(field_name, "multipath_disable") == 0) {
++ g_enfs_config_info.multipath_state = int_value;
++ return ENFS_RET_OK;
++ }
++ return -EINVAL;
++}
++
++static int32_t enfs_check_and_assign_loadbalance_mode(char *field_name,
++ char *value,
++ int min_value,
++ int max_value)
++{
++ if (value == NULL)
++ return -EINVAL;
++
++ if (strcmp(field_name, "multipath_select_policy") == 0) {
++ if (strcmp(value, "roundrobin") == 0) {
++ g_enfs_config_info.loadbalance_mode
++ = ENFS_LOADBALANCE_RR;
++ return ENFS_RET_OK;
++ }
++ }
++ return -EINVAL;
++}
++
++static const struct check_and_assign_value g_check_and_assign_value[] = {
++ {"path_detect_interval", enfs_check_and_assign_int_value,
++ MIN_PATH_DETECT_INTERVAL, MAX_PATH_DETECT_INTERVAL},
++ {"path_detect_timeout", enfs_check_and_assign_int_value,
++ MIN_PATH_DETECT_TIMEOUT, MAX_PATH_DETECT_TIMEOUT},
++ {"multipath_timeout", enfs_check_and_assign_int_value,
++ MIN_MULTIPATH_TIMEOUT, MAX_MULTIPATH_TIMEOUT},
++ {"multipath_disable", enfs_check_and_assign_int_value,
++ MIN_MULTIPATH_STATE, MAX_MULTIPATH_STATE},
++ {"multipath_select_policy", enfs_check_and_assign_loadbalance_mode,
++ 0, 0},
++};
++
++static int32_t enfs_read_config_file(char *buffer, char *file_path)
++{
++ int ret;
++ struct file *filp = NULL;
++ loff_t f_pos = 0;
++ mm_segment_t fs;
++
++
++ filp = filp_open(file_path, O_RDONLY, 0);
++
++ if (IS_ERR(filp)) {
++ enfs_log_error("Failed to open file %s\n", CONFIG_FILE_PATH);
++ ret = -ENOENT;
++ return ret;
++ }
++
++ fs = get_fs();
++ set_fs(get_ds());
++ kernel_read(filp, buffer, MAX_FILE_SIZE, &f_pos);
++ set_fs(fs);
++
++ ret = filp_close(filp, NULL);
++ if (ret) {
++ enfs_log_error("Close File:%s failed:%d.\n",
++ CONFIG_FILE_PATH, ret);
++ return -EINVAL;
++ }
++ return ENFS_RET_OK;
++}
++
++static int32_t enfs_deal_with_comment_line(char *buffer)
++{
++ int ret;
++ char *pos = strchr(buffer, '\n');
++
++ if (pos != NULL)
++ ret = strlen(buffer) - strlen(pos);
++ else
++ ret = strlen(buffer);
++
++ return ret;
++}
++
++static int32_t enfs_parse_key_value_from_config(char *buffer, char *key,
++ char *value, int keyLen,
++ int valueLen)
++{
++ char *line;
++ char *tokenPtr;
++ int len;
++ char *tem;
++ char *pos = strchr(buffer, '\n');
++
++ if (pos != NULL)
++ len = strlen(buffer) - strlen(pos);
++ else
++ len = strlen(buffer);
++
++ line = kmalloc(len + 1, GFP_KERNEL);
++ if (!line) {
++ enfs_log_error("Failed to allocate memory.\n");
++ return -ENOMEM;
++ }
++ line[len] = '\0';
++ strncpy(line, buffer, len);
++
++ tem = line;
++ tokenPtr = strsep(&tem, "=");
++ if (tokenPtr == NULL || tem == NULL) {
++ kfree(line);
++ return len;
++ }
++ strncpy(key, strim(tokenPtr), keyLen);
++ strncpy(value, strim(tem), valueLen);
++
++ kfree(line);
++ return len;
++}
++
++static int32_t enfs_get_value_from_config_file(char *buffer, char *field_name,
++ char *value, int valueLen)
++{
++ int ret;
++ char key[STRING_BUF_SIZE + 1] = {0};
++ char val[STRING_BUF_SIZE + 1] = {0};
++
++ while (buffer[0] != '\0') {
++ if (buffer[0] == '\n') {
++ buffer++;
++ } else if (buffer[0] == '#') {
++ ret = enfs_deal_with_comment_line(buffer);
++ if (ret > 0)
++ buffer += ret;
++ } else {
++ ret = enfs_parse_key_value_from_config(buffer, key, val,
++ STRING_BUF_SIZE,
++ STRING_BUF_SIZE);
++ if (ret < 0) {
++ enfs_log_error("failed parse key value, %d\n"
++ , ret);
++ return ret;
++ }
++ key[STRING_BUF_SIZE] = '\0';
++ val[STRING_BUF_SIZE] = '\0';
++
++ buffer += ret;
++
++ if (strcmp(field_name, key) == 0) {
++ strncpy(value, val, valueLen);
++ return ENFS_RET_OK;
++ }
++ }
++ }
++ enfs_log_error("can not find value which matched field_name: %s.\n",
++ field_name);
++ return -EINVAL;
++}
++
++int32_t enfs_config_load(void)
++{
++ char value[STRING_BUF_SIZE + 1];
++ int ret;
++ int table_len;
++ int min;
++ int max;
++ int i;
++ char *buffer;
++
++ buffer = kmalloc(MAX_FILE_SIZE, GFP_KERNEL);
++ if (!buffer) {
++ enfs_log_error("Failed to allocate memory.\n");
++ return -ENOMEM;
++ }
++ memset(buffer, 0, MAX_FILE_SIZE);
++
++ g_enfs_config_info.path_detect_interval = DEFAULT_PATH_DETECT_INTERVAL;
++ g_enfs_config_info.path_detect_timeout = DEFAULT_PATH_DETECT_TIMEOUT;
++ g_enfs_config_info.multipath_timeout = DEFAULT_MULTIPATH_TIMEOUT;
++ g_enfs_config_info.multipath_state = DEFAULT_MULTIPATH_STATE;
++ g_enfs_config_info.loadbalance_mode = DEFAULT_LOADBALANCE_MODE;
++
++ table_len = sizeof(g_check_and_assign_value) /
++ sizeof(g_check_and_assign_value[0]);
++
++ ret = enfs_read_config_file(buffer, CONFIG_FILE_PATH);
++ if (ret != 0) {
++ kfree(buffer);
++ return ret;
++ }
++
++ for (i = 0; i < table_len; i++) {
++ ret = enfs_get_value_from_config_file(buffer,
++ g_check_and_assign_value[i].field_name,
++ value, STRING_BUF_SIZE);
++ if (ret < 0)
++ continue;
++
++ value[STRING_BUF_SIZE] = '\0';
++ min = g_check_and_assign_value[i].min_value;
++ max = g_check_and_assign_value[i].max_value;
++ if (g_check_and_assign_value[i].func != NULL)
++ (*g_check_and_assign_value[i].func)(
++ g_check_and_assign_value[i].field_name,
++ value, min, max);
++ }
++
++ kfree(buffer);
++ return ENFS_RET_OK;
++}
++
++int32_t enfs_get_config_path_detect_interval(void)
++{
++ return g_enfs_config_info.path_detect_interval;
++}
++
++int32_t enfs_get_config_path_detect_timeout(void)
++{
++ return g_enfs_config_info.path_detect_timeout;
++}
++
++int32_t enfs_get_config_multipath_timeout(void)
++{
++ return g_enfs_config_info.multipath_timeout;
++}
++
++int32_t enfs_get_config_multipath_state(void)
++{
++ return g_enfs_config_info.multipath_state;
++}
++
++int32_t enfs_get_config_loadbalance_mode(void)
++{
++ return g_enfs_config_info.loadbalance_mode;
++}
++
++static bool enfs_file_changed(const char *filename)
++{
++ int err;
++ struct kstat file_stat;
++
++ err = vfs_stat(filename, &file_stat);
++ if (err) {
++ pr_err("failed to open file:%s err:%d\n", filename, err);
++ return false;
++ }
++
++ if (timespec64_compare(&modify_time, &file_stat.mtime) == -1) {
++ modify_time = file_stat.mtime;
++ pr_info("file change: %lld %lld\n", modify_time.tv_sec,
++ file_stat.mtime.tv_sec);
++ return true;
++ }
++
++ return false;
++}
++
++static int enfs_thread_func(void *data)
++{
++ while (!kthread_should_stop()) {
++ if (enfs_file_changed(CONFIG_FILE_PATH))
++ enfs_config_load();
++
++ msleep(ENFS_NOTIFY_FILE_PERIOD);
++ }
++ return 0;
++}
++
++int enfs_config_timer_init(void)
++{
++ thread = kthread_run(enfs_thread_func, NULL, "enfs_notiy_file_thread");
++ if (IS_ERR(thread)) {
++ pr_err("Failed to create kernel thread\n");
++ return PTR_ERR(thread);
++ }
++ return 0;
++}
++
++void enfs_config_timer_exit(void)
++{
++ pr_info("enfs_notify_file_exit\n");
++ if (thread)
++ kthread_stop(thread);
++}
+diff --git a/fs/nfs/enfs/enfs_config.h b/fs/nfs/enfs/enfs_config.h
+new file mode 100644
+index 000000000000..752710129170
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_config.h
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs configuration
++ * Author: y00583252
++ * Create: 2023-07-27
++ */
++
++#ifndef ENFS_CONFIG_H
++#define ENFS_CONFIG_H
++
++#include <linux/types.h>
++
++enum enfs_multipath_state {
++ ENFS_MULTIPATH_ENABLE = 0,
++ ENFS_MULTIPATH_DISABLE = 1,
++};
++
++enum enfs_loadbalance_mode {
++ ENFS_LOADBALANCE_RR,
++};
++
++
++int32_t enfs_get_config_path_detect_interval(void);
++int32_t enfs_get_config_path_detect_timeout(void);
++int32_t enfs_get_config_multipath_timeout(void);
++int32_t enfs_get_config_multipath_state(void);
++int32_t enfs_get_config_loadbalance_mode(void);
++int32_t enfs_config_load(void);
++int32_t enfs_config_timer_init(void);
++void enfs_config_timer_exit(void);
++#endif // ENFS_CONFIG_H
+diff --git a/fs/nfs/enfs/enfs_errcode.h b/fs/nfs/enfs/enfs_errcode.h
+new file mode 100644
+index 000000000000..cca47ab9a191
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_errcode.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs errocode
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#ifndef ENFS_ERRCODE_H
++#define ENFS_ERRCODE_H
++
++enum {
++ ENFS_RET_OK = 0,
++ ENFS_RET_FAIL
++};
++
++#endif // ENFS_ERRCODE_H
+diff --git a/fs/nfs/enfs/enfs_log.h b/fs/nfs/enfs/enfs_log.h
+new file mode 100644
+index 000000000000..177b404f05df
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_log.h
+@@ -0,0 +1,25 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: enfs log
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++#ifndef ENFS_LOG_H
++#define ENFS_LOG_H
++
++#include <linux/printk.h>
++
++#define enfs_log_info(fmt, ...) \
++ pr_info("enfs:[%s]" pr_fmt(fmt), \
++ __func__, ##__VA_ARGS__)
++
++#define enfs_log_error(fmt, ...) \
++ pr_err("enfs:[%s]" pr_fmt(fmt), \
++ __func__, ##__VA_ARGS__)
++
++#define enfs_log_debug(fmt, ...) \
++ pr_debug("enfs:[%s]" pr_fmt(fmt), \
++ __func__, ##__VA_ARGS__)
++
++#endif // ENFS_ERRCODE_H
+diff --git a/fs/nfs/enfs/failover_com.h b/fs/nfs/enfs/failover_com.h
+new file mode 100644
+index 000000000000..c52940da232e
+--- /dev/null
++++ b/fs/nfs/enfs/failover_com.h
+@@ -0,0 +1,23 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: failover time commont header file
++ * Create: 2023-08-02
++ */
++#ifndef FAILOVER_COMMON_H
++#define FAILOVER_COMMON_H
++
++static inline bool failover_is_enfs_clnt(struct rpc_clnt *clnt)
++{
++ struct rpc_clnt *next = clnt->cl_parent;
++
++ while (next) {
++ if (next == next->cl_parent)
++ break;
++ next = next->cl_parent;
++ }
++
++ return next != NULL ? next->cl_enfs : clnt->cl_enfs;
++}
++
++#endif // FAILOVER_COMMON_H
+diff --git a/fs/nfs/enfs/failover_path.c b/fs/nfs/enfs/failover_path.c
+new file mode 100644
+index 000000000000..93b454de29d1
+--- /dev/null
++++ b/fs/nfs/enfs/failover_path.c
+@@ -0,0 +1,207 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs path failover file
++ * Author: y00583252
++ * Create: 2023-08-02
++ */
++
++#include "failover_path.h"
++#include <linux/nfs.h>
++#include <linux/nfs3.h>
++#include <linux/nfs4.h>
++#include <linux/sunrpc/clnt.h>
++#include <linux/sunrpc/sched.h>
++#include <linux/sunrpc/xprt.h>
++#include "enfs_config.h"
++#include "enfs_log.h"
++#include "failover_com.h"
++#include "pm_state.h"
++#include "pm_ping.h"
++
++enum failover_policy_t {
++ FAILOVER_NOACTION = 1,
++ FAILOVER_RETRY,
++ FAILOVER_RETRY_DELAY,
++};
++
++static void failover_retry_path(struct rpc_task *task)
++{
++ xprt_release(task);
++ rpc_init_task_retry_counters(task);
++ rpc_task_release_transport(task);
++ rpc_restart_call(task);
++}
++
++static void failover_retry_path_delay(struct rpc_task *task, int32_t delay)
++{
++ failover_retry_path(task);
++ rpc_delay(task, delay);
++}
++
++static void failover_retry_path_by_policy(struct rpc_task *task,
++ enum failover_policy_t policy)
++{
++ if (policy == FAILOVER_RETRY)
++ failover_retry_path(task);
++ else if (policy == FAILOVER_RETRY_DELAY)
++ failover_retry_path_delay(task, 3 * HZ); // delay 3s
++}
++
++static
++enum failover_policy_t failover_get_nfs3_retry_policy(struct rpc_task *task)
++{
++ enum failover_policy_t policy = FAILOVER_NOACTION;
++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
++ u32 proc;
++
++ if (unlikely(procinfo == NULL)) {
++ enfs_log_error("the task contains no valid proc.\n");
++ return FAILOVER_NOACTION;
++ }
++
++ proc = procinfo->p_proc;
++
++ switch (proc) {
++ case NFS3PROC_CREATE:
++ case NFS3PROC_MKDIR:
++ case NFS3PROC_REMOVE:
++ case NFS3PROC_RMDIR:
++ case NFS3PROC_SYMLINK:
++ case NFS3PROC_LINK:
++ case NFS3PROC_SETATTR:
++ case NFS3PROC_WRITE:
++ policy = FAILOVER_RETRY_DELAY;
++ default:
++ policy = FAILOVER_RETRY;
++ }
++ return policy;
++}
++
++static
++enum failover_policy_t failover_get_nfs4_retry_policy(struct rpc_task *task)
++{
++ enum failover_policy_t policy = FAILOVER_NOACTION;
++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
++ u32 proc_idx;
++
++ if (unlikely(procinfo == NULL)) {
++ enfs_log_error("the task contains no valid proc.\n");
++ return FAILOVER_NOACTION;
++ }
++
++ proc_idx = procinfo->p_statidx;
++
++ switch (proc_idx) {
++ case NFSPROC4_CLNT_CREATE:
++ case NFSPROC4_CLNT_REMOVE:
++ case NFSPROC4_CLNT_LINK:
++ case NFSPROC4_CLNT_SYMLINK:
++ case NFSPROC4_CLNT_SETATTR:
++ case NFSPROC4_CLNT_WRITE:
++ case NFSPROC4_CLNT_RENAME:
++ case NFSPROC4_CLNT_SETACL:
++ policy = FAILOVER_RETRY_DELAY;
++ default:
++ policy = FAILOVER_RETRY;
++ }
++ return policy;
++}
++
++static enum failover_policy_t failover_get_retry_policy(struct rpc_task *task)
++{
++ struct rpc_clnt *clnt = task->tk_client;
++ u32 version = clnt->cl_vers;
++ enum failover_policy_t policy = FAILOVER_NOACTION;
++
++ // 1. if the task meant to send to certain xprt, take no action
++ if (task->tk_flags & RPC_TASK_FIXED)
++ return FAILOVER_NOACTION;
++
++ // 2. get policy by different version of nfs protocal
++ if (version == 3) // nfs v3
++ policy = failover_get_nfs3_retry_policy(task);
++ else if (version == 4) // nfs v4
++ policy = failover_get_nfs4_retry_policy(task);
++ else
++ return FAILOVER_NOACTION;
++
++ // 3. if the task is not send to target, retry immediately
++ if (!RPC_WAS_SENT(task))
++ policy = FAILOVER_RETRY;
++
++ return policy;
++}
++
++static int failover_check_task(struct rpc_task *task)
++{
++ struct rpc_clnt *clnt = NULL;
++ int disable_mpath = enfs_get_config_multipath_state();
++
++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return -EINVAL;
++ }
++
++ if (unlikely((task == NULL) || (task->tk_client == NULL))) {
++ enfs_log_error("The task is not valid.\n");
++ return -EINVAL;
++ }
++
++ clnt = task->tk_client;
++
++ if (clnt->cl_prog != NFS_PROGRAM) {
++ enfs_log_debug("The clnt is not prog{%u} type.\n",
++ clnt->cl_prog);
++ return -EINVAL;
++ }
++
++ if (!failover_is_enfs_clnt(clnt)) {
++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
++void failover_handle(struct rpc_task *task)
++{
++ enum failover_policy_t policy;
++ int ret;
++
++ ret = failover_check_task(task);
++ if (ret != 0)
++ return;
++
++ pm_set_path_state(task->tk_xprt, PM_STATE_FAULT);
++
++ policy = failover_get_retry_policy(task);
++
++ failover_retry_path_by_policy(task, policy);
++}
++
++bool failover_task_need_call_start_again(struct rpc_task *task)
++{
++ int ret;
++
++ ret = failover_check_task(task);
++ if (ret != 0)
++ return false;
++
++ return true;
++}
++
++bool failover_prepare_transmit(struct rpc_task *task)
++{
++ if (task->tk_flags & RPC_TASK_FIXED)
++ return true;
++
++ if (pm_ping_is_test_xprt_task(task))
++ return true;
++
++ if (pm_get_path_state(task->tk_xprt) == PM_STATE_FAULT) {
++ task->tk_status = -ETIMEDOUT;
++ return false;
++ }
++
++ return true;
++}
+diff --git a/fs/nfs/enfs/failover_path.h b/fs/nfs/enfs/failover_path.h
+new file mode 100644
+index 000000000000..6f1294829a6e
+--- /dev/null
++++ b/fs/nfs/enfs/failover_path.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs path failover header file
++ * Author: y00583252
++ * Create: 2023-08-02
++ */
++
++#ifndef FAILOVER_PATH_H
++#define FAILOVER_PATH_H
++
++#include <linux/sunrpc/sched.h>
++
++void failover_handle(struct rpc_task *task);
++bool failover_prepare_transmit(struct rpc_task *task);
++
++#endif // FAILOVER_PATH_H
+diff --git a/fs/nfs/enfs/failover_time.c b/fs/nfs/enfs/failover_time.c
+new file mode 100644
+index 000000000000..866ea82d13fc
+--- /dev/null
++++ b/fs/nfs/enfs/failover_time.c
+@@ -0,0 +1,99 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: failover time file
++ * Create: 2023-08-02
++ */
++
++#include "failover_time.h"
++#include <linux/jiffies.h>
++#include <linux/sunrpc/clnt.h>
++#include "enfs_config.h"
++#include "enfs_log.h"
++#include "failover_com.h"
++#include "pm_ping.h"
++
++static unsigned long failover_get_mulitipath_timeout(struct rpc_clnt *clnt)
++{
++ unsigned long config_tmo = enfs_get_config_multipath_timeout() * HZ;
++ unsigned long clnt_tmo = clnt->cl_timeout->to_initval;
++
++ if (config_tmo == 0)
++ return clnt_tmo;
++
++ return config_tmo > clnt_tmo ? clnt_tmo : config_tmo;
++}
++
++void failover_adjust_task_timeout(struct rpc_task *task, void *condition)
++{
++ struct rpc_clnt *clnt = NULL;
++ unsigned long tmo;
++ int disable_mpath = enfs_get_config_multipath_state();
++
++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return;
++ }
++
++ clnt = task->tk_client;
++ if (unlikely(clnt == NULL)) {
++ enfs_log_error("task associate client is NULL.\n");
++ return;
++ }
++
++ if (!failover_is_enfs_clnt(clnt)) {
++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
++ return;
++ }
++
++ tmo = failover_get_mulitipath_timeout(clnt);
++ if (tmo == 0) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return;
++ }
++
++ if (task->tk_timeout != 0)
++ task->tk_timeout =
++ task->tk_timeout < tmo ? task->tk_timeout : tmo;
++ else
++ task->tk_timeout = tmo;
++}
++
++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
++{
++ struct rpc_clnt *clnt = NULL;
++ int disable_mpath = enfs_get_config_multipath_state();
++
++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return;
++ }
++
++ clnt = task->tk_client;
++ if (unlikely(clnt == NULL)) {
++ enfs_log_error("task associate client is NULL.\n");
++ return;
++ }
++
++ if (!failover_is_enfs_clnt(clnt)) {
++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
++ return;
++ }
++
++ if (!pm_ping_is_test_xprt_task(task))
++ req->rq_timeout = failover_get_mulitipath_timeout(clnt);
++ else {
++ req->rq_timeout = enfs_get_config_path_detect_timeout() * HZ;
++ req->rq_majortimeo = req->rq_timeout + jiffies;
++ }
++
++ /*
++ * when task is retried, the req is new, we lost major-timeout times,
++ * so we have to restore req major
++ * timeouts from the task, if it is stored.
++ */
++ if (task->tk_major_timeo != 0)
++ req->rq_majortimeo = task->tk_major_timeo;
++ else
++ task->tk_major_timeo = req->rq_majortimeo;
++}
+diff --git a/fs/nfs/enfs/failover_time.h b/fs/nfs/enfs/failover_time.h
+new file mode 100644
+index 000000000000..ede25b577a2a
+--- /dev/null
++++ b/fs/nfs/enfs/failover_time.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: failover time header file
++ * Create: 2023-08-02
++ */
++
++#ifndef FAILOVER_TIME_H
++#define FAILOVER_TIME_H
++
++#include <linux/sunrpc/sched.h>
++
++void failover_adjust_task_timeout(struct rpc_task *task, void *condition);
++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req);
++
++#endif // FAILOVER_TIME_H
+diff --git a/fs/nfs/enfs/init.h b/fs/nfs/enfs/init.h
+new file mode 100644
+index 000000000000..fdabb9084e19
+--- /dev/null
++++ b/fs/nfs/enfs/init.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs client init
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#ifndef ENFS_INIT_H
++#define ENFS_INIT_H
++
++#include <linux/types.h>
++
++int32_t enfs_init(void);
++void enfs_fini(void);
++
++#endif
+diff --git a/fs/nfs/enfs/mgmt_init.c b/fs/nfs/enfs/mgmt_init.c
+new file mode 100644
+index 000000000000..75a40c5e0f6c
+--- /dev/null
++++ b/fs/nfs/enfs/mgmt_init.c
+@@ -0,0 +1,22 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: mgmt component init
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#include "mgmt_init.h"
++#include <linux/printk.h>
++#include "enfs_errcode.h"
++#include "enfs_config.h"
++
++int32_t mgmt_init(void)
++{
++ return enfs_config_timer_init();
++}
++
++void mgmt_fini(void)
++{
++ enfs_config_timer_exit();
++}
+diff --git a/fs/nfs/enfs/mgmt_init.h b/fs/nfs/enfs/mgmt_init.h
+new file mode 100644
+index 000000000000..aa78303b9f01
+--- /dev/null
++++ b/fs/nfs/enfs/mgmt_init.h
+@@ -0,0 +1,18 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: mgmt component init
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#ifndef MGMT_INIT_H
++#define MGMT_INIT_H
++
++#include <linux/types.h>
++
++int32_t mgmt_init(void);
++void mgmt_fini(void);
++
++
++#endif // MGMT_INIT_H
+diff --git a/fs/nfs/enfs/pm_ping.c b/fs/nfs/enfs/pm_ping.c
+new file mode 100644
+index 000000000000..24153cd4c7f3
+--- /dev/null
++++ b/fs/nfs/enfs/pm_ping.c
+@@ -0,0 +1,421 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: path state header file
++ * Author: x00833432
++ * Create: 2023-08-21
++ */
++
++#include "pm_ping.h"
++#include <linux/err.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/printk.h>
++#include <linux/kthread.h>
++#include <linux/nfs.h>
++#include <linux/errno.h>
++#include <linux/rcupdate.h>
++#include <linux/workqueue.h>
++#include <net/netns/generic.h>
++#include <linux/atomic.h>
++#include <linux/sunrpc/clnt.h>
++
++#include "../../../net/sunrpc/netns.h"
++#include "pm_state.h"
++#include "enfs.h"
++#include "enfs_log.h"
++#include "enfs_config.h"
++
++#define SLEEP_INTERVAL 2
++extern unsigned int sunrpc_net_id;
++
++static struct task_struct *pm_ping_timer_thread;
++//protect pint_execute_workq
++static spinlock_t ping_execute_workq_lock;
++// timer for test xprt workqueue
++static struct workqueue_struct *ping_execute_workq;
++// count the ping xprt work on flight
++static atomic_t check_xprt_count;
++
++struct ping_xprt_work {
++ struct rpc_xprt *xprt; // use this specific xprt
++ struct rpc_clnt *clnt; // use this specific rpc_client
++ struct work_struct ping_work;
++};
++
++struct pm_ping_async_callback {
++ void *data;
++ void (*func)(void *data);
++};
++
++// set xprt's enum pm_check_state
++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
++ enum pm_check_state state)
++{
++ struct enfs_xprt_context *ctx = NULL;
++
++ if (IS_ERR(xprt)) {
++ enfs_log_error("The xprt ptr is not exist.\n");
++ return;
++ }
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ xprt_get(xprt);
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ xprt_put(xprt);
++ return;
++ }
++
++ atomic_set(&ctx->path_check_state, state);
++ xprt_put(xprt);
++}
++
++// get xprt's enum pm_check_state
++static enum pm_check_state pm_ping_get_path_check_state(struct rpc_xprt *xprt)
++{
++ struct enfs_xprt_context *ctx = NULL;
++ enum pm_check_state state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return PM_CHECK_UNDEFINE;
++ }
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ return PM_CHECK_UNDEFINE;
++ }
++
++ state = atomic_read(&ctx->path_check_state);
++
++ return state;
++}
++
++static void pm_ping_call_done_callback(void *data)
++{
++ struct pm_ping_async_callback *callback_data =
++ (struct pm_ping_async_callback *)data;
++
++ if (callback_data == NULL)
++ return;
++
++ callback_data->func(callback_data->data);
++
++ kfree(callback_data);
++}
++
++// Default callback for async RPC calls
++static void pm_ping_call_done(struct rpc_task *task, void *data)
++{
++ struct rpc_xprt *xprt = task->tk_xprt;
++
++ atomic_dec(&check_xprt_count);
++ if (task->tk_status >= 0)
++ pm_set_path_state(xprt, PM_STATE_NORMAL);
++ else
++ pm_set_path_state(xprt, PM_STATE_FAULT);
++
++ pm_ping_set_path_check_state(xprt, PM_CHECK_FINISH);
++
++ pm_ping_call_done_callback(data);
++}
++
++// register func to rpc_call_done
++static const struct rpc_call_ops pm_ping_set_status_ops = {
++ .rpc_call_done = pm_ping_call_done,
++};
++
++// execute work which in work_queue
++static void pm_ping_execute_work(struct work_struct *work)
++{
++ int ret = 0;
++
++ // get the work information
++ struct ping_xprt_work *work_info =
++ container_of(work, struct ping_xprt_work, ping_work);
++
++ // if check state is pending
++ if (pm_ping_get_path_check_state(work_info->xprt) == PM_CHECK_WAITING) {
++
++ pm_ping_set_path_check_state(work_info->xprt,
++ PM_CHECK_CHECKING);
++
++ ret = rpc_clnt_test_xprt(work_info->clnt,
++ work_info->xprt,
++ &pm_ping_set_status_ops,
++ NULL,
++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
++
++ if (ret < 0) {
++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
++
++ pm_ping_set_path_check_state(work_info->xprt,
++ PM_CHECK_FINISH);
++
++ } else
++ atomic_inc(&check_xprt_count);
++
++ }
++
++ atomic_dec(&work_info->clnt->cl_count);
++ xprt_put(work_info->xprt);
++ kfree(work_info);
++ work_info = NULL;
++}
++
++static bool pm_ping_workqueue_queue_work(struct work_struct *work)
++{
++ bool ret = false;
++
++ spin_lock(&ping_execute_workq_lock);
++
++ if (ping_execute_workq != NULL)
++ ret = queue_work(ping_execute_workq, work);
++
++ spin_unlock(&ping_execute_workq_lock);
++ return ret;
++}
++
++// init test work and add this work to workqueue
++static int pm_ping_add_work(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt, void *data)
++{
++ struct ping_xprt_work *work_info;
++ bool ret = false;
++
++ if (IS_ERR(xprt) || xprt == NULL) {
++ enfs_log_error("The xprt ptr is not exist.\n");
++ return -EINVAL;
++ }
++
++ if (IS_ERR(clnt) || clnt == NULL) {
++ enfs_log_error("The clnt ptr is not exist.\n");
++ return -EINVAL;
++ }
++
++ if (!xprt->multipath_context) {
++ enfs_log_error("multipath_context is null.\n");
++ return -EINVAL;
++ }
++
++ // check xprt pending status, if pending status equals Finish
++ // means this xprt can inster to work queue
++ if (pm_ping_get_path_check_state(xprt) ==
++ PM_CHECK_FINISH ||
++ pm_ping_get_path_check_state(xprt) ==
++ PM_CHECK_INIT) {
++
++ enfs_log_debug("find xprt pointer. %p\n", xprt);
++ work_info = kzalloc(sizeof(struct ping_xprt_work), GFP_ATOMIC);
++ if (work_info == NULL)
++ return -ENOMEM;
++ work_info->clnt = clnt;
++ atomic_inc(&clnt->cl_count);
++ work_info->xprt = xprt;
++ xprt_get(xprt);
++ INIT_WORK(&work_info->ping_work, pm_ping_execute_work);
++ pm_ping_set_path_check_state(xprt, PM_CHECK_WAITING);
++
++ ret = pm_ping_workqueue_queue_work(&work_info->ping_work);
++ if (!ret) {
++ atomic_dec(&work_info->clnt->cl_count);
++ xprt_put(work_info->xprt);
++ kfree(work_info);
++ return -EINVAL;
++ }
++ }
++ return 0;
++}
++
++// encapsulate pm_ping_add_work()
++static int pm_ping_execute_xprt_test(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt, void *data)
++{
++ pm_ping_add_work(clnt, xprt, NULL);
++ // return 0 for rpc_clnt_iterate_for_each_xprt();
++ // because negative value will stop iterate all xprt
++ // and we need return negative value for debug
++ // Therefore, we need this function to iterate all xprt
++ return 0;
++}
++
++// export to other module add ping work to workqueue
++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
++{
++ int ret;
++
++ ret = pm_ping_add_work(clnt, xprt, NULL);
++ return ret;
++}
++
++// iterate xprt in the client
++static void pm_ping_loop_rpclnt(struct sunrpc_net *sn)
++{
++ struct rpc_clnt *clnt;
++
++ spin_lock(&sn->rpc_client_lock);
++ list_for_each_entry_rcu(clnt, &sn->all_clients, cl_clients) {
++ if (clnt->cl_enfs) {
++ enfs_log_debug("find rpc_clnt. %p\n", clnt);
++ rpc_clnt_iterate_for_each_xprt(clnt,
++ pm_ping_execute_xprt_test, NULL);
++ }
++ }
++ spin_unlock(&sn->rpc_client_lock);
++}
++
++// iterate each clnt in the sunrpc_net
++static void pm_ping_loop_sunrpc_net(void)
++{
++ struct net *net;
++ struct sunrpc_net *sn;
++
++ rcu_read_lock();
++ for_each_net_rcu(net) {
++ sn = net_generic(net, sunrpc_net_id);
++ if (sn == NULL)
++ continue;
++ pm_ping_loop_rpclnt(sn);
++ }
++ rcu_read_unlock();
++}
++
++static int pm_ping_routine(void *data)
++{
++ while (!kthread_should_stop()) {
++ // equale 0 means open multipath
++ if (enfs_get_config_multipath_state() ==
++ ENFS_MULTIPATH_ENABLE)
++ pm_ping_loop_sunrpc_net();
++
++ msleep((unsigned int)
++ enfs_get_config_path_detect_interval() * 1000);
++ }
++ return 0;
++}
++
++// start thread to cycly ping
++static int pm_ping_start(void)
++{
++ pm_ping_timer_thread =
++ kthread_run(pm_ping_routine, NULL, "pm_ping_routine");
++ if (IS_ERR(pm_ping_timer_thread)) {
++ enfs_log_error("Failed to create kernel thread\n");
++ return PTR_ERR(pm_ping_timer_thread);
++ }
++ return 0;
++}
++
++// initialize workqueue
++static int pm_ping_workqueue_init(void)
++{
++ struct workqueue_struct *queue = NULL;
++
++ queue = create_workqueue("pm_ping_workqueue");
++
++ if (queue == NULL) {
++ enfs_log_error("create workqueue failed.\n");
++ return -ENOMEM;
++ }
++
++ spin_lock(&ping_execute_workq_lock);
++ ping_execute_workq = queue;
++ spin_unlock(&ping_execute_workq_lock);
++ enfs_log_info("create workqueue succeeeded.\n");
++ return 0;
++}
++
++static void pm_ping_workqueue_fini(void)
++{
++ struct workqueue_struct *queue = NULL;
++
++ spin_lock(&ping_execute_workq_lock);
++ queue = ping_execute_workq;
++ ping_execute_workq = NULL;
++ spin_unlock(&ping_execute_workq_lock);
++
++ enfs_log_info("delete work queue\n");
++
++ if (queue != NULL) {
++ flush_workqueue(queue);
++ destroy_workqueue(queue);
++ }
++}
++
++// module exit func
++void pm_ping_fini(void)
++{
++ if (pm_ping_timer_thread)
++ kthread_stop(pm_ping_timer_thread);
++
++ pm_ping_workqueue_fini();
++
++ while (atomic_read(&check_xprt_count) != 0)
++ msleep(SLEEP_INTERVAL);
++}
++
++// module init func
++int pm_ping_init(void)
++{
++ int ret;
++
++ atomic_set(&check_xprt_count, 0);
++ ret = pm_ping_workqueue_init();
++ if (ret != 0) {
++ enfs_log_error("PM_PING Module loading failed.\n");
++ return ret;
++ }
++ ret = pm_ping_start();
++ if (ret != 0) {
++ enfs_log_error("PM_PING Module loading failed.\n");
++ pm_ping_workqueue_fini();
++ return ret;
++ }
++
++ return ret;
++}
++
++bool pm_ping_is_test_xprt_task(struct rpc_task *task)
++{
++ return task->tk_ops == &pm_ping_set_status_ops ? true : false;
++}
++
++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt,
++ void (*func)(void *data),
++ void *data)
++{
++ int ret;
++
++ struct pm_ping_async_callback *callback_data =
++ kzalloc(sizeof(struct pm_ping_async_callback), GFP_KERNEL);
++
++ if (callback_data == NULL) {
++ enfs_log_error("failed to mzalloc mem\n");
++ return -ENOMEM;
++ }
++
++ callback_data->data = data;
++ callback_data->func = func;
++ atomic_inc(&check_xprt_count);
++ ret = rpc_clnt_test_xprt(clnt, xprt,
++ &pm_ping_set_status_ops,
++ callback_data,
++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
++
++ if (ret < 0) {
++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
++ atomic_dec(&check_xprt_count);
++ }
++
++ return ret;
++}
+diff --git a/fs/nfs/enfs/pm_ping.h b/fs/nfs/enfs/pm_ping.h
+new file mode 100644
+index 000000000000..6bcb94bfc836
+--- /dev/null
++++ b/fs/nfs/enfs/pm_ping.h
+@@ -0,0 +1,33 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs configuration
++ * Author: x00833432
++ * Create: 2023-07-27
++ */
++
++#ifndef PM_PING_H
++#define PM_PING_H
++
++#include <linux/sunrpc/clnt.h>
++
++enum pm_check_state {
++ PM_CHECK_INIT, // this xprt never been queued
++ PM_CHECK_WAITING, // this xprt waiting in the queue
++ PM_CHECK_CHECKING, // this xprt is testing
++ PM_CHECK_FINISH, // this xprt has been finished
++ PM_CHECK_UNDEFINE, // undefine multipath struct
++};
++
++int pm_ping_init(void);
++void pm_ping_fini(void);
++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
++ enum pm_check_state state);
++bool pm_ping_is_test_xprt_task(struct rpc_task *task);
++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt,
++ void (*func)(void *data),
++ void *data);
++
++#endif // PM_PING_H
+diff --git a/fs/nfs/enfs/pm_state.c b/fs/nfs/enfs/pm_state.c
+new file mode 100644
+index 000000000000..220621a207a2
+--- /dev/null
++++ b/fs/nfs/enfs/pm_state.c
+@@ -0,0 +1,158 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: path state file
++ * Author: y00583252
++ * Create: 2023-08-12
++ */
++#include "pm_state.h"
++#include <linux/sunrpc/xprt.h>
++
++#include "enfs.h"
++#include "enfs_log.h"
++
++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt)
++{
++ struct enfs_xprt_context *ctx = NULL;
++ enum pm_path_state state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return PM_STATE_UNDEFINED;
++ }
++
++ xprt_get(xprt);
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ xprt_put(xprt);
++ return PM_STATE_UNDEFINED;
++ }
++
++ state = atomic_read(&ctx->path_state);
++
++ xprt_put(xprt);
++
++ return state;
++}
++
++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state)
++{
++ struct enfs_xprt_context *ctx = NULL;
++ enum pm_path_state cur_state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ xprt_get(xprt);
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ xprt_put(xprt);
++ return;
++ }
++
++ cur_state = atomic_read(&ctx->path_state);
++ if (cur_state == state) {
++ enfs_log_debug("The xprt is already {%d}.\n", state);
++ xprt_put(xprt);
++ return;
++ }
++
++ atomic_set(&ctx->path_state, state);
++ enfs_log_info("The xprt {%p} path state change from {%d} to {%d}.\n",
++ xprt, cur_state, state);
++
++ xprt_put(xprt);
++}
++
++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len)
++{
++ enum pm_path_state state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ if ((buf == NULL) || (len <= 0)) {
++ enfs_log_error("Buffer is not valid, len=%d.\n", len);
++ return;
++ }
++
++ state = pm_get_path_state(xprt);
++
++ switch (state) {
++ case PM_STATE_INIT:
++ (void)snprintf(buf, len, "Init");
++ break;
++ case PM_STATE_NORMAL:
++ (void)snprintf(buf, len, "Normal");
++ break;
++ case PM_STATE_FAULT:
++ (void)snprintf(buf, len, "Fault");
++ break;
++ default:
++ (void)snprintf(buf, len, "Unknown");
++ break;
++ }
++}
++
++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len)
++{
++ int i;
++ unsigned long state;
++ static unsigned long xprt_mask[] = {
++ XPRT_LOCKED, XPRT_CONNECTED,
++ XPRT_CONNECTING, XPRT_CLOSE_WAIT,
++ XPRT_BOUND, XPRT_BINDING, XPRT_CLOSING,
++ XPRT_CONGESTED};
++
++ static const char *const xprt_state_desc[] = {
++ "LOCKED", "CONNECTED", "CONNECTING",
++ "CLOSE_WAIT", "BOUND", "BINDING",
++ "CLOSING", "CONGESTED"};
++ int pos = 0;
++ int ret = 0;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ if ((buf == NULL) || (len <= 0)) {
++ enfs_log_error(
++ "Xprt state buffer is not valid, len=%d.\n",
++ len);
++ return;
++ }
++
++ xprt_get(xprt);
++ state = READ_ONCE(xprt->state);
++ xprt_put(xprt);
++
++ for (i = 0; i < ARRAY_SIZE(xprt_mask); ++i) {
++ if (pos >= len)
++ break;
++
++ if (!test_bit(xprt_mask[i], &state))
++ continue;
++
++ if (pos == 0)
++ ret = snprintf(buf, len, "%s", xprt_state_desc[i]);
++ else
++ ret = snprintf(buf + pos, len - pos, "|%s",
++ xprt_state_desc[i]);
++
++ if (ret < 0) {
++ enfs_log_error("format state failed, ret %d.\n", ret);
++ break;
++ }
++
++ pos += ret;
++ }
++}
+diff --git a/fs/nfs/enfs/pm_state.h b/fs/nfs/enfs/pm_state.h
+new file mode 100644
+index 000000000000..f5f52e5ab91d
+--- /dev/null
++++ b/fs/nfs/enfs/pm_state.h
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: path state header file
++ * Author: y00583252
++ * Create: 2023-08-12
++ */
++
++#ifndef PM_STATE_H
++#define PM_STATE_H
++
++#include <linux/types.h>
++#include <linux/sunrpc/xprt.h>
++
++enum pm_path_state {
++ PM_STATE_INIT,
++ PM_STATE_NORMAL,
++ PM_STATE_FAULT,
++ PM_STATE_UNDEFINED // xprt is not multipath xprt
++};
++
++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state);
++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt);
++
++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len);
++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len);
++
++#endif // PM_STATE_H
diff --git a/0006-add_enfs_compile_option.patch b/0006-add_enfs_compile_option.patch
new file mode 100644
index 0000000..ff3bc0e
--- /dev/null
+++ b/0006-add_enfs_compile_option.patch
@@ -0,0 +1,70 @@
+diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig
+index b04256636d4b..ae53510c0627 100644
+--- a/arch/arm64/configs/openeuler_defconfig
++++ b/arch/arm64/configs/openeuler_defconfig
+@@ -5344,6 +5344,7 @@ CONFIG_LOCKD=m
+ CONFIG_LOCKD_V4=y
+ CONFIG_NFS_ACL_SUPPORT=m
+ CONFIG_NFS_COMMON=y
++# CONFIG_ENFS is not set
+ CONFIG_SUNRPC=m
+ CONFIG_SUNRPC_GSS=m
+ CONFIG_SUNRPC_BACKCHANNEL=y
+diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig
+index 59baeb2973af..ccc317f7fdb2 100644
+--- a/arch/x86/configs/openeuler_defconfig
++++ b/arch/x86/configs/openeuler_defconfig
+@@ -6825,6 +6825,7 @@ CONFIG_LOCKD=m
+ CONFIG_LOCKD_V4=y
+ CONFIG_NFS_ACL_SUPPORT=m
+ CONFIG_NFS_COMMON=y
++CONFIG_ENFS=y
+ CONFIG_SUNRPC=m
+ CONFIG_SUNRPC_GSS=m
+ CONFIG_SUNRPC_BACKCHANNEL=y
+diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
+index e55f86713948..872c9b7671b1 100644
+--- a/fs/nfs/Kconfig
++++ b/fs/nfs/Kconfig
+@@ -196,3 +196,14 @@ config NFS_DEBUG
+ depends on NFS_FS && SUNRPC_DEBUG
+ select CRC32
+ default y
++
++config ENFS
++ tristate "NFS client support for ENFS"
++ depends on NFS_FS
++ default n
++ help
++ This option enables support multipath of the NFS protocol
++ in the kernel's NFS client.
++ This feature will improve performance and reliability.
++
++ If sure, say Y.
+diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
+index c587e3c4c6a6..19d0ac2ba3b8 100644
+--- a/fs/nfs/Makefile
++++ b/fs/nfs/Makefile
+@@ -12,6 +12,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
+ nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
+ nfs-$(CONFIG_SYSCTL) += sysctl.o
+ nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
++nfs-$(CONFIG_ENFS) += enfs_adapter.o
+
+ obj-$(CONFIG_NFS_V2) += nfsv2.o
+ nfsv2-y := nfs2super.o proc.o nfs2xdr.o
+@@ -34,3 +35,5 @@ nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o
+ obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
+ obj-$(CONFIG_PNFS_BLOCK) += blocklayout/
+ obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/
++
++obj-$(CONFIG_ENFS) += enfs/
+diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
+index 090658c3da12..fe4e3b28c5d1 100644
+--- a/net/sunrpc/Makefile
++++ b/net/sunrpc/Makefile
+@@ -19,3 +19,4 @@ sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
+ sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o
+ sunrpc-$(CONFIG_PROC_FS) += stats.o
+ sunrpc-$(CONFIG_SYSCTL) += sysctl.o
++sunrpc-$(CONFIG_ENFS) += sunrpc_enfs_adapter.o
--
2.25.0.windows.1
1
0

[PATCH openEuler-1.0-LTS] [just for review!!!!]Add feature: eNFS - nfs multipath to improve performance and reliability
by mingqian218472 25 Sep '23
by mingqian218472 25 Sep '23
25 Sep '23
From: 闫海涛 <yanhaitao2(a)huawei.com>
driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I7SVH7
---------------------------------
Currently, the NFS client can use only one server IP address at a single mount point. As a result, the hardware capability of multiple storage nodes and NICs cannot be fully utilized. In multiple financial sites, the performance cannot meet service requirements. In addition, when a single link is faulty, services are suspended. The reliability problem needs to be solved.
OpenEuler-based commercial OS vendors hope that the eNFS feature will be integrated into 20.03 SP4 to resolve performance and reliability problems.
When user mount one NFS share, can input localaddrs/remoteaddrs these two optional Parameters to use eNFS multipath. If these optional parameters are not used, NFS will behave as before. For example,
mount -t nfs -o [localaddrs=127.17.0.1-127.17.0.4],[remoteaddrs=127.17.1.1-127.17.1.4] xx.xx.xx.xx:/test /mnt/test
Changes in eNFS are as follows:
1. patch 0001:
At the NFS layer, the eNFS registration function is called back when the mount command parses parameters. The eNFS parses and saves the IP address list entered by users.
2. patch 0002:
At the sunrpc layer, the eNFS registration function is called back When the NFS uses sunrpc to create rpc_clnt, the eNFS combines the IP address list entered for mount to generate multiple xprts. When the I/O times out, the callback function of the eNFS is called back so that the eNFS switches to an available link for retry.
3. patch 0003:
The eNFS module registers the interface for parsing the mount command. During the mount process, the NFS invokes the eNFS interface to enable the eNFS to parse the mounting parameters of UltraPath. The eNFS module saves the mounting parameters to the context of nfs_client.
4. patch 0004:
When the NFS invokes the SunRPC to create rpc_clnt, the eNFS interface is called back. The eNFS creates multiple xprts based on the output IP address list. When NFS V3 I/Os are delivered, eNFS distributes I/Os to available links based on the link status, improving performance through load balancing.
5. patch 0005:
When sending I/Os from the SunRPC module to the NFS server times out, the SunRPC module calls back the eNFS module to reselect a link. The eNFS module distributes I/Os to other available links, preventing service interruption caused by a single link failure.
6. patch 0006:
The eNFS compilation option and makefile are added. By default, the eNFS compilation is not performed.
Signed-off-by: mingqian218472 <zhangmingqian.zhang(a)huawei.com>
---
...-nfs-multipath-to-improve-performanc.patch | 6148 +++++++++++++++++
...enfs_registe_and_handle_mount_option.patch | 757 ++
...nd_create_multipath_then_dispatch_IO.patch | 805 +++
...add_enfs_module_for_nfs_mount_option.patch | 1209 ++++
...dd_enfs_module_for_sunrpc_multipatch.patch | 1581 +++++
...le_for_sunrpc_failover_and_configure.patch | 1607 +++++
0006-add_enfs_compile_option.patch | 70 +
7 files changed, 12177 insertions(+)
create mode 100644 0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch
create mode 100644 0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
create mode 100644 0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
create mode 100644 0003-add_enfs_module_for_nfs_mount_option.patch
create mode 100644 0004-add_enfs_module_for_sunrpc_multipatch.patch
create mode 100644 0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
create mode 100644 0006-add_enfs_compile_option.patch
diff --git a/0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch b/0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch
new file mode 100644
index 0000000..2974c5f
--- /dev/null
+++ b/0001-Add-feature-eNFS-nfs-multipath-to-improve-performanc.patch
@@ -0,0 +1,6148 @@
+From 53f616b0a649494e33d30b250d06c4049ccb88be Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=E9=97=AB=E6=B5=B7=E6=B6=9B?= <yanhaitao2(a)huawei.com>
+Date: Mon, 25 Sep 2023 19:19:15 +0800
+Subject: [PATCH openEuler-20.03-LTS-SP3] Add feature: eNFS - nfs multipath to
+ improve performance and reliability
+
+driver inclusion
+category: feature
+bugzilla: https://gitee.com/openeuler/release-management/issues/I7U0W0
+
+---------------------------------
+
+Currently, the NFS client can use only one server IP address at a single mount point. As a result, the hardware capability of multiple storage nodes and NICs cannot be fully utilized. In multiple financial sites, the performance cannot meet service requirements. In addition, when a single link is faulty, services are suspended. The reliability problem needs to be solved.
+OpenEuler-based commercial OS vendors hope that the eNFS feature will be integrated into 20.03 SP4 to resolve performance and reliability problems.
+
+When user mount one NFS share, can input localaddrs/remoteaddrs these two optional Parameters to use eNFS multipath. If these optional parameters are not used, NFS will behave as before. For example,
+mount -t nfs -o [localaddrs=127.17.0.1-127.17.0.4],[remoteaddrs=127.17.1.1-127.17.1.4] xx.xx.xx.xx:/test /mnt/test
+
+Changes in eNFS are as follows:
+1. patch 0001:
+At the NFS layer, the eNFS registration function is called back when the mount command parses parameters. The eNFS parses and saves the IP address list entered by users.
+2. patch 0002:
+At the sunrpc layer, the eNFS registration function is called back When the NFS uses sunrpc to create rpc_clnt, the eNFS combines the IP address list entered for mount to generate multiple xprts. When the I/O times out, the callback function of the eNFS is called back so that the eNFS switches to an available link for retry.
+3. patch 0003:
+The eNFS module registers the interface for parsing the mount command. During the mount process, the NFS invokes the eNFS interface to enable the eNFS to parse the mounting parameters of UltraPath. The eNFS module saves the mounting parameters to the context of nfs_client.
+4. patch 0004:
+When the NFS invokes the SunRPC to create rpc_clnt, the eNFS interface is called back. The eNFS creates multiple xprts based on the output IP address list. When NFS V3 I/Os are delivered, eNFS distributes I/Os to available links based on the link status, improving performance through load balancing.
+5. patch 0005:
+When sending I/Os from the SunRPC module to the NFS server times out, the SunRPC module calls back the eNFS module to reselect a link. The eNFS module distributes I/Os to other available links, preventing service interruption caused by a single link failure.
+6. patch 0006:
+The eNFS compilation option and makefile are added. By default, the eNFS compilation is not performed.
+
+Signed-off-by: mingqian218472 <zhangmingqian.zhang(a)huawei.com>
+---
+ ...enfs_registe_and_handle_mount_option.patch | 757 ++++++++
+ ...nd_create_multipath_then_dispatch_IO.patch | 805 +++++++++
+ ...add_enfs_module_for_nfs_mount_option.patch | 1209 +++++++++++++
+ ...dd_enfs_module_for_sunrpc_multipatch.patch | 1581 ++++++++++++++++
+ ...le_for_sunrpc_failover_and_configure.patch | 1607 +++++++++++++++++
+ 0006-add_enfs_compile_option.patch | 70 +
+ kernel.spec | 13 +
+ 7 files changed, 6042 insertions(+)
+ create mode 100644 0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
+ create mode 100644 0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
+ create mode 100644 0003-add_enfs_module_for_nfs_mount_option.patch
+ create mode 100644 0004-add_enfs_module_for_sunrpc_multipatch.patch
+ create mode 100644 0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
+ create mode 100644 0006-add_enfs_compile_option.patch
+
+diff --git a/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
+new file mode 100644
+index 0000000..38e57a9
+--- /dev/null
++++ b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
+@@ -0,0 +1,757 @@
++diff --git a/fs/nfs/client.c b/fs/nfs/client.c
++index 7d02dc52209d..50820a8a684a 100644
++--- a/fs/nfs/client.c
+++++ b/fs/nfs/client.c
++@@ -48,7 +48,7 @@
++ #include "callback.h"
++ #include "delegation.h"
++ #include "iostat.h"
++-#include "internal.h"
+++#include "enfs_adapter.h"
++ #include "fscache.h"
++ #include "pnfs.h"
++ #include "nfs.h"
++@@ -255,6 +255,7 @@ void nfs_free_client(struct nfs_client *clp)
++ put_nfs_version(clp->cl_nfs_mod);
++ kfree(clp->cl_hostname);
++ kfree(clp->cl_acceptor);
+++ nfs_free_multi_path_client(clp);
++ kfree(clp);
++ }
++ EXPORT_SYMBOL_GPL(nfs_free_client);
++@@ -330,6 +331,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
++ sap))
++ continue;
++
+++ if (!nfs_multipath_client_match(clp, data))
+++ continue;
+++
++ refcount_inc(&clp->cl_count);
++ return clp;
++ }
++@@ -512,6 +516,9 @@ int nfs_create_rpc_client(struct nfs_client *clp,
++ .program = &nfs_program,
++ .version = clp->rpc_ops->version,
++ .authflavor = flavor,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .multipath_option = cl_init->enfs_option,
+++#endif
++ };
++
++ if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
++@@ -634,6 +641,13 @@ struct nfs_client *nfs_init_client(struct nfs_client *clp,
++ /* the client is already initialised */
++ if (clp->cl_cons_state == NFS_CS_READY)
++ return clp;
+++ error = nfs_create_multi_path_client(clp, cl_init);
+++ if (error < 0) {
+++ dprintk("%s: create failed.%d!\n", __func__, error);
+++ nfs_put_client(clp);
+++ clp = ERR_PTR(error);
+++ return clp;
+++ }
++
++ /*
++ * Create a client RPC handle for doing FSSTAT with UNIX auth only
++@@ -666,6 +680,9 @@ static int nfs_init_server(struct nfs_server *server,
++ .net = data->net,
++ .timeparms = &timeparms,
++ .init_flags = (1UL << NFS_CS_REUSEPORT),
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .enfs_option = data->enfs_option,
+++#endif
++ };
++ struct nfs_client *clp;
++ int error;
++diff --git a/fs/nfs/enfs_adapter.c b/fs/nfs/enfs_adapter.c
++new file mode 100644
++index 000000000000..7f471f2072c4
++--- /dev/null
+++++ b/fs/nfs/enfs_adapter.c
++@@ -0,0 +1,230 @@
+++// 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/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 <linux/sunrpc/sched.h>
+++#include <linux/nfs_iostat.h>
+++#include "enfs_adapter.h"
+++#include "iostat.h"
+++
+++struct enfs_adapter_ops __rcu *enfs_adapter;
+++
+++int enfs_adapter_register(struct enfs_adapter_ops *ops)
+++{
+++ struct enfs_adapter_ops *old;
+++
+++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, NULL, ops);
+++ if (old == NULL || old == ops)
+++ return 0;
+++ pr_err("regist %s ops %p failed. old %p\n", __func__, ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(enfs_adapter_register);
+++
+++int enfs_adapter_unregister(struct enfs_adapter_ops *ops)
+++{
+++ struct enfs_adapter_ops *old;
+++
+++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, ops, NULL);
+++ if (old == ops || old == NULL)
+++ return 0;
+++ pr_err("unregist %s ops %p failed. old %p\n", __func__, ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(enfs_adapter_unregister);
+++
+++struct enfs_adapter_ops *nfs_multipath_router_get(void)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ rcu_read_lock();
+++ ops = rcu_dereference(enfs_adapter);
+++ if (ops == NULL) {
+++ rcu_read_unlock();
+++ return NULL;
+++ }
+++ if (!try_module_get(ops->owner))
+++ ops = NULL;
+++ rcu_read_unlock();
+++ return ops;
+++}
+++
+++void nfs_multipath_router_put(struct enfs_adapter_ops *ops)
+++{
+++ if (ops)
+++ module_put(ops->owner);
+++}
+++
+++bool is_valid_option(enum nfsmultipathoptions option)
+++{
+++ if (option < REMOTEADDR || option >= INVALID_OPTION) {
+++ pr_warn("%s: ENFS invalid option %d\n", __func__, option);
+++ return false;
+++ }
+++
+++ return true;
+++}
+++
+++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
+++ struct nfs_parsed_mount_data *mnt)
+++{
+++
+++ //parseMultiPathOptions(getNfsMultiPathOpt(token), string, mnt);
+++
+++ int rc;
+++ struct enfs_adapter_ops *ops;
+++
+++ ops = nfs_multipath_router_get();
+++ if ((ops == NULL) || (ops->parse_mount_options == NULL) ||
+++ !is_valid_option(option)) {
+++ nfs_multipath_router_put(ops);
+++ dfprintk(MOUNT,
+++ "NFS: parsing nfs mount option enfs not load[%s]\n"
+++ , __func__);
+++ return -EOPNOTSUPP;
+++ }
+++ // nfs_multipath_parse_options
+++ dfprintk(MOUNT, "NFS: parsing nfs mount option '%s' type: %d[%s]\n"
+++ , str, option, __func__);
+++ rc = ops->parse_mount_options(option, str, &mnt->enfs_option, mnt->net);
+++ nfs_multipath_router_put(ops);
+++ return rc;
+++}
+++
+++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ if (data->enfs_option == NULL)
+++ return;
+++
+++ ops = nfs_multipath_router_get();
+++ if ((ops == NULL) || (ops->free_mount_options == NULL)) {
+++ nfs_multipath_router_put(ops);
+++ return;
+++ }
+++ ops->free_mount_options((void *)&data->enfs_option);
+++ nfs_multipath_router_put(ops);
+++}
+++
+++int nfs_create_multi_path_client(struct nfs_client *client,
+++ const struct nfs_client_initdata *cl_init)
+++{
+++ int ret = 0;
+++ struct enfs_adapter_ops *ops;
+++
+++ if (cl_init->enfs_option == NULL)
+++ return 0;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_init != NULL)
+++ ret = ops->client_info_init(
+++ (void *)&client->cl_multipath_data, cl_init);
+++ nfs_multipath_router_put(ops);
+++
+++ return ret;
+++}
+++EXPORT_SYMBOL_GPL(nfs_create_multi_path_client);
+++
+++void nfs_free_multi_path_client(struct nfs_client *clp)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ if (clp->cl_multipath_data == NULL)
+++ return;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_free != NULL)
+++ ops->client_info_free(clp->cl_multipath_data);
+++ nfs_multipath_router_put(ops);
+++}
+++
+++int nfs_multipath_client_match(struct nfs_client *clp,
+++ const struct nfs_client_initdata *sap)
+++{
+++ int ret = true;
+++ struct enfs_adapter_ops *ops;
+++
+++ pr_info("%s src %p dst %p\n.", __func__,
+++ clp->cl_multipath_data, sap->enfs_option);
+++
+++ if (clp->cl_multipath_data == NULL && sap->enfs_option == NULL)
+++ return true;
+++
+++ if ((clp->cl_multipath_data == NULL && sap->enfs_option) ||
+++ (clp->cl_multipath_data && sap->enfs_option == NULL)) {
+++ pr_err("not match client src %p dst %p\n.",
+++ clp->cl_multipath_data, sap->enfs_option);
+++ return false;
+++ }
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_match != NULL)
+++ ret = ops->client_info_match(clp->cl_multipath_data,
+++ sap->enfs_option);
+++ nfs_multipath_router_put(ops);
+++
+++ return ret;
+++}
+++
+++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst)
+++{
+++ int ret = true;
+++ struct enfs_adapter_ops *ops;
+++
+++ if (src->cl_multipath_data == NULL && dst->cl_multipath_data == NULL)
+++ return true;
+++
+++ if (src->cl_multipath_data == NULL || dst->cl_multipath_data == NULL)
+++ return false;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->nfs4_client_info_match != NULL)
+++ ret = ops->nfs4_client_info_match(src->cl_multipath_data,
+++ src->cl_multipath_data);
+++ nfs_multipath_router_put(ops);
+++
+++ return ret;
+++}
+++EXPORT_SYMBOL_GPL(nfs4_multipath_client_match);
+++
+++void nfs_multipath_show_client_info(struct seq_file *mount_option,
+++ struct nfs_server *server)
+++{
+++ struct enfs_adapter_ops *ops;
+++
+++ if (mount_option == NULL || server == NULL ||
+++ server->client == NULL ||
+++ server->nfs_client->cl_multipath_data == NULL)
+++ return;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->client_info_show != NULL)
+++ ops->client_info_show(mount_option, server);
+++ nfs_multipath_router_put(ops);
+++}
+++
+++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option)
+++{
+++ int ret = 0;
+++ struct enfs_adapter_ops *ops;
+++
+++ if (nfs_client == NULL || nfs_client->cl_rpcclient == NULL)
+++ return 0;
+++
+++ ops = nfs_multipath_router_get();
+++ if (ops != NULL && ops->remount_ip_list != NULL)
+++ ret = ops->remount_ip_list(nfs_client, enfs_option);
+++ nfs_multipath_router_put(ops);
+++ return ret;
+++}
+++EXPORT_SYMBOL_GPL(nfs_remount_iplist);
++diff --git a/fs/nfs/enfs_adapter.h b/fs/nfs/enfs_adapter.h
++new file mode 100644
++index 000000000000..752544e18056
++--- /dev/null
+++++ b/fs/nfs/enfs_adapter.h
++@@ -0,0 +1,101 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Client-side ENFS adapt header.
+++ *
+++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+++ */
+++#ifndef _NFS_MULTIPATH_H_
+++#define _NFS_MULTIPATH_H_
+++
+++#include "internal.h"
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++enum nfsmultipathoptions {
+++ REMOTEADDR,
+++ LOCALADDR,
+++ REMOTEDNSNAME,
+++ REMOUNTREMOTEADDR,
+++ REMOUNTLOCALADDR,
+++ INVALID_OPTION
+++};
+++
+++
+++struct enfs_adapter_ops {
+++ const char *name;
+++ struct module *owner;
+++ int (*parse_mount_options)(enum nfsmultipathoptions option,
+++ char *str, void **enfs_option, struct net *net_ns);
+++
+++ void (*free_mount_options)(void **data);
+++
+++ int (*client_info_init)(void **data,
+++ const struct nfs_client_initdata *cl_init);
+++ void (*client_info_free)(void *data);
+++ int (*client_info_match)(void *src, void *dst);
+++ int (*nfs4_client_info_match)(void *src, void *dst);
+++ void (*client_info_show)(struct seq_file *mount_option, void *data);
+++ int (*remount_ip_list)(struct nfs_client *nfs_client,
+++ void *enfs_option);
+++};
+++
+++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
+++ struct nfs_parsed_mount_data *mnt);
+++void enfs_free_mount_options(struct nfs_parsed_mount_data *data);
+++int nfs_create_multi_path_client(struct nfs_client *client,
+++ const struct nfs_client_initdata *cl_init);
+++void nfs_free_multi_path_client(struct nfs_client *clp);
+++int nfs_multipath_client_match(struct nfs_client *clp,
+++ const struct nfs_client_initdata *sap);
+++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst);
+++void nfs_multipath_show_client_info(struct seq_file *mount_option,
+++ struct nfs_server *server);
+++int enfs_adapter_register(struct enfs_adapter_ops *ops);
+++int enfs_adapter_unregister(struct enfs_adapter_ops *ops);
+++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option);
+++int nfs4_create_multi_path(struct nfs_server *server,
+++ struct nfs_parsed_mount_data *data,
+++ const struct rpc_timeout *timeparms);
+++
+++#else
+++static inline
+++void nfs_free_multi_path_client(struct nfs_client *clp)
+++{
+++
+++}
+++
+++static inline
+++int nfs_multipath_client_match(struct nfs_client *clp,
+++ const struct nfs_client_initdata *sap)
+++{
+++ return 1;
+++}
+++
+++static inline
+++int nfs_create_multi_path_client(struct nfs_client *client,
+++ const struct nfs_client_initdata *cl_init)
+++{
+++ return 0;
+++}
+++
+++static inline
+++void nfs_multipath_show_client_info(struct seq_file *mount_option,
+++ struct nfs_server *server)
+++{
+++
+++}
+++
+++static inline
+++int nfs4_multipath_client_match(struct nfs_client *src,
+++ struct nfs_client *dst)
+++{
+++ return 1;
+++}
+++
+++static inline
+++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
+++{
+++
+++}
+++
+++#endif // CONFIG_ENFS
+++#endif // _NFS_MULTIPATH_H_
++diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
++index 0ce5a90640c4..c696693edc7b 100644
++--- a/fs/nfs/internal.h
+++++ b/fs/nfs/internal.h
++@@ -93,6 +93,9 @@ struct nfs_client_initdata {
++ u32 minorversion;
++ struct net *net;
++ const struct rpc_timeout *timeparms;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ void *enfs_option; /* struct multipath_mount_options * */
+++#endif
++ };
++
++ /*
++@@ -135,6 +138,9 @@ struct nfs_parsed_mount_data {
++
++ struct security_mnt_opts lsm_opts;
++ struct net *net;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ void *enfs_option; /* struct multipath_mount_options * */
+++#endif
++ };
++
++ /* mount_clnt.c */
++diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
++index 1350ea673672..4aa6e1f961f7 100644
++--- a/fs/nfs/nfs4client.c
+++++ b/fs/nfs/nfs4client.c
++@@ -10,7 +10,7 @@
++ #include <linux/sunrpc/xprt.h>
++ #include <linux/sunrpc/bc_xprt.h>
++ #include <linux/sunrpc/rpc_pipe_fs.h>
++-#include "internal.h"
+++#include "enfs_adapter.h"
++ #include "callback.h"
++ #include "delegation.h"
++ #include "nfs4session.h"
++@@ -225,6 +225,16 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
++ __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
++ __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ err = nfs_create_multi_path_client(clp, cl_init);
+++ if (err < 0) {
+++ dprintk("%s: create failed.%d\n", __func__, err);
+++ nfs_put_client(clp);
+++ clp = ERR_PTR(err);
+++ return clp;
+++ }
+++#endif
+++
++ /*
++ * Set up the connection to the server before we add add to the
++ * global list.
++@@ -529,6 +539,9 @@ static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new,
++ if (!nfs4_match_client_owner_id(pos, new))
++ return 1;
++
+++ if (!nfs4_multipath_client_match(pos, new))
+++ return 1;
+++
++ return 0;
++ }
++
++@@ -860,7 +873,7 @@ static int nfs4_set_client(struct nfs_server *server,
++ const size_t addrlen,
++ const char *ip_addr,
++ int proto, const struct rpc_timeout *timeparms,
++- u32 minorversion, struct net *net)
+++ u32 minorversion, struct net *net, void *enfs_option)
++ {
++ struct nfs_client_initdata cl_init = {
++ .hostname = hostname,
++@@ -872,6 +885,9 @@ static int nfs4_set_client(struct nfs_server *server,
++ .minorversion = minorversion,
++ .net = net,
++ .timeparms = timeparms,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .enfs_option = enfs_option,
+++#endif
++ };
++ struct nfs_client *clp;
++
++@@ -1042,6 +1058,30 @@ static int nfs4_server_common_setup(struct nfs_server *server,
++ return error;
++ }
++
+++int nfs4_create_multi_path(struct nfs_server *server,
+++ struct nfs_parsed_mount_data *data,
+++ const struct rpc_timeout *timeparms)
+++{
+++ struct nfs_client_initdata cl_init = {
+++ .hostname = data->nfs_server.hostname,
+++ .addr = (const struct sockaddr *)&data->nfs_server.address,
+++ .addrlen = data->nfs_server.addrlen,
+++ .ip_addr = data->client_address,
+++ .nfs_mod = &nfs_v4,
+++ .proto = data->nfs_server.protocol,
+++ .minorversion = data->minorversion,
+++ .net = data->net,
+++ .timeparms = timeparms,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ .enfs_option = data->enfs_option,
+++#endif // CONFIG_ENFS
+++ };
+++
+++ return nfs_create_multi_path_client(server->nfs_client, &cl_init);
+++
+++}
+++EXPORT_SYMBOL_GPL(nfs4_create_multi_path);
+++
++ /*
++ * Create a version 4 volume record
++ */
++@@ -1050,6 +1090,7 @@ static int nfs4_init_server(struct nfs_server *server,
++ {
++ struct rpc_timeout timeparms;
++ int error;
+++ void *enfs_option = NULL;
++
++ nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
++ data->timeo, data->retrans);
++@@ -1067,6 +1108,10 @@ static int nfs4_init_server(struct nfs_server *server,
++ else
++ data->selected_flavor = RPC_AUTH_UNIX;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ enfs_option = data->enfs_option;
+++#endif
+++
++ /* Get a client record */
++ error = nfs4_set_client(server,
++ data->nfs_server.hostname,
++@@ -1076,7 +1121,7 @@ static int nfs4_init_server(struct nfs_server *server,
++ data->nfs_server.protocol,
++ &timeparms,
++ data->minorversion,
++- data->net);
+++ data->net, enfs_option);
++ if (error < 0)
++ return error;
++
++@@ -1161,7 +1206,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
++ XPRT_TRANSPORT_RDMA,
++ parent_server->client->cl_timeout,
++ parent_client->cl_mvops->minor_version,
++- parent_client->cl_net);
+++ parent_client->cl_net, NULL);
++ if (!error)
++ goto init_server;
++ #endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
++@@ -1174,7 +1219,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
++ XPRT_TRANSPORT_TCP,
++ parent_server->client->cl_timeout,
++ parent_client->cl_mvops->minor_version,
++- parent_client->cl_net);
+++ parent_client->cl_net, NULL);
++ if (error < 0)
++ goto error;
++
++@@ -1269,7 +1314,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
++ set_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
++ error = nfs4_set_client(server, hostname, sap, salen, buf,
++ clp->cl_proto, clnt->cl_timeout,
++- clp->cl_minorversion, net);
+++ clp->cl_minorversion, net, NULL);
++ clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
++ if (error != 0) {
++ nfs_server_insert_lists(server);
++diff --git a/fs/nfs/super.c b/fs/nfs/super.c
++index a05e1eb2c3fd..83cd294aca15 100644
++--- a/fs/nfs/super.c
+++++ b/fs/nfs/super.c
++@@ -61,7 +61,7 @@
++ #include "callback.h"
++ #include "delegation.h"
++ #include "iostat.h"
++-#include "internal.h"
+++#include "enfs_adapter.h"
++ #include "fscache.h"
++ #include "nfs4session.h"
++ #include "pnfs.h"
++@@ -113,6 +113,12 @@ enum {
++
++ /* Special mount options */
++ Opt_userspace, Opt_deprecated, Opt_sloppy,
+++#if IS_ENABLED(CONFIG_ENFS)
+++ Opt_remote_iplist,
+++ Opt_local_iplist,
+++ Opt_remote_dnslist,
+++ Opt_enfs_info,
+++#endif
++
++ Opt_err
++ };
++@@ -183,6 +189,13 @@ static const match_table_t nfs_mount_option_tokens = {
++ { Opt_fscache_uniq, "fsc=%s" },
++ { Opt_local_lock, "local_lock=%s" },
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ { Opt_remote_iplist, "remoteaddrs=%s" },
+++ { Opt_local_iplist, "localaddrs=%s" },
+++ { Opt_remote_dnslist, "remotednsname=%s" },
+++ { Opt_enfs_info, "enfs_info=%s" },
+++#endif
+++
++ /* The following needs to be listed after all other options */
++ { Opt_nfsvers, "v%s" },
++
++@@ -365,6 +378,21 @@ static struct shrinker acl_shrinker = {
++ .seeks = DEFAULT_SEEKS,
++ };
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++enum nfsmultipathoptions getNfsMultiPathOpt(int token)
+++{
+++ switch (token) {
+++ case Opt_remote_iplist:
+++ return REMOUNTREMOTEADDR;
+++ case Opt_local_iplist:
+++ return REMOUNTLOCALADDR;
+++ case Opt_remote_dnslist:
+++ return REMOTEDNSNAME;
+++ }
+++ return INVALID_OPTION;
+++}
+++#endif
+++
++ /*
++ * Register the NFS filesystems
++ */
++@@ -758,6 +786,9 @@ int nfs_show_options(struct seq_file *m, struct dentry *root)
++ seq_printf(m, ",addr=%s",
++ rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient,
++ RPC_DISPLAY_ADDR));
+++
+++ nfs_multipath_show_client_info(m, nfss);
+++
++ rcu_read_unlock();
++
++ return 0;
++@@ -853,6 +884,8 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
++ seq_puts(m, root->d_sb->s_flags & SB_NODIRATIME ? ",nodiratime" : "");
++ nfs_show_mount_options(m, nfss, 1);
++
+++ nfs_multipath_show_client_info(m, nfss);
+++
++ seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
++
++ show_implementation_id(m, nfss);
++@@ -977,6 +1010,7 @@ static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data)
++ kfree(data->nfs_server.export_path);
++ kfree(data->nfs_server.hostname);
++ kfree(data->fscache_uniq);
+++ enfs_free_mount_options(data);
++ security_free_mnt_opts(&data->lsm_opts);
++ kfree(data);
++ }
++@@ -1641,7 +1675,34 @@ static int nfs_parse_mount_options(char *raw,
++ return 0;
++ };
++ break;
++-
+++#if IS_ENABLED(CONFIG_ENFS)
+++ case Opt_remote_iplist:
+++ case Opt_local_iplist:
+++ case Opt_remote_dnslist:
+++ string = match_strdup(args);
+++ if (string == NULL)
+++ goto out_nomem;
+++ rc = enfs_parse_mount_options(getNfsMultiPathOpt(token),
+++ string, mnt);
+++ kfree(string);
+++ switch (rc) {
+++ case 0:
+++ break;
+++ case -ENOMEM:
+++ goto out_nomem;
+++ case -ENOSPC:
+++ goto out_limit;
+++ case -EINVAL:
+++ goto out_invalid_address;
+++ case -ENOTSUPP:
+++ goto out_invalid_address;
+++ case -EOPNOTSUPP:
+++ goto out_invalid_address;
+++ }
+++ break;
+++ case Opt_enfs_info:
+++ break;
+++#endif
++ /*
++ * Special options
++ */
++@@ -1720,6 +1781,11 @@ static int nfs_parse_mount_options(char *raw,
++ free_secdata(secdata);
++ printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
++ return 0;
+++#if IS_ENABLED(CONFIG_ENFS)
+++out_limit:
+++ dprintk("NFS: param is more than supported limit: %d\n", rc);
+++ return 0;
+++#endif
++ }
++
++ /*
++@@ -2335,6 +2401,14 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
++ if (!nfs_parse_mount_options((char *)options, data))
++ goto out;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ if (data->enfs_option) {
+++ error = nfs_remount_iplist(nfss->nfs_client, data->enfs_option);
+++ if (error)
+++ goto out;
+++ }
+++#endif
+++
++ /*
++ * noac is a special case. It implies -o sync, but that's not
++ * necessarily reflected in the mtab options. do_remount_sb
++@@ -2347,6 +2421,11 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
++ /* compare new mount options with old ones */
++ error = nfs_compare_remount_data(nfss, data);
++ out:
+++#if IS_ENABLED(CONFIG_ENFS)
+++ /* release remount option member */
+++ if (data->enfs_option)
+++ enfs_free_mount_options(data);
+++#endif
++ nfs_free_parsed_mount_data(data);
++ return error;
++ }
++diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
++index 7023ae64e3d7..2c19678afe8d 100644
++--- a/include/linux/nfs_fs_sb.h
+++++ b/include/linux/nfs_fs_sb.h
++@@ -123,6 +123,11 @@ struct nfs_client {
++
++ struct net *cl_net;
++ struct list_head pending_cb_stateids;
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ /* multi path private structure (struct multipath_client_info *) */
+++ void *cl_multipath_data;
+++#endif
++ };
++
++ /*
+diff --git a/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
+new file mode 100644
+index 0000000..540a2ce
+--- /dev/null
++++ b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
+@@ -0,0 +1,805 @@
++diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
++index 8aa865bce4f6..89178f78de8c 100644
++--- a/include/linux/sunrpc/clnt.h
+++++ b/include/linux/sunrpc/clnt.h
++@@ -70,6 +70,10 @@ struct rpc_clnt {
++ struct dentry *cl_debugfs; /* debugfs directory */
++ #endif
++ struct rpc_xprt_iter cl_xpi;
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ bool cl_enfs;
+++#endif
++ };
++
++ /*
++@@ -124,6 +128,9 @@ struct rpc_create_args {
++ unsigned long flags;
++ char *client_name;
++ struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */
+++#if IS_ENABLED(CONFIG_ENFS)
+++ void *multipath_option;
+++#endif
++ };
++
++ struct rpc_add_xprt_test {
++@@ -221,6 +228,12 @@ bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
++ const struct sockaddr *sap);
++ void rpc_cleanup_clids(void);
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++int
+++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+++ const struct rpc_call_ops *ops, void *data, int flags);
+++#endif /* CONFIG_ENFS */
+++
++ static inline int rpc_reply_expected(struct rpc_task *task)
++ {
++ return (task->tk_msg.rpc_proc != NULL) &&
++diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
++index ad2e243f3f03..124f5a0faf3e 100644
++--- a/include/linux/sunrpc/sched.h
+++++ b/include/linux/sunrpc/sched.h
++@@ -90,6 +90,9 @@ struct rpc_task {
++ tk_garb_retry : 2,
++ tk_cred_retry : 2,
++ tk_rebind_retry : 2;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ unsigned long tk_major_timeo; /* major timeout ticks */
+++#endif
++ };
++
++ typedef void (*rpc_action)(struct rpc_task *);
++@@ -118,6 +121,9 @@ struct rpc_task_setup {
++ */
++ #define RPC_TASK_ASYNC 0x0001 /* is an async task */
++ #define RPC_TASK_SWAPPER 0x0002 /* is swapping in/out */
+++#if IS_ENABLED(CONFIG_ENFS)
+++#define RPC_TASK_FIXED 0x0004 /* detect xprt status task */
+++#endif
++ #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */
++ #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */
++ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
++@@ -257,6 +263,9 @@ void rpc_destroy_mempool(void);
++ extern struct workqueue_struct *rpciod_workqueue;
++ extern struct workqueue_struct *xprtiod_workqueue;
++ void rpc_prepare_task(struct rpc_task *task);
+++#if IS_ENABLED(CONFIG_ENFS)
+++void rpc_init_task_retry_counters(struct rpc_task *task);
+++#endif
++
++ static inline int rpc_wait_for_completion_task(struct rpc_task *task)
++ {
++diff --git a/include/linux/sunrpc/sunrpc_enfs_adapter.h b/include/linux/sunrpc/sunrpc_enfs_adapter.h
++new file mode 100644
++index 000000000000..28abedcf5cf6
++--- /dev/null
+++++ b/include/linux/sunrpc/sunrpc_enfs_adapter.h
++@@ -0,0 +1,128 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/* Client-side SUNRPC ENFS adapter header.
+++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+++ */
+++#ifndef _SUNRPC_ENFS_ADAPTER_H_
+++#define _SUNRPC_ENFS_ADAPTER_H_
+++#include <linux/sunrpc/clnt.h>
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++
+++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
+++{
+++ xps->xps_nactive--;
+++}
+++
+++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
+++{
+++ xps->xps_nactive--;
+++}
+++
+++struct rpc_xprt *rpc_task_get_xprt
+++(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
+++
+++struct rpc_multipath_ops {
+++ struct module *owner;
+++ void (*create_clnt)(struct rpc_create_args *args,
+++ struct rpc_clnt *clnt);
+++ void (*releas_clnt)(struct rpc_clnt *clnt);
+++ void (*create_xprt)(struct rpc_xprt *xprt);
+++ void (*destroy_xprt)(struct rpc_xprt *xprt);
+++ void (*xprt_iostat)(struct rpc_task *task);
+++ void (*failover_handle)(struct rpc_task *task);
+++ bool (*task_need_call_start_again)(struct rpc_task *task);
+++ void (*adjust_task_timeout)(struct rpc_task *task, void *condition);
+++ void (*init_task_req)(struct rpc_task *task, struct rpc_rqst *req);
+++ bool (*prepare_transmit)(struct rpc_task *task);
+++};
+++
+++extern struct rpc_multipath_ops __rcu *multipath_ops;
+++void rpc_init_task_retry_counters(struct rpc_task *task);
+++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops);
+++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops);
+++struct rpc_multipath_ops *rpc_multipath_ops_get(void);
+++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops);
+++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
+++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
+++ struct rpc_clnt *clnt);
+++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt);
+++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt);
+++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt);
+++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task);
+++void rpc_multipath_ops_failover_handle(struct rpc_task *task);
+++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task);
+++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
+++ void *condition);
+++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
+++ struct rpc_rqst *req);
+++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task);
+++
+++#else
+++static inline struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt)
+++{
+++ return NULL;
+++}
+++
+++static inline void rpc_task_release_xprt(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt)
+++{
+++}
+++
+++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
+++{
+++}
+++
+++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_create_clnt
+++(struct rpc_create_args *args, struct rpc_clnt *clnt)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
+++{
+++}
+++
+++static inline bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
+++{
+++ return false;
+++}
+++
+++static inline void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
+++{
+++}
+++
+++static inline void rpc_multipath_ops_failover_handle(struct rpc_task *task)
+++{
+++}
+++
+++static inline
+++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
+++{
+++ return false;
+++}
+++
+++static inline void
+++rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task, void *condition)
+++{
+++}
+++
+++static inline void
+++rpc_multipath_ops_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
+++{
+++}
+++
+++static inline bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
+++{
+++ return false;
+++}
+++
+++#endif
+++#endif // _SUNRPC_ENFS_ADAPTER_H_
++diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
++index ccfacca1eba9..2e47b3577947 100644
++--- a/include/linux/sunrpc/xprt.h
+++++ b/include/linux/sunrpc/xprt.h
++@@ -279,6 +279,10 @@ struct rpc_xprt {
++ atomic_t inject_disconnect;
++ #endif
++ struct rcu_head rcu;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ atomic_long_t queuelen;
+++ void *multipath_context;
+++#endif
++ };
++
++ #if defined(CONFIG_SUNRPC_BACKCHANNEL)
++diff --git a/include/linux/sunrpc/xprtmultipath.h b/include/linux/sunrpc/xprtmultipath.h
++index af1257c030d2..d54e4dbbbf34 100644
++--- a/include/linux/sunrpc/xprtmultipath.h
+++++ b/include/linux/sunrpc/xprtmultipath.h
++@@ -22,6 +22,10 @@ struct rpc_xprt_switch {
++ const struct rpc_xprt_iter_ops *xps_iter_ops;
++
++ struct rcu_head xps_rcu;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ unsigned int xps_nactive;
+++ atomic_long_t xps_queuelen;
+++#endif
++ };
++
++ struct rpc_xprt_iter {
++@@ -69,4 +73,8 @@ extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
++
++ extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
++ const struct sockaddr *sap);
+++#if IS_ENABLED(CONFIG_ENFS)
+++extern void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+++ struct rpc_xprt *xprt);
+++#endif
++ #endif
++diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
++index 0fc540b0d183..d7ffee637148 100644
++--- a/net/sunrpc/clnt.c
+++++ b/net/sunrpc/clnt.c
++@@ -37,6 +37,7 @@
++ #include <linux/sunrpc/rpc_pipe_fs.h>
++ #include <linux/sunrpc/metrics.h>
++ #include <linux/sunrpc/bc_xprt.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++ #include <trace/events/sunrpc.h>
++
++ #include "sunrpc.h"
++@@ -490,6 +491,8 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
++ }
++ }
++
+++ rpc_multipath_ops_create_clnt(args, clnt);
+++
++ clnt->cl_softrtry = 1;
++ if (args->flags & RPC_CLNT_CREATE_HARDRTRY)
++ clnt->cl_softrtry = 0;
++@@ -869,6 +872,8 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
++ list_empty(&clnt->cl_tasks), 1*HZ);
++ }
++
+++ rpc_multipath_ops_releas_clnt(clnt);
+++
++ rpc_release_client(clnt);
++ }
++ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
++@@ -981,7 +986,13 @@ void rpc_task_release_transport(struct rpc_task *task)
++
++ if (xprt) {
++ task->tk_xprt = NULL;
++- xprt_put(xprt);
+++#if IS_ENABLED(CONFIG_ENFS)
+++ if (task->tk_client) {
+++ rpc_task_release_xprt(task->tk_client, xprt);
+++ return;
+++ }
+++#endif
+++ xprt_put(xprt);
++ }
++ }
++ EXPORT_SYMBOL_GPL(rpc_task_release_transport);
++@@ -990,6 +1001,10 @@ void rpc_task_release_client(struct rpc_task *task)
++ {
++ struct rpc_clnt *clnt = task->tk_client;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ rpc_task_release_transport(task);
+++#endif
+++
++ if (clnt != NULL) {
++ /* Remove from client task list */
++ spin_lock(&clnt->cl_lock);
++@@ -999,14 +1014,29 @@ void rpc_task_release_client(struct rpc_task *task)
++
++ rpc_release_client(clnt);
++ }
+++#if IS_ENABLED(CONFIG_ENFS)
+++#else
++ rpc_task_release_transport(task);
+++#endif
++ }
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++static struct rpc_xprt *
+++rpc_task_get_next_xprt(struct rpc_clnt *clnt)
+++{
+++ return rpc_task_get_xprt(clnt, xprt_iter_get_next(&clnt->cl_xpi));
+++}
+++#endif
+++
++ static
++ void rpc_task_set_transport(struct rpc_task *task, struct rpc_clnt *clnt)
++ {
++ if (!task->tk_xprt)
+++#if IS_ENABLED(CONFIG_ENFS)
+++ task->tk_xprt = rpc_task_get_next_xprt(clnt);
+++#else
++ task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
+++#endif
++ }
++
++ static
++@@ -1597,6 +1627,14 @@ call_reserveresult(struct rpc_task *task)
++ return;
++ case -EIO: /* probably a shutdown */
++ break;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ case -ETIMEDOUT: /* woken up; restart */
+++ if (rpc_multipath_ops_task_need_call_start_again(task)) {
+++ rpc_task_release_transport(task);
+++ task->tk_action = call_start;
+++ return;
+++ }
+++#endif
++ default:
++ printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
++ __func__, status);
++@@ -1962,6 +2000,10 @@ call_transmit(struct rpc_task *task)
++ return;
++ if (!xprt_prepare_transmit(task))
++ return;
+++
+++ if (rpc_multipath_ops_prepare_transmit(task))
+++ return;
+++
++ task->tk_action = call_transmit_status;
++ /* Encode here so that rpcsec_gss can use correct sequence number. */
++ if (rpc_task_need_encode(task)) {
++@@ -2277,6 +2319,9 @@ call_timeout(struct rpc_task *task)
++
++ retry:
++ task->tk_action = call_bind;
+++#if IS_ENABLED(CONFIG_ENFS)
+++ rpc_multipath_ops_failover_handle(task);
+++#endif
++ task->tk_status = 0;
++ }
++
++@@ -2961,3 +3006,30 @@ rpc_clnt_swap_deactivate(struct rpc_clnt *clnt)
++ }
++ EXPORT_SYMBOL_GPL(rpc_clnt_swap_deactivate);
++ #endif /* CONFIG_SUNRPC_SWAP */
+++
+++#if IS_ENABLED(CONFIG_ENFS)
+++/* rpc_clnt_test_xprt - Test and add a new transport to a rpc_clnt
+++ * @clnt: pointer to struct rpc_clnt
+++ * @xprt: pointer struct rpc_xprt
+++ * @ops: async operation
+++ */
+++int
+++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+++ const struct rpc_call_ops *ops, void *data, int flags)
+++{
+++ struct rpc_cred *cred;
+++ struct rpc_task *task;
+++
+++ cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+++ task = rpc_call_null_helper(clnt, xprt, cred,
+++ RPC_TASK_SOFT | RPC_TASK_SOFTCONN | flags,
+++ ops, data);
+++ put_rpccred(cred);
+++ if (IS_ERR(task))
+++ return PTR_ERR(task);
+++
+++ rpc_put_task(task);
+++ return 1;
+++}
+++EXPORT_SYMBOL_GPL(rpc_clnt_test_xprt);
+++#endif
++diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
++index a873c92a4898..2254fea0e863 100644
++--- a/net/sunrpc/sched.c
+++++ b/net/sunrpc/sched.c
++@@ -20,7 +20,7 @@
++ #include <linux/mutex.h>
++ #include <linux/freezer.h>
++
++-#include <linux/sunrpc/clnt.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++ #include "sunrpc.h"
++
++@@ -962,7 +962,12 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
++ /* Initialize workqueue for async tasks */
++ task->tk_workqueue = task_setup_data->workqueue;
++
+++#if IS_ENABLED(CONFIG_ENFS)
+++ task->tk_xprt = rpc_task_get_xprt(task_setup_data->rpc_client,
+++ xprt_get(task_setup_data->rpc_xprt));
+++#else
++ task->tk_xprt = xprt_get(task_setup_data->rpc_xprt);
+++#endif
++
++ if (task->tk_ops->rpc_call_prepare != NULL)
++ task->tk_action = rpc_prepare_task;
++diff --git a/net/sunrpc/sunrpc_enfs_adapter.c b/net/sunrpc/sunrpc_enfs_adapter.c
++new file mode 100644
++index 000000000000..c1543545c6de
++--- /dev/null
+++++ b/net/sunrpc/sunrpc_enfs_adapter.c
++@@ -0,0 +1,214 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/* Client-side SUNRPC ENFS adapter header.
+++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+++ */
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+++
+++struct rpc_multipath_ops __rcu *multipath_ops;
+++
+++void rpc_init_task_retry_counters(struct rpc_task *task)
+++{
+++ /* Initialize retry counters */
+++ task->tk_garb_retry = 2;
+++ task->tk_cred_retry = 2;
+++ task->tk_rebind_retry = 2;
+++}
+++EXPORT_SYMBOL_GPL(rpc_init_task_retry_counters);
+++
+++struct rpc_xprt *
+++rpc_task_get_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+++{
+++ struct rpc_xprt_switch *xps;
+++
+++ if (!xprt)
+++ return NULL;
+++ rcu_read_lock();
+++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+++ atomic_long_inc(&xps->xps_queuelen);
+++ rcu_read_unlock();
+++ atomic_long_inc(&xprt->queuelen);
+++
+++ return xprt;
+++}
+++
+++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops)
+++{
+++ struct rpc_multipath_ops *old;
+++
+++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, NULL, ops);
+++ if (!old || old == ops)
+++ return 0;
+++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_register);
+++
+++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops)
+++{
+++ struct rpc_multipath_ops *old;
+++
+++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, ops, NULL);
+++ if (!old || old == ops)
+++ return 0;
+++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
+++ return -EPERM;
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_unregister);
+++
+++struct rpc_multipath_ops *rpc_multipath_ops_get(void)
+++{
+++ struct rpc_multipath_ops *ops;
+++
+++ rcu_read_lock();
+++ ops = rcu_dereference(multipath_ops);
+++ if (!ops) {
+++ rcu_read_unlock();
+++ return NULL;
+++ }
+++ if (!try_module_get(ops->owner))
+++ ops = NULL;
+++ rcu_read_unlock();
+++ return ops;
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_get);
+++
+++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops)
+++{
+++ if (ops)
+++ module_put(ops->owner);
+++}
+++EXPORT_SYMBOL_GPL(rpc_multipath_ops_put);
+++
+++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+++{
+++ struct rpc_xprt_switch *xps;
+++
+++ atomic_long_dec(&xprt->queuelen);
+++ rcu_read_lock();
+++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+++ atomic_long_dec(&xps->xps_queuelen);
+++ rcu_read_unlock();
+++
+++ xprt_put(xprt);
+++}
+++
+++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
+++ struct rpc_clnt *clnt)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ if (args->multipath_option) {
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->create_clnt)
+++ mops->create_clnt(args, clnt);
+++ rpc_multipath_ops_put(mops);
+++ }
+++}
+++
+++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->releas_clnt)
+++ mops->releas_clnt(clnt);
+++
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->create_xprt) {
+++ mops->create_xprt(xprt);
+++ if (!xprt->multipath_context) {
+++ rpc_multipath_ops_put(mops);
+++ return true;
+++ }
+++ }
+++ rpc_multipath_ops_put(mops);
+++ return false;
+++}
+++
+++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ if (xprt->multipath_context) {
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->destroy_xprt)
+++ mops->destroy_xprt(xprt);
+++ rpc_multipath_ops_put(mops);
+++ }
+++}
+++
+++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mops;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (task->tk_client && mops && mops->xprt_iostat)
+++ mops->xprt_iostat(task);
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++void rpc_multipath_ops_failover_handle(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mpath_ops = NULL;
+++
+++ mpath_ops = rpc_multipath_ops_get();
+++ if (mpath_ops && mpath_ops->failover_handle)
+++ mpath_ops->failover_handle(task);
+++ rpc_multipath_ops_put(mpath_ops);
+++}
+++
+++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mpath_ops = NULL;
+++ bool ret = false;
+++
+++ mpath_ops = rpc_multipath_ops_get();
+++ if (mpath_ops && mpath_ops->task_need_call_start_again)
+++ ret = mpath_ops->task_need_call_start_again(task);
+++ rpc_multipath_ops_put(mpath_ops);
+++ return ret;
+++}
+++
+++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
+++ void *condition)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->adjust_task_timeout)
+++ mops->adjust_task_timeout(task, NULL);
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
+++ struct rpc_rqst *req)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->init_task_req)
+++ mops->init_task_req(task, req);
+++ rpc_multipath_ops_put(mops);
+++}
+++
+++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
+++{
+++ struct rpc_multipath_ops *mops = NULL;
+++
+++ mops = rpc_multipath_ops_get();
+++ if (mops && mops->prepare_transmit) {
+++ if (!(mops->prepare_transmit(task))) {
+++ rpc_multipath_ops_put(mops);
+++ return true;
+++ }
+++ }
+++ rpc_multipath_ops_put(mops);
+++ return false;
+++}
++diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
++index c912bf20faa2..c2b63b3d5217 100644
++--- a/net/sunrpc/xprt.c
+++++ b/net/sunrpc/xprt.c
++@@ -48,6 +48,7 @@
++ #include <linux/sunrpc/clnt.h>
++ #include <linux/sunrpc/metrics.h>
++ #include <linux/sunrpc/bc_xprt.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++ #include <linux/rcupdate.h>
++
++ #include <trace/events/sunrpc.h>
++@@ -259,6 +260,9 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
++ dprintk("RPC: %5u failed to lock transport %p\n",
++ task->tk_pid, xprt);
++ task->tk_timeout = 0;
+++
+++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
+++
++ task->tk_status = -EAGAIN;
++ if (req == NULL)
++ priority = RPC_PRIORITY_LOW;
++@@ -560,6 +564,9 @@ void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action)
++ struct rpc_xprt *xprt = req->rq_xprt;
++
++ task->tk_timeout = RPC_IS_SOFT(task) ? req->rq_timeout : 0;
+++
+++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
+++
++ rpc_sleep_on(&xprt->pending, task, action);
++ }
++ EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space);
++@@ -1347,6 +1354,9 @@ xprt_request_init(struct rpc_task *task)
++ req->rq_rcv_buf.buflen = 0;
++ req->rq_release_snd_buf = NULL;
++ xprt_reset_majortimeo(req);
+++
+++ rpc_multipath_ops_init_task_req(task, req);
+++
++ dprintk("RPC: %5u reserved req %p xid %08x\n", task->tk_pid,
++ req, ntohl(req->rq_xid));
++ }
++@@ -1427,6 +1437,9 @@ void xprt_release(struct rpc_task *task)
++ task->tk_ops->rpc_count_stats(task, task->tk_calldata);
++ else if (task->tk_client)
++ rpc_count_iostats(task, task->tk_client->cl_metrics);
+++
+++ rpc_multipath_ops_xprt_iostat(task);
+++
++ spin_lock(&xprt->recv_lock);
++ if (!list_empty(&req->rq_list)) {
++ list_del_init(&req->rq_list);
++@@ -1455,6 +1468,7 @@ void xprt_release(struct rpc_task *task)
++ else
++ xprt_free_bc_request(req);
++ }
+++EXPORT_SYMBOL_GPL(xprt_release);
++
++ static void xprt_init(struct rpc_xprt *xprt, struct net *net)
++ {
++@@ -1528,6 +1542,10 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
++ return ERR_PTR(-ENOMEM);
++ }
++
+++if (rpc_multipath_ops_create_xprt(xprt)) {
+++ xprt_destroy(xprt);
+++ return ERR_PTR(-ENOMEM);
+++}
++ rpc_xprt_debugfs_register(xprt);
++
++ dprintk("RPC: created transport %p with %u slots\n", xprt,
++@@ -1547,6 +1565,9 @@ static void xprt_destroy_cb(struct work_struct *work)
++ rpc_destroy_wait_queue(&xprt->sending);
++ rpc_destroy_wait_queue(&xprt->backlog);
++ kfree(xprt->servername);
+++
+++ rpc_multipath_ops_destroy_xprt(xprt);
+++
++ /*
++ * Tear down transport state and free the rpc_xprt
++ */
++diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c
++index 6ebaa58b4eff..6202a0be1327 100644
++--- a/net/sunrpc/xprtmultipath.c
+++++ b/net/sunrpc/xprtmultipath.c
++@@ -18,6 +18,7 @@
++ #include <linux/sunrpc/xprt.h>
++ #include <linux/sunrpc/addr.h>
++ #include <linux/sunrpc/xprtmultipath.h>
+++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++ typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct list_head *head,
++ const struct rpc_xprt *cur);
++@@ -26,8 +27,8 @@ static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular;
++ static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin;
++ static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall;
++
++-static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++- struct rpc_xprt *xprt)
+++void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+++ struct rpc_xprt *xprt)
++ {
++ if (unlikely(xprt_get(xprt) == NULL))
++ return;
++@@ -36,7 +37,9 @@ static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++ if (xps->xps_nxprts == 0)
++ xps->xps_net = xprt->xprt_net;
++ xps->xps_nxprts++;
+++ rpc_xps_nactive_add_one(xps);
++ }
+++EXPORT_SYMBOL(xprt_switch_add_xprt_locked);
++
++ /**
++ * rpc_xprt_switch_add_xprt - Add a new rpc_xprt to an rpc_xprt_switch
++@@ -63,6 +66,7 @@ static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps,
++ if (unlikely(xprt == NULL))
++ return;
++ xps->xps_nxprts--;
+++ rpc_xps_nactive_sub_one(xps);
++ if (xps->xps_nxprts == 0)
++ xps->xps_net = NULL;
++ smp_wmb();
++@@ -84,7 +88,7 @@ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
++ spin_unlock(&xps->xps_lock);
++ xprt_put(xprt);
++ }
++-
+++EXPORT_SYMBOL(rpc_xprt_switch_remove_xprt);
++ /**
++ * xprt_switch_alloc - Allocate a new struct rpc_xprt_switch
++ * @xprt: pointer to struct rpc_xprt
++@@ -102,7 +106,13 @@ struct rpc_xprt_switch *xprt_switch_alloc(struct rpc_xprt *xprt,
++ if (xps != NULL) {
++ spin_lock_init(&xps->xps_lock);
++ kref_init(&xps->xps_kref);
+++#if IS_ENABLED(CONFIG_ENFS)
+++ xps->xps_nxprts = 0;
+++ xps->xps_nactive = 0;
+++ atomic_long_set(&xps->xps_queuelen, 0);
+++#else
++ xps->xps_nxprts = 0;
+++#endif
++ INIT_LIST_HEAD(&xps->xps_xprt_list);
++ xps->xps_iter_ops = &rpc_xprt_iter_singular;
++ xprt_switch_add_xprt_locked(xps, xprt);
++@@ -148,6 +158,7 @@ struct rpc_xprt_switch *xprt_switch_get(struct rpc_xprt_switch *xps)
++ return xps;
++ return NULL;
++ }
+++EXPORT_SYMBOL(xprt_switch_get);
++
++ /**
++ * xprt_switch_put - Release a reference to a rpc_xprt_switch
++@@ -160,6 +171,7 @@ void xprt_switch_put(struct rpc_xprt_switch *xps)
++ if (xps != NULL)
++ kref_put(&xps->xps_kref, xprt_switch_free);
++ }
+++EXPORT_SYMBOL(xprt_switch_put);
++
++ /**
++ * rpc_xprt_switch_set_roundrobin - Set a round-robin policy on rpc_xprt_switch
+diff --git a/0003-add_enfs_module_for_nfs_mount_option.patch b/0003-add_enfs_module_for_nfs_mount_option.patch
+new file mode 100644
+index 0000000..70753b5
+--- /dev/null
++++ b/0003-add_enfs_module_for_nfs_mount_option.patch
+@@ -0,0 +1,1209 @@
++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..9c4c6c1880b6
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_multipath_parse.c
++@@ -0,0 +1,601 @@
+++// 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/parser.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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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..6f3e8703e3e2
++--- /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 nfsmultipathoptions type,
+++ char *str, void **enfs_option, struct net *net_ns);
+++void nfs_multipath_free_options(void **enfs_option);
+++
+++#endif
+diff --git a/0004-add_enfs_module_for_sunrpc_multipatch.patch b/0004-add_enfs_module_for_sunrpc_multipatch.patch
+new file mode 100644
+index 0000000..2c0fcc7
+--- /dev/null
++++ b/0004-add_enfs_module_for_sunrpc_multipatch.patch
+@@ -0,0 +1,1581 @@
++diff --git a/fs/nfs/enfs/enfs_multipath.h b/fs/nfs/enfs/enfs_multipath.h
++new file mode 100644
++index 000000000000..e064c2929ced
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_multipath.h
++@@ -0,0 +1,24 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: enfs multipath
+++ * Author:
+++ * 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_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_path.c b/fs/nfs/enfs/enfs_path.c
++new file mode 100644
++index 000000000000..7355f8c2f672
++--- /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 000000000000..97b1ef3730b8
++--- /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 000000000000..53fa1a07642f
++--- /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 000000000000..321951031c2e
++--- /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 000000000000..2c3fe125c735
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_remount.c
++@@ -0,0 +1,221 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: remount ip source file
+++ * Author: y00583252
+++ * 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 000000000000..a663ed257004
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_remount.h
++@@ -0,0 +1,15 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: remount ip header file
+++ * Author: y00583252
+++ * 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 000000000000..4e4eda784a3e
++--- /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 000000000000..b72b088a6258
++--- /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
+diff --git a/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
+new file mode 100644
+index 0000000..cc6b677
+--- /dev/null
++++ b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
+@@ -0,0 +1,1607 @@
++diff --git a/fs/nfs/enfs/enfs_config.c b/fs/nfs/enfs/enfs_config.c
++new file mode 100644
++index 000000000000..11aa7a00385b
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_config.c
++@@ -0,0 +1,378 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ */
+++#include <linux/cdev.h>
+++#include <linux/errno.h>
+++#include <linux/fcntl.h>
+++#include <linux/fs.h>
+++#include <linux/kernel.h>
+++#include <linux/kthread.h>
+++#include <linux/slab.h>
+++#include <linux/string.h>
+++#include <linux/uaccess.h>
+++#include <linux/delay.h>
+++
+++#include "enfs_errcode.h"
+++#include "enfs_log.h"
+++#include "enfs_config.h"
+++
+++#define MAX_FILE_SIZE 8192
+++#define STRING_BUF_SIZE 128
+++#define CONFIG_FILE_PATH "/etc/enfs/config.ini"
+++#define ENFS_NOTIFY_FILE_PERIOD 1000UL
+++
+++#define MAX_PATH_DETECT_INTERVAL 300
+++#define MIN_PATH_DETECT_INTERVAL 5
+++#define MAX_PATH_DETECT_TIMEOUT 60
+++#define MIN_PATH_DETECT_TIMEOUT 1
+++#define MAX_MULTIPATH_TIMEOUT 60
+++#define MIN_MULTIPATH_TIMEOUT 0
+++#define MAX_MULTIPATH_STATE ENFS_MULTIPATH_DISABLE
+++#define MIN_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
+++
+++#define DEFAULT_PATH_DETECT_INTERVAL 10
+++#define DEFAULT_PATH_DETECT_TIMEOUT 5
+++#define DEFAULT_MULTIPATH_TIMEOUT 0
+++#define DEFAULT_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
+++#define DEFAULT_LOADBALANCE_MODE ENFS_LOADBALANCE_RR
+++
+++typedef int (*check_and_assign_func)(char *, char *, int, int);
+++
+++struct enfs_config_info {
+++ int32_t path_detect_interval;
+++ int32_t path_detect_timeout;
+++ int32_t multipath_timeout;
+++ int32_t loadbalance_mode;
+++ int32_t multipath_state;
+++};
+++
+++struct check_and_assign_value {
+++ char *field_name;
+++ check_and_assign_func func;
+++ int min_value;
+++ int max_value;
+++};
+++
+++static struct enfs_config_info g_enfs_config_info;
+++static struct timespec64 modify_time;
+++static struct task_struct *thread;
+++
+++static int enfs_check_config_value(char *value, int min_value, int max_value)
+++{
+++ unsigned long num_value;
+++ int ret;
+++
+++ ret = kstrtol(value, 10, &num_value);
+++ if (ret != 0) {
+++ enfs_log_error("Failed to convert string to int\n");
+++ return -EINVAL;
+++ }
+++
+++ if (num_value < min_value || num_value > max_value)
+++ return -EINVAL;
+++
+++ return num_value;
+++}
+++
+++static int32_t enfs_check_and_assign_int_value(char *field_name, char *value,
+++ int min_value, int max_value)
+++{
+++ int int_value = enfs_check_config_value(value, min_value, max_value);
+++
+++ if (int_value < 0)
+++ return -EINVAL;
+++
+++ if (strcmp(field_name, "path_detect_interval") == 0) {
+++ g_enfs_config_info.path_detect_interval = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ if (strcmp(field_name, "path_detect_timeout") == 0) {
+++ g_enfs_config_info.path_detect_timeout = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ if (strcmp(field_name, "multipath_timeout") == 0) {
+++ g_enfs_config_info.multipath_timeout = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ if (strcmp(field_name, "multipath_disable") == 0) {
+++ g_enfs_config_info.multipath_state = int_value;
+++ return ENFS_RET_OK;
+++ }
+++ return -EINVAL;
+++}
+++
+++static int32_t enfs_check_and_assign_loadbalance_mode(char *field_name,
+++ char *value,
+++ int min_value,
+++ int max_value)
+++{
+++ if (value == NULL)
+++ return -EINVAL;
+++
+++ if (strcmp(field_name, "multipath_select_policy") == 0) {
+++ if (strcmp(value, "roundrobin") == 0) {
+++ g_enfs_config_info.loadbalance_mode
+++ = ENFS_LOADBALANCE_RR;
+++ return ENFS_RET_OK;
+++ }
+++ }
+++ return -EINVAL;
+++}
+++
+++static const struct check_and_assign_value g_check_and_assign_value[] = {
+++ {"path_detect_interval", enfs_check_and_assign_int_value,
+++ MIN_PATH_DETECT_INTERVAL, MAX_PATH_DETECT_INTERVAL},
+++ {"path_detect_timeout", enfs_check_and_assign_int_value,
+++ MIN_PATH_DETECT_TIMEOUT, MAX_PATH_DETECT_TIMEOUT},
+++ {"multipath_timeout", enfs_check_and_assign_int_value,
+++ MIN_MULTIPATH_TIMEOUT, MAX_MULTIPATH_TIMEOUT},
+++ {"multipath_disable", enfs_check_and_assign_int_value,
+++ MIN_MULTIPATH_STATE, MAX_MULTIPATH_STATE},
+++ {"multipath_select_policy", enfs_check_and_assign_loadbalance_mode,
+++ 0, 0},
+++};
+++
+++static int32_t enfs_read_config_file(char *buffer, char *file_path)
+++{
+++ int ret;
+++ struct file *filp = NULL;
+++ loff_t f_pos = 0;
+++ mm_segment_t fs;
+++
+++
+++ filp = filp_open(file_path, O_RDONLY, 0);
+++
+++ if (IS_ERR(filp)) {
+++ enfs_log_error("Failed to open file %s\n", CONFIG_FILE_PATH);
+++ ret = -ENOENT;
+++ return ret;
+++ }
+++
+++ fs = get_fs();
+++ set_fs(get_ds());
+++ kernel_read(filp, buffer, MAX_FILE_SIZE, &f_pos);
+++ set_fs(fs);
+++
+++ ret = filp_close(filp, NULL);
+++ if (ret) {
+++ enfs_log_error("Close File:%s failed:%d.\n",
+++ CONFIG_FILE_PATH, ret);
+++ return -EINVAL;
+++ }
+++ return ENFS_RET_OK;
+++}
+++
+++static int32_t enfs_deal_with_comment_line(char *buffer)
+++{
+++ int ret;
+++ char *pos = strchr(buffer, '\n');
+++
+++ if (pos != NULL)
+++ ret = strlen(buffer) - strlen(pos);
+++ else
+++ ret = strlen(buffer);
+++
+++ return ret;
+++}
+++
+++static int32_t enfs_parse_key_value_from_config(char *buffer, char *key,
+++ char *value, int keyLen,
+++ int valueLen)
+++{
+++ char *line;
+++ char *tokenPtr;
+++ int len;
+++ char *tem;
+++ char *pos = strchr(buffer, '\n');
+++
+++ if (pos != NULL)
+++ len = strlen(buffer) - strlen(pos);
+++ else
+++ len = strlen(buffer);
+++
+++ line = kmalloc(len + 1, GFP_KERNEL);
+++ if (!line) {
+++ enfs_log_error("Failed to allocate memory.\n");
+++ return -ENOMEM;
+++ }
+++ line[len] = '\0';
+++ strncpy(line, buffer, len);
+++
+++ tem = line;
+++ tokenPtr = strsep(&tem, "=");
+++ if (tokenPtr == NULL || tem == NULL) {
+++ kfree(line);
+++ return len;
+++ }
+++ strncpy(key, strim(tokenPtr), keyLen);
+++ strncpy(value, strim(tem), valueLen);
+++
+++ kfree(line);
+++ return len;
+++}
+++
+++static int32_t enfs_get_value_from_config_file(char *buffer, char *field_name,
+++ char *value, int valueLen)
+++{
+++ int ret;
+++ char key[STRING_BUF_SIZE + 1] = {0};
+++ char val[STRING_BUF_SIZE + 1] = {0};
+++
+++ while (buffer[0] != '\0') {
+++ if (buffer[0] == '\n') {
+++ buffer++;
+++ } else if (buffer[0] == '#') {
+++ ret = enfs_deal_with_comment_line(buffer);
+++ if (ret > 0)
+++ buffer += ret;
+++ } else {
+++ ret = enfs_parse_key_value_from_config(buffer, key, val,
+++ STRING_BUF_SIZE,
+++ STRING_BUF_SIZE);
+++ if (ret < 0) {
+++ enfs_log_error("failed parse key value, %d\n"
+++ , ret);
+++ return ret;
+++ }
+++ key[STRING_BUF_SIZE] = '\0';
+++ val[STRING_BUF_SIZE] = '\0';
+++
+++ buffer += ret;
+++
+++ if (strcmp(field_name, key) == 0) {
+++ strncpy(value, val, valueLen);
+++ return ENFS_RET_OK;
+++ }
+++ }
+++ }
+++ enfs_log_error("can not find value which matched field_name: %s.\n",
+++ field_name);
+++ return -EINVAL;
+++}
+++
+++int32_t enfs_config_load(void)
+++{
+++ char value[STRING_BUF_SIZE + 1];
+++ int ret;
+++ int table_len;
+++ int min;
+++ int max;
+++ int i;
+++ char *buffer;
+++
+++ buffer = kmalloc(MAX_FILE_SIZE, GFP_KERNEL);
+++ if (!buffer) {
+++ enfs_log_error("Failed to allocate memory.\n");
+++ return -ENOMEM;
+++ }
+++ memset(buffer, 0, MAX_FILE_SIZE);
+++
+++ g_enfs_config_info.path_detect_interval = DEFAULT_PATH_DETECT_INTERVAL;
+++ g_enfs_config_info.path_detect_timeout = DEFAULT_PATH_DETECT_TIMEOUT;
+++ g_enfs_config_info.multipath_timeout = DEFAULT_MULTIPATH_TIMEOUT;
+++ g_enfs_config_info.multipath_state = DEFAULT_MULTIPATH_STATE;
+++ g_enfs_config_info.loadbalance_mode = DEFAULT_LOADBALANCE_MODE;
+++
+++ table_len = sizeof(g_check_and_assign_value) /
+++ sizeof(g_check_and_assign_value[0]);
+++
+++ ret = enfs_read_config_file(buffer, CONFIG_FILE_PATH);
+++ if (ret != 0) {
+++ kfree(buffer);
+++ return ret;
+++ }
+++
+++ for (i = 0; i < table_len; i++) {
+++ ret = enfs_get_value_from_config_file(buffer,
+++ g_check_and_assign_value[i].field_name,
+++ value, STRING_BUF_SIZE);
+++ if (ret < 0)
+++ continue;
+++
+++ value[STRING_BUF_SIZE] = '\0';
+++ min = g_check_and_assign_value[i].min_value;
+++ max = g_check_and_assign_value[i].max_value;
+++ if (g_check_and_assign_value[i].func != NULL)
+++ (*g_check_and_assign_value[i].func)(
+++ g_check_and_assign_value[i].field_name,
+++ value, min, max);
+++ }
+++
+++ kfree(buffer);
+++ return ENFS_RET_OK;
+++}
+++
+++int32_t enfs_get_config_path_detect_interval(void)
+++{
+++ return g_enfs_config_info.path_detect_interval;
+++}
+++
+++int32_t enfs_get_config_path_detect_timeout(void)
+++{
+++ return g_enfs_config_info.path_detect_timeout;
+++}
+++
+++int32_t enfs_get_config_multipath_timeout(void)
+++{
+++ return g_enfs_config_info.multipath_timeout;
+++}
+++
+++int32_t enfs_get_config_multipath_state(void)
+++{
+++ return g_enfs_config_info.multipath_state;
+++}
+++
+++int32_t enfs_get_config_loadbalance_mode(void)
+++{
+++ return g_enfs_config_info.loadbalance_mode;
+++}
+++
+++static bool enfs_file_changed(const char *filename)
+++{
+++ int err;
+++ struct kstat file_stat;
+++
+++ err = vfs_stat(filename, &file_stat);
+++ if (err) {
+++ pr_err("failed to open file:%s err:%d\n", filename, err);
+++ return false;
+++ }
+++
+++ if (timespec64_compare(&modify_time, &file_stat.mtime) == -1) {
+++ modify_time = file_stat.mtime;
+++ pr_info("file change: %lld %lld\n", modify_time.tv_sec,
+++ file_stat.mtime.tv_sec);
+++ return true;
+++ }
+++
+++ return false;
+++}
+++
+++static int enfs_thread_func(void *data)
+++{
+++ while (!kthread_should_stop()) {
+++ if (enfs_file_changed(CONFIG_FILE_PATH))
+++ enfs_config_load();
+++
+++ msleep(ENFS_NOTIFY_FILE_PERIOD);
+++ }
+++ return 0;
+++}
+++
+++int enfs_config_timer_init(void)
+++{
+++ thread = kthread_run(enfs_thread_func, NULL, "enfs_notiy_file_thread");
+++ if (IS_ERR(thread)) {
+++ pr_err("Failed to create kernel thread\n");
+++ return PTR_ERR(thread);
+++ }
+++ return 0;
+++}
+++
+++void enfs_config_timer_exit(void)
+++{
+++ pr_info("enfs_notify_file_exit\n");
+++ if (thread)
+++ kthread_stop(thread);
+++}
++diff --git a/fs/nfs/enfs/enfs_config.h b/fs/nfs/enfs/enfs_config.h
++new file mode 100644
++index 000000000000..752710129170
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_config.h
++@@ -0,0 +1,32 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs configuration
+++ * Author: y00583252
+++ * Create: 2023-07-27
+++ */
+++
+++#ifndef ENFS_CONFIG_H
+++#define ENFS_CONFIG_H
+++
+++#include <linux/types.h>
+++
+++enum enfs_multipath_state {
+++ ENFS_MULTIPATH_ENABLE = 0,
+++ ENFS_MULTIPATH_DISABLE = 1,
+++};
+++
+++enum enfs_loadbalance_mode {
+++ ENFS_LOADBALANCE_RR,
+++};
+++
+++
+++int32_t enfs_get_config_path_detect_interval(void);
+++int32_t enfs_get_config_path_detect_timeout(void);
+++int32_t enfs_get_config_multipath_timeout(void);
+++int32_t enfs_get_config_multipath_state(void);
+++int32_t enfs_get_config_loadbalance_mode(void);
+++int32_t enfs_config_load(void);
+++int32_t enfs_config_timer_init(void);
+++void enfs_config_timer_exit(void);
+++#endif // ENFS_CONFIG_H
++diff --git a/fs/nfs/enfs/enfs_errcode.h b/fs/nfs/enfs/enfs_errcode.h
++new file mode 100644
++index 000000000000..cca47ab9a191
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_errcode.h
++@@ -0,0 +1,17 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs errocode
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#ifndef ENFS_ERRCODE_H
+++#define ENFS_ERRCODE_H
+++
+++enum {
+++ ENFS_RET_OK = 0,
+++ ENFS_RET_FAIL
+++};
+++
+++#endif // ENFS_ERRCODE_H
++diff --git a/fs/nfs/enfs/enfs_log.h b/fs/nfs/enfs/enfs_log.h
++new file mode 100644
++index 000000000000..177b404f05df
++--- /dev/null
+++++ b/fs/nfs/enfs/enfs_log.h
++@@ -0,0 +1,25 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: enfs log
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++#ifndef ENFS_LOG_H
+++#define ENFS_LOG_H
+++
+++#include <linux/printk.h>
+++
+++#define enfs_log_info(fmt, ...) \
+++ pr_info("enfs:[%s]" pr_fmt(fmt), \
+++ __func__, ##__VA_ARGS__)
+++
+++#define enfs_log_error(fmt, ...) \
+++ pr_err("enfs:[%s]" pr_fmt(fmt), \
+++ __func__, ##__VA_ARGS__)
+++
+++#define enfs_log_debug(fmt, ...) \
+++ pr_debug("enfs:[%s]" pr_fmt(fmt), \
+++ __func__, ##__VA_ARGS__)
+++
+++#endif // ENFS_ERRCODE_H
++diff --git a/fs/nfs/enfs/failover_com.h b/fs/nfs/enfs/failover_com.h
++new file mode 100644
++index 000000000000..c52940da232e
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_com.h
++@@ -0,0 +1,23 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: failover time commont header file
+++ * Create: 2023-08-02
+++ */
+++#ifndef FAILOVER_COMMON_H
+++#define FAILOVER_COMMON_H
+++
+++static inline bool failover_is_enfs_clnt(struct rpc_clnt *clnt)
+++{
+++ struct rpc_clnt *next = clnt->cl_parent;
+++
+++ while (next) {
+++ if (next == next->cl_parent)
+++ break;
+++ next = next->cl_parent;
+++ }
+++
+++ return next != NULL ? next->cl_enfs : clnt->cl_enfs;
+++}
+++
+++#endif // FAILOVER_COMMON_H
++diff --git a/fs/nfs/enfs/failover_path.c b/fs/nfs/enfs/failover_path.c
++new file mode 100644
++index 000000000000..93b454de29d1
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_path.c
++@@ -0,0 +1,207 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs path failover file
+++ * Author: y00583252
+++ * Create: 2023-08-02
+++ */
+++
+++#include "failover_path.h"
+++#include <linux/nfs.h>
+++#include <linux/nfs3.h>
+++#include <linux/nfs4.h>
+++#include <linux/sunrpc/clnt.h>
+++#include <linux/sunrpc/sched.h>
+++#include <linux/sunrpc/xprt.h>
+++#include "enfs_config.h"
+++#include "enfs_log.h"
+++#include "failover_com.h"
+++#include "pm_state.h"
+++#include "pm_ping.h"
+++
+++enum failover_policy_t {
+++ FAILOVER_NOACTION = 1,
+++ FAILOVER_RETRY,
+++ FAILOVER_RETRY_DELAY,
+++};
+++
+++static void failover_retry_path(struct rpc_task *task)
+++{
+++ xprt_release(task);
+++ rpc_init_task_retry_counters(task);
+++ rpc_task_release_transport(task);
+++ rpc_restart_call(task);
+++}
+++
+++static void failover_retry_path_delay(struct rpc_task *task, int32_t delay)
+++{
+++ failover_retry_path(task);
+++ rpc_delay(task, delay);
+++}
+++
+++static void failover_retry_path_by_policy(struct rpc_task *task,
+++ enum failover_policy_t policy)
+++{
+++ if (policy == FAILOVER_RETRY)
+++ failover_retry_path(task);
+++ else if (policy == FAILOVER_RETRY_DELAY)
+++ failover_retry_path_delay(task, 3 * HZ); // delay 3s
+++}
+++
+++static
+++enum failover_policy_t failover_get_nfs3_retry_policy(struct rpc_task *task)
+++{
+++ enum failover_policy_t policy = FAILOVER_NOACTION;
+++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
+++ u32 proc;
+++
+++ if (unlikely(procinfo == NULL)) {
+++ enfs_log_error("the task contains no valid proc.\n");
+++ return FAILOVER_NOACTION;
+++ }
+++
+++ proc = procinfo->p_proc;
+++
+++ switch (proc) {
+++ case NFS3PROC_CREATE:
+++ case NFS3PROC_MKDIR:
+++ case NFS3PROC_REMOVE:
+++ case NFS3PROC_RMDIR:
+++ case NFS3PROC_SYMLINK:
+++ case NFS3PROC_LINK:
+++ case NFS3PROC_SETATTR:
+++ case NFS3PROC_WRITE:
+++ policy = FAILOVER_RETRY_DELAY;
+++ default:
+++ policy = FAILOVER_RETRY;
+++ }
+++ return policy;
+++}
+++
+++static
+++enum failover_policy_t failover_get_nfs4_retry_policy(struct rpc_task *task)
+++{
+++ enum failover_policy_t policy = FAILOVER_NOACTION;
+++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
+++ u32 proc_idx;
+++
+++ if (unlikely(procinfo == NULL)) {
+++ enfs_log_error("the task contains no valid proc.\n");
+++ return FAILOVER_NOACTION;
+++ }
+++
+++ proc_idx = procinfo->p_statidx;
+++
+++ switch (proc_idx) {
+++ case NFSPROC4_CLNT_CREATE:
+++ case NFSPROC4_CLNT_REMOVE:
+++ case NFSPROC4_CLNT_LINK:
+++ case NFSPROC4_CLNT_SYMLINK:
+++ case NFSPROC4_CLNT_SETATTR:
+++ case NFSPROC4_CLNT_WRITE:
+++ case NFSPROC4_CLNT_RENAME:
+++ case NFSPROC4_CLNT_SETACL:
+++ policy = FAILOVER_RETRY_DELAY;
+++ default:
+++ policy = FAILOVER_RETRY;
+++ }
+++ return policy;
+++}
+++
+++static enum failover_policy_t failover_get_retry_policy(struct rpc_task *task)
+++{
+++ struct rpc_clnt *clnt = task->tk_client;
+++ u32 version = clnt->cl_vers;
+++ enum failover_policy_t policy = FAILOVER_NOACTION;
+++
+++ // 1. if the task meant to send to certain xprt, take no action
+++ if (task->tk_flags & RPC_TASK_FIXED)
+++ return FAILOVER_NOACTION;
+++
+++ // 2. get policy by different version of nfs protocal
+++ if (version == 3) // nfs v3
+++ policy = failover_get_nfs3_retry_policy(task);
+++ else if (version == 4) // nfs v4
+++ policy = failover_get_nfs4_retry_policy(task);
+++ else
+++ return FAILOVER_NOACTION;
+++
+++ // 3. if the task is not send to target, retry immediately
+++ if (!RPC_WAS_SENT(task))
+++ policy = FAILOVER_RETRY;
+++
+++ return policy;
+++}
+++
+++static int failover_check_task(struct rpc_task *task)
+++{
+++ struct rpc_clnt *clnt = NULL;
+++ int disable_mpath = enfs_get_config_multipath_state();
+++
+++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return -EINVAL;
+++ }
+++
+++ if (unlikely((task == NULL) || (task->tk_client == NULL))) {
+++ enfs_log_error("The task is not valid.\n");
+++ return -EINVAL;
+++ }
+++
+++ clnt = task->tk_client;
+++
+++ if (clnt->cl_prog != NFS_PROGRAM) {
+++ enfs_log_debug("The clnt is not prog{%u} type.\n",
+++ clnt->cl_prog);
+++ return -EINVAL;
+++ }
+++
+++ if (!failover_is_enfs_clnt(clnt)) {
+++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
+++ return -EINVAL;
+++ }
+++ return 0;
+++}
+++
+++void failover_handle(struct rpc_task *task)
+++{
+++ enum failover_policy_t policy;
+++ int ret;
+++
+++ ret = failover_check_task(task);
+++ if (ret != 0)
+++ return;
+++
+++ pm_set_path_state(task->tk_xprt, PM_STATE_FAULT);
+++
+++ policy = failover_get_retry_policy(task);
+++
+++ failover_retry_path_by_policy(task, policy);
+++}
+++
+++bool failover_task_need_call_start_again(struct rpc_task *task)
+++{
+++ int ret;
+++
+++ ret = failover_check_task(task);
+++ if (ret != 0)
+++ return false;
+++
+++ return true;
+++}
+++
+++bool failover_prepare_transmit(struct rpc_task *task)
+++{
+++ if (task->tk_flags & RPC_TASK_FIXED)
+++ return true;
+++
+++ if (pm_ping_is_test_xprt_task(task))
+++ return true;
+++
+++ if (pm_get_path_state(task->tk_xprt) == PM_STATE_FAULT) {
+++ task->tk_status = -ETIMEDOUT;
+++ return false;
+++ }
+++
+++ return true;
+++}
++diff --git a/fs/nfs/enfs/failover_path.h b/fs/nfs/enfs/failover_path.h
++new file mode 100644
++index 000000000000..6f1294829a6e
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_path.h
++@@ -0,0 +1,17 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs path failover header file
+++ * Author: y00583252
+++ * Create: 2023-08-02
+++ */
+++
+++#ifndef FAILOVER_PATH_H
+++#define FAILOVER_PATH_H
+++
+++#include <linux/sunrpc/sched.h>
+++
+++void failover_handle(struct rpc_task *task);
+++bool failover_prepare_transmit(struct rpc_task *task);
+++
+++#endif // FAILOVER_PATH_H
++diff --git a/fs/nfs/enfs/failover_time.c b/fs/nfs/enfs/failover_time.c
++new file mode 100644
++index 000000000000..866ea82d13fc
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_time.c
++@@ -0,0 +1,99 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: failover time file
+++ * Create: 2023-08-02
+++ */
+++
+++#include "failover_time.h"
+++#include <linux/jiffies.h>
+++#include <linux/sunrpc/clnt.h>
+++#include "enfs_config.h"
+++#include "enfs_log.h"
+++#include "failover_com.h"
+++#include "pm_ping.h"
+++
+++static unsigned long failover_get_mulitipath_timeout(struct rpc_clnt *clnt)
+++{
+++ unsigned long config_tmo = enfs_get_config_multipath_timeout() * HZ;
+++ unsigned long clnt_tmo = clnt->cl_timeout->to_initval;
+++
+++ if (config_tmo == 0)
+++ return clnt_tmo;
+++
+++ return config_tmo > clnt_tmo ? clnt_tmo : config_tmo;
+++}
+++
+++void failover_adjust_task_timeout(struct rpc_task *task, void *condition)
+++{
+++ struct rpc_clnt *clnt = NULL;
+++ unsigned long tmo;
+++ int disable_mpath = enfs_get_config_multipath_state();
+++
+++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return;
+++ }
+++
+++ clnt = task->tk_client;
+++ if (unlikely(clnt == NULL)) {
+++ enfs_log_error("task associate client is NULL.\n");
+++ return;
+++ }
+++
+++ if (!failover_is_enfs_clnt(clnt)) {
+++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
+++ return;
+++ }
+++
+++ tmo = failover_get_mulitipath_timeout(clnt);
+++ if (tmo == 0) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return;
+++ }
+++
+++ if (task->tk_timeout != 0)
+++ task->tk_timeout =
+++ task->tk_timeout < tmo ? task->tk_timeout : tmo;
+++ else
+++ task->tk_timeout = tmo;
+++}
+++
+++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
+++{
+++ struct rpc_clnt *clnt = NULL;
+++ int disable_mpath = enfs_get_config_multipath_state();
+++
+++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
+++ enfs_log_debug("Multipath is not enabled.\n");
+++ return;
+++ }
+++
+++ clnt = task->tk_client;
+++ if (unlikely(clnt == NULL)) {
+++ enfs_log_error("task associate client is NULL.\n");
+++ return;
+++ }
+++
+++ if (!failover_is_enfs_clnt(clnt)) {
+++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
+++ return;
+++ }
+++
+++ if (!pm_ping_is_test_xprt_task(task))
+++ req->rq_timeout = failover_get_mulitipath_timeout(clnt);
+++ else {
+++ req->rq_timeout = enfs_get_config_path_detect_timeout() * HZ;
+++ req->rq_majortimeo = req->rq_timeout + jiffies;
+++ }
+++
+++ /*
+++ * when task is retried, the req is new, we lost major-timeout times,
+++ * so we have to restore req major
+++ * timeouts from the task, if it is stored.
+++ */
+++ if (task->tk_major_timeo != 0)
+++ req->rq_majortimeo = task->tk_major_timeo;
+++ else
+++ task->tk_major_timeo = req->rq_majortimeo;
+++}
++diff --git a/fs/nfs/enfs/failover_time.h b/fs/nfs/enfs/failover_time.h
++new file mode 100644
++index 000000000000..ede25b577a2a
++--- /dev/null
+++++ b/fs/nfs/enfs/failover_time.h
++@@ -0,0 +1,16 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: failover time header file
+++ * Create: 2023-08-02
+++ */
+++
+++#ifndef FAILOVER_TIME_H
+++#define FAILOVER_TIME_H
+++
+++#include <linux/sunrpc/sched.h>
+++
+++void failover_adjust_task_timeout(struct rpc_task *task, void *condition);
+++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req);
+++
+++#endif // FAILOVER_TIME_H
++diff --git a/fs/nfs/enfs/init.h b/fs/nfs/enfs/init.h
++new file mode 100644
++index 000000000000..fdabb9084e19
++--- /dev/null
+++++ b/fs/nfs/enfs/init.h
++@@ -0,0 +1,17 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs client init
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#ifndef ENFS_INIT_H
+++#define ENFS_INIT_H
+++
+++#include <linux/types.h>
+++
+++int32_t enfs_init(void);
+++void enfs_fini(void);
+++
+++#endif
++diff --git a/fs/nfs/enfs/mgmt_init.c b/fs/nfs/enfs/mgmt_init.c
++new file mode 100644
++index 000000000000..75a40c5e0f6c
++--- /dev/null
+++++ b/fs/nfs/enfs/mgmt_init.c
++@@ -0,0 +1,22 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: mgmt component init
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#include "mgmt_init.h"
+++#include <linux/printk.h>
+++#include "enfs_errcode.h"
+++#include "enfs_config.h"
+++
+++int32_t mgmt_init(void)
+++{
+++ return enfs_config_timer_init();
+++}
+++
+++void mgmt_fini(void)
+++{
+++ enfs_config_timer_exit();
+++}
++diff --git a/fs/nfs/enfs/mgmt_init.h b/fs/nfs/enfs/mgmt_init.h
++new file mode 100644
++index 000000000000..aa78303b9f01
++--- /dev/null
+++++ b/fs/nfs/enfs/mgmt_init.h
++@@ -0,0 +1,18 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: mgmt component init
+++ * Author: y00583252
+++ * Create: 2023-07-31
+++ */
+++
+++#ifndef MGMT_INIT_H
+++#define MGMT_INIT_H
+++
+++#include <linux/types.h>
+++
+++int32_t mgmt_init(void);
+++void mgmt_fini(void);
+++
+++
+++#endif // MGMT_INIT_H
++diff --git a/fs/nfs/enfs/pm_ping.c b/fs/nfs/enfs/pm_ping.c
++new file mode 100644
++index 000000000000..24153cd4c7f3
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_ping.c
++@@ -0,0 +1,421 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: path state header file
+++ * Author: x00833432
+++ * Create: 2023-08-21
+++ */
+++
+++#include "pm_ping.h"
+++#include <linux/err.h>
+++#include <linux/spinlock.h>
+++#include <linux/slab.h>
+++#include <linux/module.h>
+++#include <linux/printk.h>
+++#include <linux/kthread.h>
+++#include <linux/nfs.h>
+++#include <linux/errno.h>
+++#include <linux/rcupdate.h>
+++#include <linux/workqueue.h>
+++#include <net/netns/generic.h>
+++#include <linux/atomic.h>
+++#include <linux/sunrpc/clnt.h>
+++
+++#include "../../../net/sunrpc/netns.h"
+++#include "pm_state.h"
+++#include "enfs.h"
+++#include "enfs_log.h"
+++#include "enfs_config.h"
+++
+++#define SLEEP_INTERVAL 2
+++extern unsigned int sunrpc_net_id;
+++
+++static struct task_struct *pm_ping_timer_thread;
+++//protect pint_execute_workq
+++static spinlock_t ping_execute_workq_lock;
+++// timer for test xprt workqueue
+++static struct workqueue_struct *ping_execute_workq;
+++// count the ping xprt work on flight
+++static atomic_t check_xprt_count;
+++
+++struct ping_xprt_work {
+++ struct rpc_xprt *xprt; // use this specific xprt
+++ struct rpc_clnt *clnt; // use this specific rpc_client
+++ struct work_struct ping_work;
+++};
+++
+++struct pm_ping_async_callback {
+++ void *data;
+++ void (*func)(void *data);
+++};
+++
+++// set xprt's enum pm_check_state
+++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
+++ enum pm_check_state state)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++
+++ if (IS_ERR(xprt)) {
+++ enfs_log_error("The xprt ptr is not exist.\n");
+++ return;
+++ }
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ xprt_get(xprt);
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ xprt_put(xprt);
+++ return;
+++ }
+++
+++ atomic_set(&ctx->path_check_state, state);
+++ xprt_put(xprt);
+++}
+++
+++// get xprt's enum pm_check_state
+++static enum pm_check_state pm_ping_get_path_check_state(struct rpc_xprt *xprt)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++ enum pm_check_state state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return PM_CHECK_UNDEFINE;
+++ }
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ return PM_CHECK_UNDEFINE;
+++ }
+++
+++ state = atomic_read(&ctx->path_check_state);
+++
+++ return state;
+++}
+++
+++static void pm_ping_call_done_callback(void *data)
+++{
+++ struct pm_ping_async_callback *callback_data =
+++ (struct pm_ping_async_callback *)data;
+++
+++ if (callback_data == NULL)
+++ return;
+++
+++ callback_data->func(callback_data->data);
+++
+++ kfree(callback_data);
+++}
+++
+++// Default callback for async RPC calls
+++static void pm_ping_call_done(struct rpc_task *task, void *data)
+++{
+++ struct rpc_xprt *xprt = task->tk_xprt;
+++
+++ atomic_dec(&check_xprt_count);
+++ if (task->tk_status >= 0)
+++ pm_set_path_state(xprt, PM_STATE_NORMAL);
+++ else
+++ pm_set_path_state(xprt, PM_STATE_FAULT);
+++
+++ pm_ping_set_path_check_state(xprt, PM_CHECK_FINISH);
+++
+++ pm_ping_call_done_callback(data);
+++}
+++
+++// register func to rpc_call_done
+++static const struct rpc_call_ops pm_ping_set_status_ops = {
+++ .rpc_call_done = pm_ping_call_done,
+++};
+++
+++// execute work which in work_queue
+++static void pm_ping_execute_work(struct work_struct *work)
+++{
+++ int ret = 0;
+++
+++ // get the work information
+++ struct ping_xprt_work *work_info =
+++ container_of(work, struct ping_xprt_work, ping_work);
+++
+++ // if check state is pending
+++ if (pm_ping_get_path_check_state(work_info->xprt) == PM_CHECK_WAITING) {
+++
+++ pm_ping_set_path_check_state(work_info->xprt,
+++ PM_CHECK_CHECKING);
+++
+++ ret = rpc_clnt_test_xprt(work_info->clnt,
+++ work_info->xprt,
+++ &pm_ping_set_status_ops,
+++ NULL,
+++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
+++
+++ if (ret < 0) {
+++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
+++
+++ pm_ping_set_path_check_state(work_info->xprt,
+++ PM_CHECK_FINISH);
+++
+++ } else
+++ atomic_inc(&check_xprt_count);
+++
+++ }
+++
+++ atomic_dec(&work_info->clnt->cl_count);
+++ xprt_put(work_info->xprt);
+++ kfree(work_info);
+++ work_info = NULL;
+++}
+++
+++static bool pm_ping_workqueue_queue_work(struct work_struct *work)
+++{
+++ bool ret = false;
+++
+++ spin_lock(&ping_execute_workq_lock);
+++
+++ if (ping_execute_workq != NULL)
+++ ret = queue_work(ping_execute_workq, work);
+++
+++ spin_unlock(&ping_execute_workq_lock);
+++ return ret;
+++}
+++
+++// init test work and add this work to workqueue
+++static int pm_ping_add_work(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt, void *data)
+++{
+++ struct ping_xprt_work *work_info;
+++ bool ret = false;
+++
+++ if (IS_ERR(xprt) || xprt == NULL) {
+++ enfs_log_error("The xprt ptr is not exist.\n");
+++ return -EINVAL;
+++ }
+++
+++ if (IS_ERR(clnt) || clnt == NULL) {
+++ enfs_log_error("The clnt ptr is not exist.\n");
+++ return -EINVAL;
+++ }
+++
+++ if (!xprt->multipath_context) {
+++ enfs_log_error("multipath_context is null.\n");
+++ return -EINVAL;
+++ }
+++
+++ // check xprt pending status, if pending status equals Finish
+++ // means this xprt can inster to work queue
+++ if (pm_ping_get_path_check_state(xprt) ==
+++ PM_CHECK_FINISH ||
+++ pm_ping_get_path_check_state(xprt) ==
+++ PM_CHECK_INIT) {
+++
+++ enfs_log_debug("find xprt pointer. %p\n", xprt);
+++ work_info = kzalloc(sizeof(struct ping_xprt_work), GFP_ATOMIC);
+++ if (work_info == NULL)
+++ return -ENOMEM;
+++ work_info->clnt = clnt;
+++ atomic_inc(&clnt->cl_count);
+++ work_info->xprt = xprt;
+++ xprt_get(xprt);
+++ INIT_WORK(&work_info->ping_work, pm_ping_execute_work);
+++ pm_ping_set_path_check_state(xprt, PM_CHECK_WAITING);
+++
+++ ret = pm_ping_workqueue_queue_work(&work_info->ping_work);
+++ if (!ret) {
+++ atomic_dec(&work_info->clnt->cl_count);
+++ xprt_put(work_info->xprt);
+++ kfree(work_info);
+++ return -EINVAL;
+++ }
+++ }
+++ return 0;
+++}
+++
+++// encapsulate pm_ping_add_work()
+++static int pm_ping_execute_xprt_test(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt, void *data)
+++{
+++ pm_ping_add_work(clnt, xprt, NULL);
+++ // return 0 for rpc_clnt_iterate_for_each_xprt();
+++ // because negative value will stop iterate all xprt
+++ // and we need return negative value for debug
+++ // Therefore, we need this function to iterate all xprt
+++ return 0;
+++}
+++
+++// export to other module add ping work to workqueue
+++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+++{
+++ int ret;
+++
+++ ret = pm_ping_add_work(clnt, xprt, NULL);
+++ return ret;
+++}
+++
+++// iterate xprt in the client
+++static void pm_ping_loop_rpclnt(struct sunrpc_net *sn)
+++{
+++ struct rpc_clnt *clnt;
+++
+++ spin_lock(&sn->rpc_client_lock);
+++ list_for_each_entry_rcu(clnt, &sn->all_clients, cl_clients) {
+++ if (clnt->cl_enfs) {
+++ enfs_log_debug("find rpc_clnt. %p\n", clnt);
+++ rpc_clnt_iterate_for_each_xprt(clnt,
+++ pm_ping_execute_xprt_test, NULL);
+++ }
+++ }
+++ spin_unlock(&sn->rpc_client_lock);
+++}
+++
+++// iterate each clnt in the sunrpc_net
+++static void pm_ping_loop_sunrpc_net(void)
+++{
+++ struct net *net;
+++ struct sunrpc_net *sn;
+++
+++ rcu_read_lock();
+++ for_each_net_rcu(net) {
+++ sn = net_generic(net, sunrpc_net_id);
+++ if (sn == NULL)
+++ continue;
+++ pm_ping_loop_rpclnt(sn);
+++ }
+++ rcu_read_unlock();
+++}
+++
+++static int pm_ping_routine(void *data)
+++{
+++ while (!kthread_should_stop()) {
+++ // equale 0 means open multipath
+++ if (enfs_get_config_multipath_state() ==
+++ ENFS_MULTIPATH_ENABLE)
+++ pm_ping_loop_sunrpc_net();
+++
+++ msleep((unsigned int)
+++ enfs_get_config_path_detect_interval() * 1000);
+++ }
+++ return 0;
+++}
+++
+++// start thread to cycly ping
+++static int pm_ping_start(void)
+++{
+++ pm_ping_timer_thread =
+++ kthread_run(pm_ping_routine, NULL, "pm_ping_routine");
+++ if (IS_ERR(pm_ping_timer_thread)) {
+++ enfs_log_error("Failed to create kernel thread\n");
+++ return PTR_ERR(pm_ping_timer_thread);
+++ }
+++ return 0;
+++}
+++
+++// initialize workqueue
+++static int pm_ping_workqueue_init(void)
+++{
+++ struct workqueue_struct *queue = NULL;
+++
+++ queue = create_workqueue("pm_ping_workqueue");
+++
+++ if (queue == NULL) {
+++ enfs_log_error("create workqueue failed.\n");
+++ return -ENOMEM;
+++ }
+++
+++ spin_lock(&ping_execute_workq_lock);
+++ ping_execute_workq = queue;
+++ spin_unlock(&ping_execute_workq_lock);
+++ enfs_log_info("create workqueue succeeeded.\n");
+++ return 0;
+++}
+++
+++static void pm_ping_workqueue_fini(void)
+++{
+++ struct workqueue_struct *queue = NULL;
+++
+++ spin_lock(&ping_execute_workq_lock);
+++ queue = ping_execute_workq;
+++ ping_execute_workq = NULL;
+++ spin_unlock(&ping_execute_workq_lock);
+++
+++ enfs_log_info("delete work queue\n");
+++
+++ if (queue != NULL) {
+++ flush_workqueue(queue);
+++ destroy_workqueue(queue);
+++ }
+++}
+++
+++// module exit func
+++void pm_ping_fini(void)
+++{
+++ if (pm_ping_timer_thread)
+++ kthread_stop(pm_ping_timer_thread);
+++
+++ pm_ping_workqueue_fini();
+++
+++ while (atomic_read(&check_xprt_count) != 0)
+++ msleep(SLEEP_INTERVAL);
+++}
+++
+++// module init func
+++int pm_ping_init(void)
+++{
+++ int ret;
+++
+++ atomic_set(&check_xprt_count, 0);
+++ ret = pm_ping_workqueue_init();
+++ if (ret != 0) {
+++ enfs_log_error("PM_PING Module loading failed.\n");
+++ return ret;
+++ }
+++ ret = pm_ping_start();
+++ if (ret != 0) {
+++ enfs_log_error("PM_PING Module loading failed.\n");
+++ pm_ping_workqueue_fini();
+++ return ret;
+++ }
+++
+++ return ret;
+++}
+++
+++bool pm_ping_is_test_xprt_task(struct rpc_task *task)
+++{
+++ return task->tk_ops == &pm_ping_set_status_ops ? true : false;
+++}
+++
+++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt,
+++ void (*func)(void *data),
+++ void *data)
+++{
+++ int ret;
+++
+++ struct pm_ping_async_callback *callback_data =
+++ kzalloc(sizeof(struct pm_ping_async_callback), GFP_KERNEL);
+++
+++ if (callback_data == NULL) {
+++ enfs_log_error("failed to mzalloc mem\n");
+++ return -ENOMEM;
+++ }
+++
+++ callback_data->data = data;
+++ callback_data->func = func;
+++ atomic_inc(&check_xprt_count);
+++ ret = rpc_clnt_test_xprt(clnt, xprt,
+++ &pm_ping_set_status_ops,
+++ callback_data,
+++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
+++
+++ if (ret < 0) {
+++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
+++ atomic_dec(&check_xprt_count);
+++ }
+++
+++ return ret;
+++}
++diff --git a/fs/nfs/enfs/pm_ping.h b/fs/nfs/enfs/pm_ping.h
++new file mode 100644
++index 000000000000..6bcb94bfc836
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_ping.h
++@@ -0,0 +1,33 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: nfs configuration
+++ * Author: x00833432
+++ * Create: 2023-07-27
+++ */
+++
+++#ifndef PM_PING_H
+++#define PM_PING_H
+++
+++#include <linux/sunrpc/clnt.h>
+++
+++enum pm_check_state {
+++ PM_CHECK_INIT, // this xprt never been queued
+++ PM_CHECK_WAITING, // this xprt waiting in the queue
+++ PM_CHECK_CHECKING, // this xprt is testing
+++ PM_CHECK_FINISH, // this xprt has been finished
+++ PM_CHECK_UNDEFINE, // undefine multipath struct
+++};
+++
+++int pm_ping_init(void);
+++void pm_ping_fini(void);
+++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
+++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
+++ enum pm_check_state state);
+++bool pm_ping_is_test_xprt_task(struct rpc_task *task);
+++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
+++ struct rpc_xprt *xprt,
+++ void (*func)(void *data),
+++ void *data);
+++
+++#endif // PM_PING_H
++diff --git a/fs/nfs/enfs/pm_state.c b/fs/nfs/enfs/pm_state.c
++new file mode 100644
++index 000000000000..220621a207a2
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_state.c
++@@ -0,0 +1,158 @@
+++// SPDX-License-Identifier: GPL-2.0
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: path state file
+++ * Author: y00583252
+++ * Create: 2023-08-12
+++ */
+++#include "pm_state.h"
+++#include <linux/sunrpc/xprt.h>
+++
+++#include "enfs.h"
+++#include "enfs_log.h"
+++
+++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++ enum pm_path_state state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return PM_STATE_UNDEFINED;
+++ }
+++
+++ xprt_get(xprt);
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ xprt_put(xprt);
+++ return PM_STATE_UNDEFINED;
+++ }
+++
+++ state = atomic_read(&ctx->path_state);
+++
+++ xprt_put(xprt);
+++
+++ return state;
+++}
+++
+++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state)
+++{
+++ struct enfs_xprt_context *ctx = NULL;
+++ enum pm_path_state cur_state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ xprt_get(xprt);
+++
+++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+++ if (ctx == NULL) {
+++ enfs_log_error("The xprt multipath ctx is not valid.\n");
+++ xprt_put(xprt);
+++ return;
+++ }
+++
+++ cur_state = atomic_read(&ctx->path_state);
+++ if (cur_state == state) {
+++ enfs_log_debug("The xprt is already {%d}.\n", state);
+++ xprt_put(xprt);
+++ return;
+++ }
+++
+++ atomic_set(&ctx->path_state, state);
+++ enfs_log_info("The xprt {%p} path state change from {%d} to {%d}.\n",
+++ xprt, cur_state, state);
+++
+++ xprt_put(xprt);
+++}
+++
+++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len)
+++{
+++ enum pm_path_state state;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ if ((buf == NULL) || (len <= 0)) {
+++ enfs_log_error("Buffer is not valid, len=%d.\n", len);
+++ return;
+++ }
+++
+++ state = pm_get_path_state(xprt);
+++
+++ switch (state) {
+++ case PM_STATE_INIT:
+++ (void)snprintf(buf, len, "Init");
+++ break;
+++ case PM_STATE_NORMAL:
+++ (void)snprintf(buf, len, "Normal");
+++ break;
+++ case PM_STATE_FAULT:
+++ (void)snprintf(buf, len, "Fault");
+++ break;
+++ default:
+++ (void)snprintf(buf, len, "Unknown");
+++ break;
+++ }
+++}
+++
+++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len)
+++{
+++ int i;
+++ unsigned long state;
+++ static unsigned long xprt_mask[] = {
+++ XPRT_LOCKED, XPRT_CONNECTED,
+++ XPRT_CONNECTING, XPRT_CLOSE_WAIT,
+++ XPRT_BOUND, XPRT_BINDING, XPRT_CLOSING,
+++ XPRT_CONGESTED};
+++
+++ static const char *const xprt_state_desc[] = {
+++ "LOCKED", "CONNECTED", "CONNECTING",
+++ "CLOSE_WAIT", "BOUND", "BINDING",
+++ "CLOSING", "CONGESTED"};
+++ int pos = 0;
+++ int ret = 0;
+++
+++ if (xprt == NULL) {
+++ enfs_log_error("The xprt is not valid.\n");
+++ return;
+++ }
+++
+++ if ((buf == NULL) || (len <= 0)) {
+++ enfs_log_error(
+++ "Xprt state buffer is not valid, len=%d.\n",
+++ len);
+++ return;
+++ }
+++
+++ xprt_get(xprt);
+++ state = READ_ONCE(xprt->state);
+++ xprt_put(xprt);
+++
+++ for (i = 0; i < ARRAY_SIZE(xprt_mask); ++i) {
+++ if (pos >= len)
+++ break;
+++
+++ if (!test_bit(xprt_mask[i], &state))
+++ continue;
+++
+++ if (pos == 0)
+++ ret = snprintf(buf, len, "%s", xprt_state_desc[i]);
+++ else
+++ ret = snprintf(buf + pos, len - pos, "|%s",
+++ xprt_state_desc[i]);
+++
+++ if (ret < 0) {
+++ enfs_log_error("format state failed, ret %d.\n", ret);
+++ break;
+++ }
+++
+++ pos += ret;
+++ }
+++}
++diff --git a/fs/nfs/enfs/pm_state.h b/fs/nfs/enfs/pm_state.h
++new file mode 100644
++index 000000000000..f5f52e5ab91d
++--- /dev/null
+++++ b/fs/nfs/enfs/pm_state.h
++@@ -0,0 +1,28 @@
+++/* SPDX-License-Identifier: GPL-2.0 */
+++/*
+++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+++ * Description: path state header file
+++ * Author: y00583252
+++ * Create: 2023-08-12
+++ */
+++
+++#ifndef PM_STATE_H
+++#define PM_STATE_H
+++
+++#include <linux/types.h>
+++#include <linux/sunrpc/xprt.h>
+++
+++enum pm_path_state {
+++ PM_STATE_INIT,
+++ PM_STATE_NORMAL,
+++ PM_STATE_FAULT,
+++ PM_STATE_UNDEFINED // xprt is not multipath xprt
+++};
+++
+++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state);
+++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt);
+++
+++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len);
+++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len);
+++
+++#endif // PM_STATE_H
+diff --git a/0006-add_enfs_compile_option.patch b/0006-add_enfs_compile_option.patch
+new file mode 100644
+index 0000000..ff3bc0e
+--- /dev/null
++++ b/0006-add_enfs_compile_option.patch
+@@ -0,0 +1,70 @@
++diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig
++index b04256636d4b..ae53510c0627 100644
++--- a/arch/arm64/configs/openeuler_defconfig
+++++ b/arch/arm64/configs/openeuler_defconfig
++@@ -5344,6 +5344,7 @@ CONFIG_LOCKD=m
++ CONFIG_LOCKD_V4=y
++ CONFIG_NFS_ACL_SUPPORT=m
++ CONFIG_NFS_COMMON=y
+++# CONFIG_ENFS is not set
++ CONFIG_SUNRPC=m
++ CONFIG_SUNRPC_GSS=m
++ CONFIG_SUNRPC_BACKCHANNEL=y
++diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig
++index 59baeb2973af..ccc317f7fdb2 100644
++--- a/arch/x86/configs/openeuler_defconfig
+++++ b/arch/x86/configs/openeuler_defconfig
++@@ -6825,6 +6825,7 @@ CONFIG_LOCKD=m
++ CONFIG_LOCKD_V4=y
++ CONFIG_NFS_ACL_SUPPORT=m
++ CONFIG_NFS_COMMON=y
+++# CONFIG_ENFS is not set
++ CONFIG_SUNRPC=m
++ CONFIG_SUNRPC_GSS=m
++ CONFIG_SUNRPC_BACKCHANNEL=y
++diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
++index e55f86713948..872c9b7671b1 100644
++--- a/fs/nfs/Kconfig
+++++ b/fs/nfs/Kconfig
++@@ -196,3 +196,14 @@ config NFS_DEBUG
++ depends on NFS_FS && SUNRPC_DEBUG
++ select CRC32
++ default y
+++
+++config ENFS
+++ tristate "NFS client support for ENFS"
+++ depends on NFS_FS
+++ default n
+++ help
+++ This option enables support multipath of the NFS protocol
+++ in the kernel's NFS client.
+++ This feature will improve performance and reliability.
+++
+++ If sure, say Y.
++diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
++index c587e3c4c6a6..19d0ac2ba3b8 100644
++--- a/fs/nfs/Makefile
+++++ b/fs/nfs/Makefile
++@@ -12,6 +12,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
++ nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
++ nfs-$(CONFIG_SYSCTL) += sysctl.o
++ nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
+++nfs-$(CONFIG_ENFS) += enfs_adapter.o
++
++ obj-$(CONFIG_NFS_V2) += nfsv2.o
++ nfsv2-y := nfs2super.o proc.o nfs2xdr.o
++@@ -34,3 +35,5 @@ nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o
++ obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
++ obj-$(CONFIG_PNFS_BLOCK) += blocklayout/
++ obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/
+++
+++obj-$(CONFIG_ENFS) += enfs/
++diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
++index 090658c3da12..fe4e3b28c5d1 100644
++--- a/net/sunrpc/Makefile
+++++ b/net/sunrpc/Makefile
++@@ -19,3 +19,4 @@ sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
++ sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o
++ sunrpc-$(CONFIG_PROC_FS) += stats.o
++ sunrpc-$(CONFIG_SYSCTL) += sysctl.o
+++sunrpc-$(CONFIG_ENFS) += sunrpc_enfs_adapter.o
+diff --git a/kernel.spec b/kernel.spec
+index 3215446..e242c00 100644
+--- a/kernel.spec
++++ b/kernel.spec
+@@ -60,6 +60,13 @@ Source9002: series.conf
+ Source9998: patches.tar.bz2
+ %endif
+
++Patch0001: 0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
++Patch0002: 0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
++Patch0003: 0003-add_enfs_module.patch
++Patch0004: 0004-add_enfs_module_for_sunrpc_multipatch.patch
++Patch0005: 0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
++Patch0006: 0006-add_enfs_compile_option.patch
++
+ #BuildRequires:
+ BuildRequires: module-init-tools, patch >= 2.5.4, bash >= 2.03, tar
+ BuildRequires: bzip2, xz, findutils, gzip, m4, perl, make >= 3.78, diffutils, gawk
+@@ -256,6 +263,12 @@ Applypatches()
+ Applypatches series.conf %{_builddir}/kernel-%{version}/linux-%{KernelVer}
+ %endif
+
++%patch0001 -p1
++%patch0002 -p1
++%patch0003 -p1
++%patch0004 -p1
++%patch0005 -p1
++%patch0006 -p1
+ touch .scmversion
+
+ find . \( -name "*.orig" -o -name "*~" \) -exec rm -f {} \; >/dev/null
+--
+2.25.0.windows.1
+
diff --git a/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
new file mode 100644
index 0000000..38e57a9
--- /dev/null
+++ b/0001-nfs_add_api_to_support_enfs_registe_and_handle_mount_option.patch
@@ -0,0 +1,757 @@
+diff --git a/fs/nfs/client.c b/fs/nfs/client.c
+index 7d02dc52209d..50820a8a684a 100644
+--- a/fs/nfs/client.c
++++ b/fs/nfs/client.c
+@@ -48,7 +48,7 @@
+ #include "callback.h"
+ #include "delegation.h"
+ #include "iostat.h"
+-#include "internal.h"
++#include "enfs_adapter.h"
+ #include "fscache.h"
+ #include "pnfs.h"
+ #include "nfs.h"
+@@ -255,6 +255,7 @@ void nfs_free_client(struct nfs_client *clp)
+ put_nfs_version(clp->cl_nfs_mod);
+ kfree(clp->cl_hostname);
+ kfree(clp->cl_acceptor);
++ nfs_free_multi_path_client(clp);
+ kfree(clp);
+ }
+ EXPORT_SYMBOL_GPL(nfs_free_client);
+@@ -330,6 +331,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
+ sap))
+ continue;
+
++ if (!nfs_multipath_client_match(clp, data))
++ continue;
++
+ refcount_inc(&clp->cl_count);
+ return clp;
+ }
+@@ -512,6 +516,9 @@ int nfs_create_rpc_client(struct nfs_client *clp,
+ .program = &nfs_program,
+ .version = clp->rpc_ops->version,
+ .authflavor = flavor,
++#if IS_ENABLED(CONFIG_ENFS)
++ .multipath_option = cl_init->enfs_option,
++#endif
+ };
+
+ if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
+@@ -634,6 +641,13 @@ struct nfs_client *nfs_init_client(struct nfs_client *clp,
+ /* the client is already initialised */
+ if (clp->cl_cons_state == NFS_CS_READY)
+ return clp;
++ error = nfs_create_multi_path_client(clp, cl_init);
++ if (error < 0) {
++ dprintk("%s: create failed.%d!\n", __func__, error);
++ nfs_put_client(clp);
++ clp = ERR_PTR(error);
++ return clp;
++ }
+
+ /*
+ * Create a client RPC handle for doing FSSTAT with UNIX auth only
+@@ -666,6 +680,9 @@ static int nfs_init_server(struct nfs_server *server,
+ .net = data->net,
+ .timeparms = &timeparms,
+ .init_flags = (1UL << NFS_CS_REUSEPORT),
++#if IS_ENABLED(CONFIG_ENFS)
++ .enfs_option = data->enfs_option,
++#endif
+ };
+ struct nfs_client *clp;
+ int error;
+diff --git a/fs/nfs/enfs_adapter.c b/fs/nfs/enfs_adapter.c
+new file mode 100644
+index 000000000000..7f471f2072c4
+--- /dev/null
++++ b/fs/nfs/enfs_adapter.c
+@@ -0,0 +1,230 @@
++// 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/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 <linux/sunrpc/sched.h>
++#include <linux/nfs_iostat.h>
++#include "enfs_adapter.h"
++#include "iostat.h"
++
++struct enfs_adapter_ops __rcu *enfs_adapter;
++
++int enfs_adapter_register(struct enfs_adapter_ops *ops)
++{
++ struct enfs_adapter_ops *old;
++
++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, NULL, ops);
++ if (old == NULL || old == ops)
++ return 0;
++ pr_err("regist %s ops %p failed. old %p\n", __func__, ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(enfs_adapter_register);
++
++int enfs_adapter_unregister(struct enfs_adapter_ops *ops)
++{
++ struct enfs_adapter_ops *old;
++
++ old = cmpxchg((struct enfs_adapter_ops **)&enfs_adapter, ops, NULL);
++ if (old == ops || old == NULL)
++ return 0;
++ pr_err("unregist %s ops %p failed. old %p\n", __func__, ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(enfs_adapter_unregister);
++
++struct enfs_adapter_ops *nfs_multipath_router_get(void)
++{
++ struct enfs_adapter_ops *ops;
++
++ rcu_read_lock();
++ ops = rcu_dereference(enfs_adapter);
++ if (ops == NULL) {
++ rcu_read_unlock();
++ return NULL;
++ }
++ if (!try_module_get(ops->owner))
++ ops = NULL;
++ rcu_read_unlock();
++ return ops;
++}
++
++void nfs_multipath_router_put(struct enfs_adapter_ops *ops)
++{
++ if (ops)
++ module_put(ops->owner);
++}
++
++bool is_valid_option(enum nfsmultipathoptions option)
++{
++ if (option < REMOTEADDR || option >= INVALID_OPTION) {
++ pr_warn("%s: ENFS invalid option %d\n", __func__, option);
++ return false;
++ }
++
++ return true;
++}
++
++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
++ struct nfs_parsed_mount_data *mnt)
++{
++
++ //parseMultiPathOptions(getNfsMultiPathOpt(token), string, mnt);
++
++ int rc;
++ struct enfs_adapter_ops *ops;
++
++ ops = nfs_multipath_router_get();
++ if ((ops == NULL) || (ops->parse_mount_options == NULL) ||
++ !is_valid_option(option)) {
++ nfs_multipath_router_put(ops);
++ dfprintk(MOUNT,
++ "NFS: parsing nfs mount option enfs not load[%s]\n"
++ , __func__);
++ return -EOPNOTSUPP;
++ }
++ // nfs_multipath_parse_options
++ dfprintk(MOUNT, "NFS: parsing nfs mount option '%s' type: %d[%s]\n"
++ , str, option, __func__);
++ rc = ops->parse_mount_options(option, str, &mnt->enfs_option, mnt->net);
++ nfs_multipath_router_put(ops);
++ return rc;
++}
++
++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
++{
++ struct enfs_adapter_ops *ops;
++
++ if (data->enfs_option == NULL)
++ return;
++
++ ops = nfs_multipath_router_get();
++ if ((ops == NULL) || (ops->free_mount_options == NULL)) {
++ nfs_multipath_router_put(ops);
++ return;
++ }
++ ops->free_mount_options((void *)&data->enfs_option);
++ nfs_multipath_router_put(ops);
++}
++
++int nfs_create_multi_path_client(struct nfs_client *client,
++ const struct nfs_client_initdata *cl_init)
++{
++ int ret = 0;
++ struct enfs_adapter_ops *ops;
++
++ if (cl_init->enfs_option == NULL)
++ return 0;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_init != NULL)
++ ret = ops->client_info_init(
++ (void *)&client->cl_multipath_data, cl_init);
++ nfs_multipath_router_put(ops);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(nfs_create_multi_path_client);
++
++void nfs_free_multi_path_client(struct nfs_client *clp)
++{
++ struct enfs_adapter_ops *ops;
++
++ if (clp->cl_multipath_data == NULL)
++ return;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_free != NULL)
++ ops->client_info_free(clp->cl_multipath_data);
++ nfs_multipath_router_put(ops);
++}
++
++int nfs_multipath_client_match(struct nfs_client *clp,
++ const struct nfs_client_initdata *sap)
++{
++ int ret = true;
++ struct enfs_adapter_ops *ops;
++
++ pr_info("%s src %p dst %p\n.", __func__,
++ clp->cl_multipath_data, sap->enfs_option);
++
++ if (clp->cl_multipath_data == NULL && sap->enfs_option == NULL)
++ return true;
++
++ if ((clp->cl_multipath_data == NULL && sap->enfs_option) ||
++ (clp->cl_multipath_data && sap->enfs_option == NULL)) {
++ pr_err("not match client src %p dst %p\n.",
++ clp->cl_multipath_data, sap->enfs_option);
++ return false;
++ }
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_match != NULL)
++ ret = ops->client_info_match(clp->cl_multipath_data,
++ sap->enfs_option);
++ nfs_multipath_router_put(ops);
++
++ return ret;
++}
++
++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst)
++{
++ int ret = true;
++ struct enfs_adapter_ops *ops;
++
++ if (src->cl_multipath_data == NULL && dst->cl_multipath_data == NULL)
++ return true;
++
++ if (src->cl_multipath_data == NULL || dst->cl_multipath_data == NULL)
++ return false;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->nfs4_client_info_match != NULL)
++ ret = ops->nfs4_client_info_match(src->cl_multipath_data,
++ src->cl_multipath_data);
++ nfs_multipath_router_put(ops);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(nfs4_multipath_client_match);
++
++void nfs_multipath_show_client_info(struct seq_file *mount_option,
++ struct nfs_server *server)
++{
++ struct enfs_adapter_ops *ops;
++
++ if (mount_option == NULL || server == NULL ||
++ server->client == NULL ||
++ server->nfs_client->cl_multipath_data == NULL)
++ return;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->client_info_show != NULL)
++ ops->client_info_show(mount_option, server);
++ nfs_multipath_router_put(ops);
++}
++
++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option)
++{
++ int ret = 0;
++ struct enfs_adapter_ops *ops;
++
++ if (nfs_client == NULL || nfs_client->cl_rpcclient == NULL)
++ return 0;
++
++ ops = nfs_multipath_router_get();
++ if (ops != NULL && ops->remount_ip_list != NULL)
++ ret = ops->remount_ip_list(nfs_client, enfs_option);
++ nfs_multipath_router_put(ops);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(nfs_remount_iplist);
+diff --git a/fs/nfs/enfs_adapter.h b/fs/nfs/enfs_adapter.h
+new file mode 100644
+index 000000000000..752544e18056
+--- /dev/null
++++ b/fs/nfs/enfs_adapter.h
+@@ -0,0 +1,101 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Client-side ENFS adapt header.
++ *
++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
++ */
++#ifndef _NFS_MULTIPATH_H_
++#define _NFS_MULTIPATH_H_
++
++#include "internal.h"
++
++#if IS_ENABLED(CONFIG_ENFS)
++enum nfsmultipathoptions {
++ REMOTEADDR,
++ LOCALADDR,
++ REMOTEDNSNAME,
++ REMOUNTREMOTEADDR,
++ REMOUNTLOCALADDR,
++ INVALID_OPTION
++};
++
++
++struct enfs_adapter_ops {
++ const char *name;
++ struct module *owner;
++ int (*parse_mount_options)(enum nfsmultipathoptions option,
++ char *str, void **enfs_option, struct net *net_ns);
++
++ void (*free_mount_options)(void **data);
++
++ int (*client_info_init)(void **data,
++ const struct nfs_client_initdata *cl_init);
++ void (*client_info_free)(void *data);
++ int (*client_info_match)(void *src, void *dst);
++ int (*nfs4_client_info_match)(void *src, void *dst);
++ void (*client_info_show)(struct seq_file *mount_option, void *data);
++ int (*remount_ip_list)(struct nfs_client *nfs_client,
++ void *enfs_option);
++};
++
++int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str,
++ struct nfs_parsed_mount_data *mnt);
++void enfs_free_mount_options(struct nfs_parsed_mount_data *data);
++int nfs_create_multi_path_client(struct nfs_client *client,
++ const struct nfs_client_initdata *cl_init);
++void nfs_free_multi_path_client(struct nfs_client *clp);
++int nfs_multipath_client_match(struct nfs_client *clp,
++ const struct nfs_client_initdata *sap);
++int nfs4_multipath_client_match(struct nfs_client *src, struct nfs_client *dst);
++void nfs_multipath_show_client_info(struct seq_file *mount_option,
++ struct nfs_server *server);
++int enfs_adapter_register(struct enfs_adapter_ops *ops);
++int enfs_adapter_unregister(struct enfs_adapter_ops *ops);
++int nfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option);
++int nfs4_create_multi_path(struct nfs_server *server,
++ struct nfs_parsed_mount_data *data,
++ const struct rpc_timeout *timeparms);
++
++#else
++static inline
++void nfs_free_multi_path_client(struct nfs_client *clp)
++{
++
++}
++
++static inline
++int nfs_multipath_client_match(struct nfs_client *clp,
++ const struct nfs_client_initdata *sap)
++{
++ return 1;
++}
++
++static inline
++int nfs_create_multi_path_client(struct nfs_client *client,
++ const struct nfs_client_initdata *cl_init)
++{
++ return 0;
++}
++
++static inline
++void nfs_multipath_show_client_info(struct seq_file *mount_option,
++ struct nfs_server *server)
++{
++
++}
++
++static inline
++int nfs4_multipath_client_match(struct nfs_client *src,
++ struct nfs_client *dst)
++{
++ return 1;
++}
++
++static inline
++void enfs_free_mount_options(struct nfs_parsed_mount_data *data)
++{
++
++}
++
++#endif // CONFIG_ENFS
++#endif // _NFS_MULTIPATH_H_
+diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
+index 0ce5a90640c4..c696693edc7b 100644
+--- a/fs/nfs/internal.h
++++ b/fs/nfs/internal.h
+@@ -93,6 +93,9 @@ struct nfs_client_initdata {
+ u32 minorversion;
+ struct net *net;
+ const struct rpc_timeout *timeparms;
++#if IS_ENABLED(CONFIG_ENFS)
++ void *enfs_option; /* struct multipath_mount_options * */
++#endif
+ };
+
+ /*
+@@ -135,6 +138,9 @@ struct nfs_parsed_mount_data {
+
+ struct security_mnt_opts lsm_opts;
+ struct net *net;
++#if IS_ENABLED(CONFIG_ENFS)
++ void *enfs_option; /* struct multipath_mount_options * */
++#endif
+ };
+
+ /* mount_clnt.c */
+diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
+index 1350ea673672..4aa6e1f961f7 100644
+--- a/fs/nfs/nfs4client.c
++++ b/fs/nfs/nfs4client.c
+@@ -10,7 +10,7 @@
+ #include <linux/sunrpc/xprt.h>
+ #include <linux/sunrpc/bc_xprt.h>
+ #include <linux/sunrpc/rpc_pipe_fs.h>
+-#include "internal.h"
++#include "enfs_adapter.h"
+ #include "callback.h"
+ #include "delegation.h"
+ #include "nfs4session.h"
+@@ -225,6 +225,16 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
+ __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
+ __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
+
++#if IS_ENABLED(CONFIG_ENFS)
++ err = nfs_create_multi_path_client(clp, cl_init);
++ if (err < 0) {
++ dprintk("%s: create failed.%d\n", __func__, err);
++ nfs_put_client(clp);
++ clp = ERR_PTR(err);
++ return clp;
++ }
++#endif
++
+ /*
+ * Set up the connection to the server before we add add to the
+ * global list.
+@@ -529,6 +539,9 @@ static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new,
+ if (!nfs4_match_client_owner_id(pos, new))
+ return 1;
+
++ if (!nfs4_multipath_client_match(pos, new))
++ return 1;
++
+ return 0;
+ }
+
+@@ -860,7 +873,7 @@ static int nfs4_set_client(struct nfs_server *server,
+ const size_t addrlen,
+ const char *ip_addr,
+ int proto, const struct rpc_timeout *timeparms,
+- u32 minorversion, struct net *net)
++ u32 minorversion, struct net *net, void *enfs_option)
+ {
+ struct nfs_client_initdata cl_init = {
+ .hostname = hostname,
+@@ -872,6 +885,9 @@ static int nfs4_set_client(struct nfs_server *server,
+ .minorversion = minorversion,
+ .net = net,
+ .timeparms = timeparms,
++#if IS_ENABLED(CONFIG_ENFS)
++ .enfs_option = enfs_option,
++#endif
+ };
+ struct nfs_client *clp;
+
+@@ -1042,6 +1058,30 @@ static int nfs4_server_common_setup(struct nfs_server *server,
+ return error;
+ }
+
++int nfs4_create_multi_path(struct nfs_server *server,
++ struct nfs_parsed_mount_data *data,
++ const struct rpc_timeout *timeparms)
++{
++ struct nfs_client_initdata cl_init = {
++ .hostname = data->nfs_server.hostname,
++ .addr = (const struct sockaddr *)&data->nfs_server.address,
++ .addrlen = data->nfs_server.addrlen,
++ .ip_addr = data->client_address,
++ .nfs_mod = &nfs_v4,
++ .proto = data->nfs_server.protocol,
++ .minorversion = data->minorversion,
++ .net = data->net,
++ .timeparms = timeparms,
++#if IS_ENABLED(CONFIG_ENFS)
++ .enfs_option = data->enfs_option,
++#endif // CONFIG_ENFS
++ };
++
++ return nfs_create_multi_path_client(server->nfs_client, &cl_init);
++
++}
++EXPORT_SYMBOL_GPL(nfs4_create_multi_path);
++
+ /*
+ * Create a version 4 volume record
+ */
+@@ -1050,6 +1090,7 @@ static int nfs4_init_server(struct nfs_server *server,
+ {
+ struct rpc_timeout timeparms;
+ int error;
++ void *enfs_option = NULL;
+
+ nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
+ data->timeo, data->retrans);
+@@ -1067,6 +1108,10 @@ static int nfs4_init_server(struct nfs_server *server,
+ else
+ data->selected_flavor = RPC_AUTH_UNIX;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ enfs_option = data->enfs_option;
++#endif
++
+ /* Get a client record */
+ error = nfs4_set_client(server,
+ data->nfs_server.hostname,
+@@ -1076,7 +1121,7 @@ static int nfs4_init_server(struct nfs_server *server,
+ data->nfs_server.protocol,
+ &timeparms,
+ data->minorversion,
+- data->net);
++ data->net, enfs_option);
+ if (error < 0)
+ return error;
+
+@@ -1161,7 +1206,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+ XPRT_TRANSPORT_RDMA,
+ parent_server->client->cl_timeout,
+ parent_client->cl_mvops->minor_version,
+- parent_client->cl_net);
++ parent_client->cl_net, NULL);
+ if (!error)
+ goto init_server;
+ #endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
+@@ -1174,7 +1219,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
+ XPRT_TRANSPORT_TCP,
+ parent_server->client->cl_timeout,
+ parent_client->cl_mvops->minor_version,
+- parent_client->cl_net);
++ parent_client->cl_net, NULL);
+ if (error < 0)
+ goto error;
+
+@@ -1269,7 +1314,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname,
+ set_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
+ error = nfs4_set_client(server, hostname, sap, salen, buf,
+ clp->cl_proto, clnt->cl_timeout,
+- clp->cl_minorversion, net);
++ clp->cl_minorversion, net, NULL);
+ clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
+ if (error != 0) {
+ nfs_server_insert_lists(server);
+diff --git a/fs/nfs/super.c b/fs/nfs/super.c
+index a05e1eb2c3fd..83cd294aca15 100644
+--- a/fs/nfs/super.c
++++ b/fs/nfs/super.c
+@@ -61,7 +61,7 @@
+ #include "callback.h"
+ #include "delegation.h"
+ #include "iostat.h"
+-#include "internal.h"
++#include "enfs_adapter.h"
+ #include "fscache.h"
+ #include "nfs4session.h"
+ #include "pnfs.h"
+@@ -113,6 +113,12 @@ enum {
+
+ /* Special mount options */
+ Opt_userspace, Opt_deprecated, Opt_sloppy,
++#if IS_ENABLED(CONFIG_ENFS)
++ Opt_remote_iplist,
++ Opt_local_iplist,
++ Opt_remote_dnslist,
++ Opt_enfs_info,
++#endif
+
+ Opt_err
+ };
+@@ -183,6 +189,13 @@ static const match_table_t nfs_mount_option_tokens = {
+ { Opt_fscache_uniq, "fsc=%s" },
+ { Opt_local_lock, "local_lock=%s" },
+
++#if IS_ENABLED(CONFIG_ENFS)
++ { Opt_remote_iplist, "remoteaddrs=%s" },
++ { Opt_local_iplist, "localaddrs=%s" },
++ { Opt_remote_dnslist, "remotednsname=%s" },
++ { Opt_enfs_info, "enfs_info=%s" },
++#endif
++
+ /* The following needs to be listed after all other options */
+ { Opt_nfsvers, "v%s" },
+
+@@ -365,6 +378,21 @@ static struct shrinker acl_shrinker = {
+ .seeks = DEFAULT_SEEKS,
+ };
+
++#if IS_ENABLED(CONFIG_ENFS)
++enum nfsmultipathoptions getNfsMultiPathOpt(int token)
++{
++ switch (token) {
++ case Opt_remote_iplist:
++ return REMOUNTREMOTEADDR;
++ case Opt_local_iplist:
++ return REMOUNTLOCALADDR;
++ case Opt_remote_dnslist:
++ return REMOTEDNSNAME;
++ }
++ return INVALID_OPTION;
++}
++#endif
++
+ /*
+ * Register the NFS filesystems
+ */
+@@ -758,6 +786,9 @@ int nfs_show_options(struct seq_file *m, struct dentry *root)
+ seq_printf(m, ",addr=%s",
+ rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient,
+ RPC_DISPLAY_ADDR));
++
++ nfs_multipath_show_client_info(m, nfss);
++
+ rcu_read_unlock();
+
+ return 0;
+@@ -853,6 +884,8 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root)
+ seq_puts(m, root->d_sb->s_flags & SB_NODIRATIME ? ",nodiratime" : "");
+ nfs_show_mount_options(m, nfss, 1);
+
++ nfs_multipath_show_client_info(m, nfss);
++
+ seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
+
+ show_implementation_id(m, nfss);
+@@ -977,6 +1010,7 @@ static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data)
+ kfree(data->nfs_server.export_path);
+ kfree(data->nfs_server.hostname);
+ kfree(data->fscache_uniq);
++ enfs_free_mount_options(data);
+ security_free_mnt_opts(&data->lsm_opts);
+ kfree(data);
+ }
+@@ -1641,7 +1675,34 @@ static int nfs_parse_mount_options(char *raw,
+ return 0;
+ };
+ break;
+-
++#if IS_ENABLED(CONFIG_ENFS)
++ case Opt_remote_iplist:
++ case Opt_local_iplist:
++ case Opt_remote_dnslist:
++ string = match_strdup(args);
++ if (string == NULL)
++ goto out_nomem;
++ rc = enfs_parse_mount_options(getNfsMultiPathOpt(token),
++ string, mnt);
++ kfree(string);
++ switch (rc) {
++ case 0:
++ break;
++ case -ENOMEM:
++ goto out_nomem;
++ case -ENOSPC:
++ goto out_limit;
++ case -EINVAL:
++ goto out_invalid_address;
++ case -ENOTSUPP:
++ goto out_invalid_address;
++ case -EOPNOTSUPP:
++ goto out_invalid_address;
++ }
++ break;
++ case Opt_enfs_info:
++ break;
++#endif
+ /*
+ * Special options
+ */
+@@ -1720,6 +1781,11 @@ static int nfs_parse_mount_options(char *raw,
+ free_secdata(secdata);
+ printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
+ return 0;
++#if IS_ENABLED(CONFIG_ENFS)
++out_limit:
++ dprintk("NFS: param is more than supported limit: %d\n", rc);
++ return 0;
++#endif
+ }
+
+ /*
+@@ -2335,6 +2401,14 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
+ if (!nfs_parse_mount_options((char *)options, data))
+ goto out;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ if (data->enfs_option) {
++ error = nfs_remount_iplist(nfss->nfs_client, data->enfs_option);
++ if (error)
++ goto out;
++ }
++#endif
++
+ /*
+ * noac is a special case. It implies -o sync, but that's not
+ * necessarily reflected in the mtab options. do_remount_sb
+@@ -2347,6 +2421,11 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
+ /* compare new mount options with old ones */
+ error = nfs_compare_remount_data(nfss, data);
+ out:
++#if IS_ENABLED(CONFIG_ENFS)
++ /* release remount option member */
++ if (data->enfs_option)
++ enfs_free_mount_options(data);
++#endif
+ nfs_free_parsed_mount_data(data);
+ return error;
+ }
+diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
+index 7023ae64e3d7..2c19678afe8d 100644
+--- a/include/linux/nfs_fs_sb.h
++++ b/include/linux/nfs_fs_sb.h
+@@ -123,6 +123,11 @@ struct nfs_client {
+
+ struct net *cl_net;
+ struct list_head pending_cb_stateids;
++
++#if IS_ENABLED(CONFIG_ENFS)
++ /* multi path private structure (struct multipath_client_info *) */
++ void *cl_multipath_data;
++#endif
+ };
+
+ /*
diff --git a/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
new file mode 100644
index 0000000..540a2ce
--- /dev/null
+++ b/0002-sunrpc_add_api_to_support_enfs_registe_and_create_multipath_then_dispatch_IO.patch
@@ -0,0 +1,805 @@
+diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
+index 8aa865bce4f6..89178f78de8c 100644
+--- a/include/linux/sunrpc/clnt.h
++++ b/include/linux/sunrpc/clnt.h
+@@ -70,6 +70,10 @@ struct rpc_clnt {
+ struct dentry *cl_debugfs; /* debugfs directory */
+ #endif
+ struct rpc_xprt_iter cl_xpi;
++
++#if IS_ENABLED(CONFIG_ENFS)
++ bool cl_enfs;
++#endif
+ };
+
+ /*
+@@ -124,6 +128,9 @@ struct rpc_create_args {
+ unsigned long flags;
+ char *client_name;
+ struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */
++#if IS_ENABLED(CONFIG_ENFS)
++ void *multipath_option;
++#endif
+ };
+
+ struct rpc_add_xprt_test {
+@@ -221,6 +228,12 @@ bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+ const struct sockaddr *sap);
+ void rpc_cleanup_clids(void);
+
++#if IS_ENABLED(CONFIG_ENFS)
++int
++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
++ const struct rpc_call_ops *ops, void *data, int flags);
++#endif /* CONFIG_ENFS */
++
+ static inline int rpc_reply_expected(struct rpc_task *task)
+ {
+ return (task->tk_msg.rpc_proc != NULL) &&
+diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
+index ad2e243f3f03..124f5a0faf3e 100644
+--- a/include/linux/sunrpc/sched.h
++++ b/include/linux/sunrpc/sched.h
+@@ -90,6 +90,9 @@ struct rpc_task {
+ tk_garb_retry : 2,
+ tk_cred_retry : 2,
+ tk_rebind_retry : 2;
++#if IS_ENABLED(CONFIG_ENFS)
++ unsigned long tk_major_timeo; /* major timeout ticks */
++#endif
+ };
+
+ typedef void (*rpc_action)(struct rpc_task *);
+@@ -118,6 +121,9 @@ struct rpc_task_setup {
+ */
+ #define RPC_TASK_ASYNC 0x0001 /* is an async task */
+ #define RPC_TASK_SWAPPER 0x0002 /* is swapping in/out */
++#if IS_ENABLED(CONFIG_ENFS)
++#define RPC_TASK_FIXED 0x0004 /* detect xprt status task */
++#endif
+ #define RPC_CALL_MAJORSEEN 0x0020 /* major timeout seen */
+ #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */
+ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
+@@ -257,6 +263,9 @@ void rpc_destroy_mempool(void);
+ extern struct workqueue_struct *rpciod_workqueue;
+ extern struct workqueue_struct *xprtiod_workqueue;
+ void rpc_prepare_task(struct rpc_task *task);
++#if IS_ENABLED(CONFIG_ENFS)
++void rpc_init_task_retry_counters(struct rpc_task *task);
++#endif
+
+ static inline int rpc_wait_for_completion_task(struct rpc_task *task)
+ {
+diff --git a/include/linux/sunrpc/sunrpc_enfs_adapter.h b/include/linux/sunrpc/sunrpc_enfs_adapter.h
+new file mode 100644
+index 000000000000..28abedcf5cf6
+--- /dev/null
++++ b/include/linux/sunrpc/sunrpc_enfs_adapter.h
+@@ -0,0 +1,128 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/* Client-side SUNRPC ENFS adapter header.
++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
++ */
++#ifndef _SUNRPC_ENFS_ADAPTER_H_
++#define _SUNRPC_ENFS_ADAPTER_H_
++#include <linux/sunrpc/clnt.h>
++
++#if IS_ENABLED(CONFIG_ENFS)
++
++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
++{
++ xps->xps_nactive--;
++}
++
++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
++{
++ xps->xps_nactive--;
++}
++
++struct rpc_xprt *rpc_task_get_xprt
++(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
++
++struct rpc_multipath_ops {
++ struct module *owner;
++ void (*create_clnt)(struct rpc_create_args *args,
++ struct rpc_clnt *clnt);
++ void (*releas_clnt)(struct rpc_clnt *clnt);
++ void (*create_xprt)(struct rpc_xprt *xprt);
++ void (*destroy_xprt)(struct rpc_xprt *xprt);
++ void (*xprt_iostat)(struct rpc_task *task);
++ void (*failover_handle)(struct rpc_task *task);
++ bool (*task_need_call_start_again)(struct rpc_task *task);
++ void (*adjust_task_timeout)(struct rpc_task *task, void *condition);
++ void (*init_task_req)(struct rpc_task *task, struct rpc_rqst *req);
++ bool (*prepare_transmit)(struct rpc_task *task);
++};
++
++extern struct rpc_multipath_ops __rcu *multipath_ops;
++void rpc_init_task_retry_counters(struct rpc_task *task);
++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops);
++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops);
++struct rpc_multipath_ops *rpc_multipath_ops_get(void);
++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops);
++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
++ struct rpc_clnt *clnt);
++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt);
++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt);
++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt);
++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task);
++void rpc_multipath_ops_failover_handle(struct rpc_task *task);
++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task);
++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
++ void *condition);
++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
++ struct rpc_rqst *req);
++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task);
++
++#else
++static inline struct rpc_xprt *rpc_task_get_xprt(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt)
++{
++ return NULL;
++}
++
++static inline void rpc_task_release_xprt(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt)
++{
++}
++
++static inline void rpc_xps_nactive_add_one(struct rpc_xprt_switch *xps)
++{
++}
++
++static inline void rpc_xps_nactive_sub_one(struct rpc_xprt_switch *xps)
++{
++}
++
++static inline void rpc_multipath_ops_create_clnt
++(struct rpc_create_args *args, struct rpc_clnt *clnt)
++{
++}
++
++static inline void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
++{
++}
++
++static inline bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
++{
++ return false;
++}
++
++static inline void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
++{
++}
++
++static inline void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
++{
++}
++
++static inline void rpc_multipath_ops_failover_handle(struct rpc_task *task)
++{
++}
++
++static inline
++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
++{
++ return false;
++}
++
++static inline void
++rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task, void *condition)
++{
++}
++
++static inline void
++rpc_multipath_ops_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
++{
++}
++
++static inline bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
++{
++ return false;
++}
++
++#endif
++#endif // _SUNRPC_ENFS_ADAPTER_H_
+diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
+index ccfacca1eba9..2e47b3577947 100644
+--- a/include/linux/sunrpc/xprt.h
++++ b/include/linux/sunrpc/xprt.h
+@@ -279,6 +279,10 @@ struct rpc_xprt {
+ atomic_t inject_disconnect;
+ #endif
+ struct rcu_head rcu;
++#if IS_ENABLED(CONFIG_ENFS)
++ atomic_long_t queuelen;
++ void *multipath_context;
++#endif
+ };
+
+ #if defined(CONFIG_SUNRPC_BACKCHANNEL)
+diff --git a/include/linux/sunrpc/xprtmultipath.h b/include/linux/sunrpc/xprtmultipath.h
+index af1257c030d2..d54e4dbbbf34 100644
+--- a/include/linux/sunrpc/xprtmultipath.h
++++ b/include/linux/sunrpc/xprtmultipath.h
+@@ -22,6 +22,10 @@ struct rpc_xprt_switch {
+ const struct rpc_xprt_iter_ops *xps_iter_ops;
+
+ struct rcu_head xps_rcu;
++#if IS_ENABLED(CONFIG_ENFS)
++ unsigned int xps_nactive;
++ atomic_long_t xps_queuelen;
++#endif
+ };
+
+ struct rpc_xprt_iter {
+@@ -69,4 +73,8 @@ extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
+
+ extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
+ const struct sockaddr *sap);
++#if IS_ENABLED(CONFIG_ENFS)
++extern void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++ struct rpc_xprt *xprt);
++#endif
+ #endif
+diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
+index 0fc540b0d183..d7ffee637148 100644
+--- a/net/sunrpc/clnt.c
++++ b/net/sunrpc/clnt.c
+@@ -37,6 +37,7 @@
+ #include <linux/sunrpc/rpc_pipe_fs.h>
+ #include <linux/sunrpc/metrics.h>
+ #include <linux/sunrpc/bc_xprt.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+ #include <trace/events/sunrpc.h>
+
+ #include "sunrpc.h"
+@@ -490,6 +491,8 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
+ }
+ }
+
++ rpc_multipath_ops_create_clnt(args, clnt);
++
+ clnt->cl_softrtry = 1;
+ if (args->flags & RPC_CLNT_CREATE_HARDRTRY)
+ clnt->cl_softrtry = 0;
+@@ -869,6 +872,8 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)
+ list_empty(&clnt->cl_tasks), 1*HZ);
+ }
+
++ rpc_multipath_ops_releas_clnt(clnt);
++
+ rpc_release_client(clnt);
+ }
+ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
+@@ -981,7 +986,13 @@ void rpc_task_release_transport(struct rpc_task *task)
+
+ if (xprt) {
+ task->tk_xprt = NULL;
+- xprt_put(xprt);
++#if IS_ENABLED(CONFIG_ENFS)
++ if (task->tk_client) {
++ rpc_task_release_xprt(task->tk_client, xprt);
++ return;
++ }
++#endif
++ xprt_put(xprt);
+ }
+ }
+ EXPORT_SYMBOL_GPL(rpc_task_release_transport);
+@@ -990,6 +1001,10 @@ void rpc_task_release_client(struct rpc_task *task)
+ {
+ struct rpc_clnt *clnt = task->tk_client;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ rpc_task_release_transport(task);
++#endif
++
+ if (clnt != NULL) {
+ /* Remove from client task list */
+ spin_lock(&clnt->cl_lock);
+@@ -999,14 +1014,29 @@ void rpc_task_release_client(struct rpc_task *task)
+
+ rpc_release_client(clnt);
+ }
++#if IS_ENABLED(CONFIG_ENFS)
++#else
+ rpc_task_release_transport(task);
++#endif
+ }
+
++#if IS_ENABLED(CONFIG_ENFS)
++static struct rpc_xprt *
++rpc_task_get_next_xprt(struct rpc_clnt *clnt)
++{
++ return rpc_task_get_xprt(clnt, xprt_iter_get_next(&clnt->cl_xpi));
++}
++#endif
++
+ static
+ void rpc_task_set_transport(struct rpc_task *task, struct rpc_clnt *clnt)
+ {
+ if (!task->tk_xprt)
++#if IS_ENABLED(CONFIG_ENFS)
++ task->tk_xprt = rpc_task_get_next_xprt(clnt);
++#else
+ task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
++#endif
+ }
+
+ static
+@@ -1597,6 +1627,14 @@ call_reserveresult(struct rpc_task *task)
+ return;
+ case -EIO: /* probably a shutdown */
+ break;
++#if IS_ENABLED(CONFIG_ENFS)
++ case -ETIMEDOUT: /* woken up; restart */
++ if (rpc_multipath_ops_task_need_call_start_again(task)) {
++ rpc_task_release_transport(task);
++ task->tk_action = call_start;
++ return;
++ }
++#endif
+ default:
+ printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
+ __func__, status);
+@@ -1962,6 +2000,10 @@ call_transmit(struct rpc_task *task)
+ return;
+ if (!xprt_prepare_transmit(task))
+ return;
++
++ if (rpc_multipath_ops_prepare_transmit(task))
++ return;
++
+ task->tk_action = call_transmit_status;
+ /* Encode here so that rpcsec_gss can use correct sequence number. */
+ if (rpc_task_need_encode(task)) {
+@@ -2277,6 +2319,9 @@ call_timeout(struct rpc_task *task)
+
+ retry:
+ task->tk_action = call_bind;
++#if IS_ENABLED(CONFIG_ENFS)
++ rpc_multipath_ops_failover_handle(task);
++#endif
+ task->tk_status = 0;
+ }
+
+@@ -2961,3 +3006,30 @@ rpc_clnt_swap_deactivate(struct rpc_clnt *clnt)
+ }
+ EXPORT_SYMBOL_GPL(rpc_clnt_swap_deactivate);
+ #endif /* CONFIG_SUNRPC_SWAP */
++
++#if IS_ENABLED(CONFIG_ENFS)
++/* rpc_clnt_test_xprt - Test and add a new transport to a rpc_clnt
++ * @clnt: pointer to struct rpc_clnt
++ * @xprt: pointer struct rpc_xprt
++ * @ops: async operation
++ */
++int
++rpc_clnt_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
++ const struct rpc_call_ops *ops, void *data, int flags)
++{
++ struct rpc_cred *cred;
++ struct rpc_task *task;
++
++ cred = authnull_ops.lookup_cred(NULL, NULL, 0);
++ task = rpc_call_null_helper(clnt, xprt, cred,
++ RPC_TASK_SOFT | RPC_TASK_SOFTCONN | flags,
++ ops, data);
++ put_rpccred(cred);
++ if (IS_ERR(task))
++ return PTR_ERR(task);
++
++ rpc_put_task(task);
++ return 1;
++}
++EXPORT_SYMBOL_GPL(rpc_clnt_test_xprt);
++#endif
+diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
+index a873c92a4898..2254fea0e863 100644
+--- a/net/sunrpc/sched.c
++++ b/net/sunrpc/sched.c
+@@ -20,7 +20,7 @@
+ #include <linux/mutex.h>
+ #include <linux/freezer.h>
+
+-#include <linux/sunrpc/clnt.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+
+ #include "sunrpc.h"
+
+@@ -962,7 +962,12 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
+ /* Initialize workqueue for async tasks */
+ task->tk_workqueue = task_setup_data->workqueue;
+
++#if IS_ENABLED(CONFIG_ENFS)
++ task->tk_xprt = rpc_task_get_xprt(task_setup_data->rpc_client,
++ xprt_get(task_setup_data->rpc_xprt));
++#else
+ task->tk_xprt = xprt_get(task_setup_data->rpc_xprt);
++#endif
+
+ if (task->tk_ops->rpc_call_prepare != NULL)
+ task->tk_action = rpc_prepare_task;
+diff --git a/net/sunrpc/sunrpc_enfs_adapter.c b/net/sunrpc/sunrpc_enfs_adapter.c
+new file mode 100644
+index 000000000000..c1543545c6de
+--- /dev/null
++++ b/net/sunrpc/sunrpc_enfs_adapter.c
+@@ -0,0 +1,214 @@
++// SPDX-License-Identifier: GPL-2.0
++/* Client-side SUNRPC ENFS adapter header.
++ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
++ */
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
++
++struct rpc_multipath_ops __rcu *multipath_ops;
++
++void rpc_init_task_retry_counters(struct rpc_task *task)
++{
++ /* Initialize retry counters */
++ task->tk_garb_retry = 2;
++ task->tk_cred_retry = 2;
++ task->tk_rebind_retry = 2;
++}
++EXPORT_SYMBOL_GPL(rpc_init_task_retry_counters);
++
++struct rpc_xprt *
++rpc_task_get_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
++{
++ struct rpc_xprt_switch *xps;
++
++ if (!xprt)
++ return NULL;
++ rcu_read_lock();
++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
++ atomic_long_inc(&xps->xps_queuelen);
++ rcu_read_unlock();
++ atomic_long_inc(&xprt->queuelen);
++
++ return xprt;
++}
++
++int rpc_multipath_ops_register(struct rpc_multipath_ops *ops)
++{
++ struct rpc_multipath_ops *old;
++
++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, NULL, ops);
++ if (!old || old == ops)
++ return 0;
++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_register);
++
++int rpc_multipath_ops_unregister(struct rpc_multipath_ops *ops)
++{
++ struct rpc_multipath_ops *old;
++
++ old = cmpxchg((struct rpc_multipath_ops **)&multipath_ops, ops, NULL);
++ if (!old || old == ops)
++ return 0;
++ pr_err("regist rpc_multipath ops %p fail. old %p\n", ops, old);
++ return -EPERM;
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_unregister);
++
++struct rpc_multipath_ops *rpc_multipath_ops_get(void)
++{
++ struct rpc_multipath_ops *ops;
++
++ rcu_read_lock();
++ ops = rcu_dereference(multipath_ops);
++ if (!ops) {
++ rcu_read_unlock();
++ return NULL;
++ }
++ if (!try_module_get(ops->owner))
++ ops = NULL;
++ rcu_read_unlock();
++ return ops;
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_get);
++
++void rpc_multipath_ops_put(struct rpc_multipath_ops *ops)
++{
++ if (ops)
++ module_put(ops->owner);
++}
++EXPORT_SYMBOL_GPL(rpc_multipath_ops_put);
++
++void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
++{
++ struct rpc_xprt_switch *xps;
++
++ atomic_long_dec(&xprt->queuelen);
++ rcu_read_lock();
++ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
++ atomic_long_dec(&xps->xps_queuelen);
++ rcu_read_unlock();
++
++ xprt_put(xprt);
++}
++
++void rpc_multipath_ops_create_clnt(struct rpc_create_args *args,
++ struct rpc_clnt *clnt)
++{
++ struct rpc_multipath_ops *mops;
++
++ if (args->multipath_option) {
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->create_clnt)
++ mops->create_clnt(args, clnt);
++ rpc_multipath_ops_put(mops);
++ }
++}
++
++void rpc_multipath_ops_releas_clnt(struct rpc_clnt *clnt)
++{
++ struct rpc_multipath_ops *mops;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->releas_clnt)
++ mops->releas_clnt(clnt);
++
++ rpc_multipath_ops_put(mops);
++}
++
++bool rpc_multipath_ops_create_xprt(struct rpc_xprt *xprt)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->create_xprt) {
++ mops->create_xprt(xprt);
++ if (!xprt->multipath_context) {
++ rpc_multipath_ops_put(mops);
++ return true;
++ }
++ }
++ rpc_multipath_ops_put(mops);
++ return false;
++}
++
++void rpc_multipath_ops_destroy_xprt(struct rpc_xprt *xprt)
++{
++ struct rpc_multipath_ops *mops;
++
++ if (xprt->multipath_context) {
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->destroy_xprt)
++ mops->destroy_xprt(xprt);
++ rpc_multipath_ops_put(mops);
++ }
++}
++
++void rpc_multipath_ops_xprt_iostat(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mops;
++
++ mops = rpc_multipath_ops_get();
++ if (task->tk_client && mops && mops->xprt_iostat)
++ mops->xprt_iostat(task);
++ rpc_multipath_ops_put(mops);
++}
++
++void rpc_multipath_ops_failover_handle(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mpath_ops = NULL;
++
++ mpath_ops = rpc_multipath_ops_get();
++ if (mpath_ops && mpath_ops->failover_handle)
++ mpath_ops->failover_handle(task);
++ rpc_multipath_ops_put(mpath_ops);
++}
++
++bool rpc_multipath_ops_task_need_call_start_again(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mpath_ops = NULL;
++ bool ret = false;
++
++ mpath_ops = rpc_multipath_ops_get();
++ if (mpath_ops && mpath_ops->task_need_call_start_again)
++ ret = mpath_ops->task_need_call_start_again(task);
++ rpc_multipath_ops_put(mpath_ops);
++ return ret;
++}
++
++void rpc_multipath_ops_adjust_task_timeout(struct rpc_task *task,
++ void *condition)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->adjust_task_timeout)
++ mops->adjust_task_timeout(task, NULL);
++ rpc_multipath_ops_put(mops);
++}
++
++void rpc_multipath_ops_init_task_req(struct rpc_task *task,
++ struct rpc_rqst *req)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->init_task_req)
++ mops->init_task_req(task, req);
++ rpc_multipath_ops_put(mops);
++}
++
++bool rpc_multipath_ops_prepare_transmit(struct rpc_task *task)
++{
++ struct rpc_multipath_ops *mops = NULL;
++
++ mops = rpc_multipath_ops_get();
++ if (mops && mops->prepare_transmit) {
++ if (!(mops->prepare_transmit(task))) {
++ rpc_multipath_ops_put(mops);
++ return true;
++ }
++ }
++ rpc_multipath_ops_put(mops);
++ return false;
++}
+diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
+index c912bf20faa2..c2b63b3d5217 100644
+--- a/net/sunrpc/xprt.c
++++ b/net/sunrpc/xprt.c
+@@ -48,6 +48,7 @@
+ #include <linux/sunrpc/clnt.h>
+ #include <linux/sunrpc/metrics.h>
+ #include <linux/sunrpc/bc_xprt.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+ #include <linux/rcupdate.h>
+
+ #include <trace/events/sunrpc.h>
+@@ -259,6 +260,9 @@ int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
+ dprintk("RPC: %5u failed to lock transport %p\n",
+ task->tk_pid, xprt);
+ task->tk_timeout = 0;
++
++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
++
+ task->tk_status = -EAGAIN;
+ if (req == NULL)
+ priority = RPC_PRIORITY_LOW;
+@@ -560,6 +564,9 @@ void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action)
+ struct rpc_xprt *xprt = req->rq_xprt;
+
+ task->tk_timeout = RPC_IS_SOFT(task) ? req->rq_timeout : 0;
++
++ rpc_multipath_ops_adjust_task_timeout(task, NULL);
++
+ rpc_sleep_on(&xprt->pending, task, action);
+ }
+ EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space);
+@@ -1347,6 +1354,9 @@ xprt_request_init(struct rpc_task *task)
+ req->rq_rcv_buf.buflen = 0;
+ req->rq_release_snd_buf = NULL;
+ xprt_reset_majortimeo(req);
++
++ rpc_multipath_ops_init_task_req(task, req);
++
+ dprintk("RPC: %5u reserved req %p xid %08x\n", task->tk_pid,
+ req, ntohl(req->rq_xid));
+ }
+@@ -1427,6 +1437,9 @@ void xprt_release(struct rpc_task *task)
+ task->tk_ops->rpc_count_stats(task, task->tk_calldata);
+ else if (task->tk_client)
+ rpc_count_iostats(task, task->tk_client->cl_metrics);
++
++ rpc_multipath_ops_xprt_iostat(task);
++
+ spin_lock(&xprt->recv_lock);
+ if (!list_empty(&req->rq_list)) {
+ list_del_init(&req->rq_list);
+@@ -1455,6 +1468,7 @@ void xprt_release(struct rpc_task *task)
+ else
+ xprt_free_bc_request(req);
+ }
++EXPORT_SYMBOL_GPL(xprt_release);
+
+ static void xprt_init(struct rpc_xprt *xprt, struct net *net)
+ {
+@@ -1528,6 +1542,10 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
+ return ERR_PTR(-ENOMEM);
+ }
+
++if (rpc_multipath_ops_create_xprt(xprt)) {
++ xprt_destroy(xprt);
++ return ERR_PTR(-ENOMEM);
++}
+ rpc_xprt_debugfs_register(xprt);
+
+ dprintk("RPC: created transport %p with %u slots\n", xprt,
+@@ -1547,6 +1565,9 @@ static void xprt_destroy_cb(struct work_struct *work)
+ rpc_destroy_wait_queue(&xprt->sending);
+ rpc_destroy_wait_queue(&xprt->backlog);
+ kfree(xprt->servername);
++
++ rpc_multipath_ops_destroy_xprt(xprt);
++
+ /*
+ * Tear down transport state and free the rpc_xprt
+ */
+diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c
+index 6ebaa58b4eff..6202a0be1327 100644
+--- a/net/sunrpc/xprtmultipath.c
++++ b/net/sunrpc/xprtmultipath.c
+@@ -18,6 +18,7 @@
+ #include <linux/sunrpc/xprt.h>
+ #include <linux/sunrpc/addr.h>
+ #include <linux/sunrpc/xprtmultipath.h>
++#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+
+ typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct list_head *head,
+ const struct rpc_xprt *cur);
+@@ -26,8 +27,8 @@ static const struct rpc_xprt_iter_ops rpc_xprt_iter_singular;
+ static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin;
+ static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall;
+
+-static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+- struct rpc_xprt *xprt)
++void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
++ struct rpc_xprt *xprt)
+ {
+ if (unlikely(xprt_get(xprt) == NULL))
+ return;
+@@ -36,7 +37,9 @@ static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps,
+ if (xps->xps_nxprts == 0)
+ xps->xps_net = xprt->xprt_net;
+ xps->xps_nxprts++;
++ rpc_xps_nactive_add_one(xps);
+ }
++EXPORT_SYMBOL(xprt_switch_add_xprt_locked);
+
+ /**
+ * rpc_xprt_switch_add_xprt - Add a new rpc_xprt to an rpc_xprt_switch
+@@ -63,6 +66,7 @@ static void xprt_switch_remove_xprt_locked(struct rpc_xprt_switch *xps,
+ if (unlikely(xprt == NULL))
+ return;
+ xps->xps_nxprts--;
++ rpc_xps_nactive_sub_one(xps);
+ if (xps->xps_nxprts == 0)
+ xps->xps_net = NULL;
+ smp_wmb();
+@@ -84,7 +88,7 @@ void rpc_xprt_switch_remove_xprt(struct rpc_xprt_switch *xps,
+ spin_unlock(&xps->xps_lock);
+ xprt_put(xprt);
+ }
+-
++EXPORT_SYMBOL(rpc_xprt_switch_remove_xprt);
+ /**
+ * xprt_switch_alloc - Allocate a new struct rpc_xprt_switch
+ * @xprt: pointer to struct rpc_xprt
+@@ -102,7 +106,13 @@ struct rpc_xprt_switch *xprt_switch_alloc(struct rpc_xprt *xprt,
+ if (xps != NULL) {
+ spin_lock_init(&xps->xps_lock);
+ kref_init(&xps->xps_kref);
++#if IS_ENABLED(CONFIG_ENFS)
++ xps->xps_nxprts = 0;
++ xps->xps_nactive = 0;
++ atomic_long_set(&xps->xps_queuelen, 0);
++#else
+ xps->xps_nxprts = 0;
++#endif
+ INIT_LIST_HEAD(&xps->xps_xprt_list);
+ xps->xps_iter_ops = &rpc_xprt_iter_singular;
+ xprt_switch_add_xprt_locked(xps, xprt);
+@@ -148,6 +158,7 @@ struct rpc_xprt_switch *xprt_switch_get(struct rpc_xprt_switch *xps)
+ return xps;
+ return NULL;
+ }
++EXPORT_SYMBOL(xprt_switch_get);
+
+ /**
+ * xprt_switch_put - Release a reference to a rpc_xprt_switch
+@@ -160,6 +171,7 @@ void xprt_switch_put(struct rpc_xprt_switch *xps)
+ if (xps != NULL)
+ kref_put(&xps->xps_kref, xprt_switch_free);
+ }
++EXPORT_SYMBOL(xprt_switch_put);
+
+ /**
+ * rpc_xprt_switch_set_roundrobin - Set a round-robin policy on rpc_xprt_switch
diff --git a/0003-add_enfs_module_for_nfs_mount_option.patch b/0003-add_enfs_module_for_nfs_mount_option.patch
new file mode 100644
index 0000000..70753b5
--- /dev/null
+++ b/0003-add_enfs_module_for_nfs_mount_option.patch
@@ -0,0 +1,1209 @@
+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..9c4c6c1880b6
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_multipath_parse.c
+@@ -0,0 +1,601 @@
++// 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/parser.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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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 nfsmultipathoptions 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..6f3e8703e3e2
+--- /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 nfsmultipathoptions type,
++ char *str, void **enfs_option, struct net *net_ns);
++void nfs_multipath_free_options(void **enfs_option);
++
++#endif
diff --git a/0004-add_enfs_module_for_sunrpc_multipatch.patch b/0004-add_enfs_module_for_sunrpc_multipatch.patch
new file mode 100644
index 0000000..2c0fcc7
--- /dev/null
+++ b/0004-add_enfs_module_for_sunrpc_multipatch.patch
@@ -0,0 +1,1581 @@
+diff --git a/fs/nfs/enfs/enfs_multipath.h b/fs/nfs/enfs/enfs_multipath.h
+new file mode 100644
+index 000000000000..e064c2929ced
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_multipath.h
+@@ -0,0 +1,24 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: enfs multipath
++ * Author:
++ * 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_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_path.c b/fs/nfs/enfs/enfs_path.c
+new file mode 100644
+index 000000000000..7355f8c2f672
+--- /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 000000000000..97b1ef3730b8
+--- /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 000000000000..53fa1a07642f
+--- /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 000000000000..321951031c2e
+--- /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 000000000000..2c3fe125c735
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_remount.c
+@@ -0,0 +1,221 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: remount ip source file
++ * Author: y00583252
++ * 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 000000000000..a663ed257004
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_remount.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: remount ip header file
++ * Author: y00583252
++ * 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 000000000000..4e4eda784a3e
+--- /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 000000000000..b72b088a6258
+--- /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
diff --git a/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
new file mode 100644
index 0000000..cc6b677
--- /dev/null
+++ b/0005-add_enfs_module_for_sunrpc_failover_and_configure.patch
@@ -0,0 +1,1607 @@
+diff --git a/fs/nfs/enfs/enfs_config.c b/fs/nfs/enfs/enfs_config.c
+new file mode 100644
+index 000000000000..11aa7a00385b
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_config.c
+@@ -0,0 +1,378 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ */
++#include <linux/cdev.h>
++#include <linux/errno.h>
++#include <linux/fcntl.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/kthread.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/uaccess.h>
++#include <linux/delay.h>
++
++#include "enfs_errcode.h"
++#include "enfs_log.h"
++#include "enfs_config.h"
++
++#define MAX_FILE_SIZE 8192
++#define STRING_BUF_SIZE 128
++#define CONFIG_FILE_PATH "/etc/enfs/config.ini"
++#define ENFS_NOTIFY_FILE_PERIOD 1000UL
++
++#define MAX_PATH_DETECT_INTERVAL 300
++#define MIN_PATH_DETECT_INTERVAL 5
++#define MAX_PATH_DETECT_TIMEOUT 60
++#define MIN_PATH_DETECT_TIMEOUT 1
++#define MAX_MULTIPATH_TIMEOUT 60
++#define MIN_MULTIPATH_TIMEOUT 0
++#define MAX_MULTIPATH_STATE ENFS_MULTIPATH_DISABLE
++#define MIN_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
++
++#define DEFAULT_PATH_DETECT_INTERVAL 10
++#define DEFAULT_PATH_DETECT_TIMEOUT 5
++#define DEFAULT_MULTIPATH_TIMEOUT 0
++#define DEFAULT_MULTIPATH_STATE ENFS_MULTIPATH_ENABLE
++#define DEFAULT_LOADBALANCE_MODE ENFS_LOADBALANCE_RR
++
++typedef int (*check_and_assign_func)(char *, char *, int, int);
++
++struct enfs_config_info {
++ int32_t path_detect_interval;
++ int32_t path_detect_timeout;
++ int32_t multipath_timeout;
++ int32_t loadbalance_mode;
++ int32_t multipath_state;
++};
++
++struct check_and_assign_value {
++ char *field_name;
++ check_and_assign_func func;
++ int min_value;
++ int max_value;
++};
++
++static struct enfs_config_info g_enfs_config_info;
++static struct timespec64 modify_time;
++static struct task_struct *thread;
++
++static int enfs_check_config_value(char *value, int min_value, int max_value)
++{
++ unsigned long num_value;
++ int ret;
++
++ ret = kstrtol(value, 10, &num_value);
++ if (ret != 0) {
++ enfs_log_error("Failed to convert string to int\n");
++ return -EINVAL;
++ }
++
++ if (num_value < min_value || num_value > max_value)
++ return -EINVAL;
++
++ return num_value;
++}
++
++static int32_t enfs_check_and_assign_int_value(char *field_name, char *value,
++ int min_value, int max_value)
++{
++ int int_value = enfs_check_config_value(value, min_value, max_value);
++
++ if (int_value < 0)
++ return -EINVAL;
++
++ if (strcmp(field_name, "path_detect_interval") == 0) {
++ g_enfs_config_info.path_detect_interval = int_value;
++ return ENFS_RET_OK;
++ }
++ if (strcmp(field_name, "path_detect_timeout") == 0) {
++ g_enfs_config_info.path_detect_timeout = int_value;
++ return ENFS_RET_OK;
++ }
++ if (strcmp(field_name, "multipath_timeout") == 0) {
++ g_enfs_config_info.multipath_timeout = int_value;
++ return ENFS_RET_OK;
++ }
++ if (strcmp(field_name, "multipath_disable") == 0) {
++ g_enfs_config_info.multipath_state = int_value;
++ return ENFS_RET_OK;
++ }
++ return -EINVAL;
++}
++
++static int32_t enfs_check_and_assign_loadbalance_mode(char *field_name,
++ char *value,
++ int min_value,
++ int max_value)
++{
++ if (value == NULL)
++ return -EINVAL;
++
++ if (strcmp(field_name, "multipath_select_policy") == 0) {
++ if (strcmp(value, "roundrobin") == 0) {
++ g_enfs_config_info.loadbalance_mode
++ = ENFS_LOADBALANCE_RR;
++ return ENFS_RET_OK;
++ }
++ }
++ return -EINVAL;
++}
++
++static const struct check_and_assign_value g_check_and_assign_value[] = {
++ {"path_detect_interval", enfs_check_and_assign_int_value,
++ MIN_PATH_DETECT_INTERVAL, MAX_PATH_DETECT_INTERVAL},
++ {"path_detect_timeout", enfs_check_and_assign_int_value,
++ MIN_PATH_DETECT_TIMEOUT, MAX_PATH_DETECT_TIMEOUT},
++ {"multipath_timeout", enfs_check_and_assign_int_value,
++ MIN_MULTIPATH_TIMEOUT, MAX_MULTIPATH_TIMEOUT},
++ {"multipath_disable", enfs_check_and_assign_int_value,
++ MIN_MULTIPATH_STATE, MAX_MULTIPATH_STATE},
++ {"multipath_select_policy", enfs_check_and_assign_loadbalance_mode,
++ 0, 0},
++};
++
++static int32_t enfs_read_config_file(char *buffer, char *file_path)
++{
++ int ret;
++ struct file *filp = NULL;
++ loff_t f_pos = 0;
++ mm_segment_t fs;
++
++
++ filp = filp_open(file_path, O_RDONLY, 0);
++
++ if (IS_ERR(filp)) {
++ enfs_log_error("Failed to open file %s\n", CONFIG_FILE_PATH);
++ ret = -ENOENT;
++ return ret;
++ }
++
++ fs = get_fs();
++ set_fs(get_ds());
++ kernel_read(filp, buffer, MAX_FILE_SIZE, &f_pos);
++ set_fs(fs);
++
++ ret = filp_close(filp, NULL);
++ if (ret) {
++ enfs_log_error("Close File:%s failed:%d.\n",
++ CONFIG_FILE_PATH, ret);
++ return -EINVAL;
++ }
++ return ENFS_RET_OK;
++}
++
++static int32_t enfs_deal_with_comment_line(char *buffer)
++{
++ int ret;
++ char *pos = strchr(buffer, '\n');
++
++ if (pos != NULL)
++ ret = strlen(buffer) - strlen(pos);
++ else
++ ret = strlen(buffer);
++
++ return ret;
++}
++
++static int32_t enfs_parse_key_value_from_config(char *buffer, char *key,
++ char *value, int keyLen,
++ int valueLen)
++{
++ char *line;
++ char *tokenPtr;
++ int len;
++ char *tem;
++ char *pos = strchr(buffer, '\n');
++
++ if (pos != NULL)
++ len = strlen(buffer) - strlen(pos);
++ else
++ len = strlen(buffer);
++
++ line = kmalloc(len + 1, GFP_KERNEL);
++ if (!line) {
++ enfs_log_error("Failed to allocate memory.\n");
++ return -ENOMEM;
++ }
++ line[len] = '\0';
++ strncpy(line, buffer, len);
++
++ tem = line;
++ tokenPtr = strsep(&tem, "=");
++ if (tokenPtr == NULL || tem == NULL) {
++ kfree(line);
++ return len;
++ }
++ strncpy(key, strim(tokenPtr), keyLen);
++ strncpy(value, strim(tem), valueLen);
++
++ kfree(line);
++ return len;
++}
++
++static int32_t enfs_get_value_from_config_file(char *buffer, char *field_name,
++ char *value, int valueLen)
++{
++ int ret;
++ char key[STRING_BUF_SIZE + 1] = {0};
++ char val[STRING_BUF_SIZE + 1] = {0};
++
++ while (buffer[0] != '\0') {
++ if (buffer[0] == '\n') {
++ buffer++;
++ } else if (buffer[0] == '#') {
++ ret = enfs_deal_with_comment_line(buffer);
++ if (ret > 0)
++ buffer += ret;
++ } else {
++ ret = enfs_parse_key_value_from_config(buffer, key, val,
++ STRING_BUF_SIZE,
++ STRING_BUF_SIZE);
++ if (ret < 0) {
++ enfs_log_error("failed parse key value, %d\n"
++ , ret);
++ return ret;
++ }
++ key[STRING_BUF_SIZE] = '\0';
++ val[STRING_BUF_SIZE] = '\0';
++
++ buffer += ret;
++
++ if (strcmp(field_name, key) == 0) {
++ strncpy(value, val, valueLen);
++ return ENFS_RET_OK;
++ }
++ }
++ }
++ enfs_log_error("can not find value which matched field_name: %s.\n",
++ field_name);
++ return -EINVAL;
++}
++
++int32_t enfs_config_load(void)
++{
++ char value[STRING_BUF_SIZE + 1];
++ int ret;
++ int table_len;
++ int min;
++ int max;
++ int i;
++ char *buffer;
++
++ buffer = kmalloc(MAX_FILE_SIZE, GFP_KERNEL);
++ if (!buffer) {
++ enfs_log_error("Failed to allocate memory.\n");
++ return -ENOMEM;
++ }
++ memset(buffer, 0, MAX_FILE_SIZE);
++
++ g_enfs_config_info.path_detect_interval = DEFAULT_PATH_DETECT_INTERVAL;
++ g_enfs_config_info.path_detect_timeout = DEFAULT_PATH_DETECT_TIMEOUT;
++ g_enfs_config_info.multipath_timeout = DEFAULT_MULTIPATH_TIMEOUT;
++ g_enfs_config_info.multipath_state = DEFAULT_MULTIPATH_STATE;
++ g_enfs_config_info.loadbalance_mode = DEFAULT_LOADBALANCE_MODE;
++
++ table_len = sizeof(g_check_and_assign_value) /
++ sizeof(g_check_and_assign_value[0]);
++
++ ret = enfs_read_config_file(buffer, CONFIG_FILE_PATH);
++ if (ret != 0) {
++ kfree(buffer);
++ return ret;
++ }
++
++ for (i = 0; i < table_len; i++) {
++ ret = enfs_get_value_from_config_file(buffer,
++ g_check_and_assign_value[i].field_name,
++ value, STRING_BUF_SIZE);
++ if (ret < 0)
++ continue;
++
++ value[STRING_BUF_SIZE] = '\0';
++ min = g_check_and_assign_value[i].min_value;
++ max = g_check_and_assign_value[i].max_value;
++ if (g_check_and_assign_value[i].func != NULL)
++ (*g_check_and_assign_value[i].func)(
++ g_check_and_assign_value[i].field_name,
++ value, min, max);
++ }
++
++ kfree(buffer);
++ return ENFS_RET_OK;
++}
++
++int32_t enfs_get_config_path_detect_interval(void)
++{
++ return g_enfs_config_info.path_detect_interval;
++}
++
++int32_t enfs_get_config_path_detect_timeout(void)
++{
++ return g_enfs_config_info.path_detect_timeout;
++}
++
++int32_t enfs_get_config_multipath_timeout(void)
++{
++ return g_enfs_config_info.multipath_timeout;
++}
++
++int32_t enfs_get_config_multipath_state(void)
++{
++ return g_enfs_config_info.multipath_state;
++}
++
++int32_t enfs_get_config_loadbalance_mode(void)
++{
++ return g_enfs_config_info.loadbalance_mode;
++}
++
++static bool enfs_file_changed(const char *filename)
++{
++ int err;
++ struct kstat file_stat;
++
++ err = vfs_stat(filename, &file_stat);
++ if (err) {
++ pr_err("failed to open file:%s err:%d\n", filename, err);
++ return false;
++ }
++
++ if (timespec64_compare(&modify_time, &file_stat.mtime) == -1) {
++ modify_time = file_stat.mtime;
++ pr_info("file change: %lld %lld\n", modify_time.tv_sec,
++ file_stat.mtime.tv_sec);
++ return true;
++ }
++
++ return false;
++}
++
++static int enfs_thread_func(void *data)
++{
++ while (!kthread_should_stop()) {
++ if (enfs_file_changed(CONFIG_FILE_PATH))
++ enfs_config_load();
++
++ msleep(ENFS_NOTIFY_FILE_PERIOD);
++ }
++ return 0;
++}
++
++int enfs_config_timer_init(void)
++{
++ thread = kthread_run(enfs_thread_func, NULL, "enfs_notiy_file_thread");
++ if (IS_ERR(thread)) {
++ pr_err("Failed to create kernel thread\n");
++ return PTR_ERR(thread);
++ }
++ return 0;
++}
++
++void enfs_config_timer_exit(void)
++{
++ pr_info("enfs_notify_file_exit\n");
++ if (thread)
++ kthread_stop(thread);
++}
+diff --git a/fs/nfs/enfs/enfs_config.h b/fs/nfs/enfs/enfs_config.h
+new file mode 100644
+index 000000000000..752710129170
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_config.h
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs configuration
++ * Author: y00583252
++ * Create: 2023-07-27
++ */
++
++#ifndef ENFS_CONFIG_H
++#define ENFS_CONFIG_H
++
++#include <linux/types.h>
++
++enum enfs_multipath_state {
++ ENFS_MULTIPATH_ENABLE = 0,
++ ENFS_MULTIPATH_DISABLE = 1,
++};
++
++enum enfs_loadbalance_mode {
++ ENFS_LOADBALANCE_RR,
++};
++
++
++int32_t enfs_get_config_path_detect_interval(void);
++int32_t enfs_get_config_path_detect_timeout(void);
++int32_t enfs_get_config_multipath_timeout(void);
++int32_t enfs_get_config_multipath_state(void);
++int32_t enfs_get_config_loadbalance_mode(void);
++int32_t enfs_config_load(void);
++int32_t enfs_config_timer_init(void);
++void enfs_config_timer_exit(void);
++#endif // ENFS_CONFIG_H
+diff --git a/fs/nfs/enfs/enfs_errcode.h b/fs/nfs/enfs/enfs_errcode.h
+new file mode 100644
+index 000000000000..cca47ab9a191
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_errcode.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs errocode
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#ifndef ENFS_ERRCODE_H
++#define ENFS_ERRCODE_H
++
++enum {
++ ENFS_RET_OK = 0,
++ ENFS_RET_FAIL
++};
++
++#endif // ENFS_ERRCODE_H
+diff --git a/fs/nfs/enfs/enfs_log.h b/fs/nfs/enfs/enfs_log.h
+new file mode 100644
+index 000000000000..177b404f05df
+--- /dev/null
++++ b/fs/nfs/enfs/enfs_log.h
+@@ -0,0 +1,25 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: enfs log
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++#ifndef ENFS_LOG_H
++#define ENFS_LOG_H
++
++#include <linux/printk.h>
++
++#define enfs_log_info(fmt, ...) \
++ pr_info("enfs:[%s]" pr_fmt(fmt), \
++ __func__, ##__VA_ARGS__)
++
++#define enfs_log_error(fmt, ...) \
++ pr_err("enfs:[%s]" pr_fmt(fmt), \
++ __func__, ##__VA_ARGS__)
++
++#define enfs_log_debug(fmt, ...) \
++ pr_debug("enfs:[%s]" pr_fmt(fmt), \
++ __func__, ##__VA_ARGS__)
++
++#endif // ENFS_ERRCODE_H
+diff --git a/fs/nfs/enfs/failover_com.h b/fs/nfs/enfs/failover_com.h
+new file mode 100644
+index 000000000000..c52940da232e
+--- /dev/null
++++ b/fs/nfs/enfs/failover_com.h
+@@ -0,0 +1,23 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: failover time commont header file
++ * Create: 2023-08-02
++ */
++#ifndef FAILOVER_COMMON_H
++#define FAILOVER_COMMON_H
++
++static inline bool failover_is_enfs_clnt(struct rpc_clnt *clnt)
++{
++ struct rpc_clnt *next = clnt->cl_parent;
++
++ while (next) {
++ if (next == next->cl_parent)
++ break;
++ next = next->cl_parent;
++ }
++
++ return next != NULL ? next->cl_enfs : clnt->cl_enfs;
++}
++
++#endif // FAILOVER_COMMON_H
+diff --git a/fs/nfs/enfs/failover_path.c b/fs/nfs/enfs/failover_path.c
+new file mode 100644
+index 000000000000..93b454de29d1
+--- /dev/null
++++ b/fs/nfs/enfs/failover_path.c
+@@ -0,0 +1,207 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs path failover file
++ * Author: y00583252
++ * Create: 2023-08-02
++ */
++
++#include "failover_path.h"
++#include <linux/nfs.h>
++#include <linux/nfs3.h>
++#include <linux/nfs4.h>
++#include <linux/sunrpc/clnt.h>
++#include <linux/sunrpc/sched.h>
++#include <linux/sunrpc/xprt.h>
++#include "enfs_config.h"
++#include "enfs_log.h"
++#include "failover_com.h"
++#include "pm_state.h"
++#include "pm_ping.h"
++
++enum failover_policy_t {
++ FAILOVER_NOACTION = 1,
++ FAILOVER_RETRY,
++ FAILOVER_RETRY_DELAY,
++};
++
++static void failover_retry_path(struct rpc_task *task)
++{
++ xprt_release(task);
++ rpc_init_task_retry_counters(task);
++ rpc_task_release_transport(task);
++ rpc_restart_call(task);
++}
++
++static void failover_retry_path_delay(struct rpc_task *task, int32_t delay)
++{
++ failover_retry_path(task);
++ rpc_delay(task, delay);
++}
++
++static void failover_retry_path_by_policy(struct rpc_task *task,
++ enum failover_policy_t policy)
++{
++ if (policy == FAILOVER_RETRY)
++ failover_retry_path(task);
++ else if (policy == FAILOVER_RETRY_DELAY)
++ failover_retry_path_delay(task, 3 * HZ); // delay 3s
++}
++
++static
++enum failover_policy_t failover_get_nfs3_retry_policy(struct rpc_task *task)
++{
++ enum failover_policy_t policy = FAILOVER_NOACTION;
++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
++ u32 proc;
++
++ if (unlikely(procinfo == NULL)) {
++ enfs_log_error("the task contains no valid proc.\n");
++ return FAILOVER_NOACTION;
++ }
++
++ proc = procinfo->p_proc;
++
++ switch (proc) {
++ case NFS3PROC_CREATE:
++ case NFS3PROC_MKDIR:
++ case NFS3PROC_REMOVE:
++ case NFS3PROC_RMDIR:
++ case NFS3PROC_SYMLINK:
++ case NFS3PROC_LINK:
++ case NFS3PROC_SETATTR:
++ case NFS3PROC_WRITE:
++ policy = FAILOVER_RETRY_DELAY;
++ default:
++ policy = FAILOVER_RETRY;
++ }
++ return policy;
++}
++
++static
++enum failover_policy_t failover_get_nfs4_retry_policy(struct rpc_task *task)
++{
++ enum failover_policy_t policy = FAILOVER_NOACTION;
++ const struct rpc_procinfo *procinfo = task->tk_msg.rpc_proc;
++ u32 proc_idx;
++
++ if (unlikely(procinfo == NULL)) {
++ enfs_log_error("the task contains no valid proc.\n");
++ return FAILOVER_NOACTION;
++ }
++
++ proc_idx = procinfo->p_statidx;
++
++ switch (proc_idx) {
++ case NFSPROC4_CLNT_CREATE:
++ case NFSPROC4_CLNT_REMOVE:
++ case NFSPROC4_CLNT_LINK:
++ case NFSPROC4_CLNT_SYMLINK:
++ case NFSPROC4_CLNT_SETATTR:
++ case NFSPROC4_CLNT_WRITE:
++ case NFSPROC4_CLNT_RENAME:
++ case NFSPROC4_CLNT_SETACL:
++ policy = FAILOVER_RETRY_DELAY;
++ default:
++ policy = FAILOVER_RETRY;
++ }
++ return policy;
++}
++
++static enum failover_policy_t failover_get_retry_policy(struct rpc_task *task)
++{
++ struct rpc_clnt *clnt = task->tk_client;
++ u32 version = clnt->cl_vers;
++ enum failover_policy_t policy = FAILOVER_NOACTION;
++
++ // 1. if the task meant to send to certain xprt, take no action
++ if (task->tk_flags & RPC_TASK_FIXED)
++ return FAILOVER_NOACTION;
++
++ // 2. get policy by different version of nfs protocal
++ if (version == 3) // nfs v3
++ policy = failover_get_nfs3_retry_policy(task);
++ else if (version == 4) // nfs v4
++ policy = failover_get_nfs4_retry_policy(task);
++ else
++ return FAILOVER_NOACTION;
++
++ // 3. if the task is not send to target, retry immediately
++ if (!RPC_WAS_SENT(task))
++ policy = FAILOVER_RETRY;
++
++ return policy;
++}
++
++static int failover_check_task(struct rpc_task *task)
++{
++ struct rpc_clnt *clnt = NULL;
++ int disable_mpath = enfs_get_config_multipath_state();
++
++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return -EINVAL;
++ }
++
++ if (unlikely((task == NULL) || (task->tk_client == NULL))) {
++ enfs_log_error("The task is not valid.\n");
++ return -EINVAL;
++ }
++
++ clnt = task->tk_client;
++
++ if (clnt->cl_prog != NFS_PROGRAM) {
++ enfs_log_debug("The clnt is not prog{%u} type.\n",
++ clnt->cl_prog);
++ return -EINVAL;
++ }
++
++ if (!failover_is_enfs_clnt(clnt)) {
++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
++void failover_handle(struct rpc_task *task)
++{
++ enum failover_policy_t policy;
++ int ret;
++
++ ret = failover_check_task(task);
++ if (ret != 0)
++ return;
++
++ pm_set_path_state(task->tk_xprt, PM_STATE_FAULT);
++
++ policy = failover_get_retry_policy(task);
++
++ failover_retry_path_by_policy(task, policy);
++}
++
++bool failover_task_need_call_start_again(struct rpc_task *task)
++{
++ int ret;
++
++ ret = failover_check_task(task);
++ if (ret != 0)
++ return false;
++
++ return true;
++}
++
++bool failover_prepare_transmit(struct rpc_task *task)
++{
++ if (task->tk_flags & RPC_TASK_FIXED)
++ return true;
++
++ if (pm_ping_is_test_xprt_task(task))
++ return true;
++
++ if (pm_get_path_state(task->tk_xprt) == PM_STATE_FAULT) {
++ task->tk_status = -ETIMEDOUT;
++ return false;
++ }
++
++ return true;
++}
+diff --git a/fs/nfs/enfs/failover_path.h b/fs/nfs/enfs/failover_path.h
+new file mode 100644
+index 000000000000..6f1294829a6e
+--- /dev/null
++++ b/fs/nfs/enfs/failover_path.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs path failover header file
++ * Author: y00583252
++ * Create: 2023-08-02
++ */
++
++#ifndef FAILOVER_PATH_H
++#define FAILOVER_PATH_H
++
++#include <linux/sunrpc/sched.h>
++
++void failover_handle(struct rpc_task *task);
++bool failover_prepare_transmit(struct rpc_task *task);
++
++#endif // FAILOVER_PATH_H
+diff --git a/fs/nfs/enfs/failover_time.c b/fs/nfs/enfs/failover_time.c
+new file mode 100644
+index 000000000000..866ea82d13fc
+--- /dev/null
++++ b/fs/nfs/enfs/failover_time.c
+@@ -0,0 +1,99 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: failover time file
++ * Create: 2023-08-02
++ */
++
++#include "failover_time.h"
++#include <linux/jiffies.h>
++#include <linux/sunrpc/clnt.h>
++#include "enfs_config.h"
++#include "enfs_log.h"
++#include "failover_com.h"
++#include "pm_ping.h"
++
++static unsigned long failover_get_mulitipath_timeout(struct rpc_clnt *clnt)
++{
++ unsigned long config_tmo = enfs_get_config_multipath_timeout() * HZ;
++ unsigned long clnt_tmo = clnt->cl_timeout->to_initval;
++
++ if (config_tmo == 0)
++ return clnt_tmo;
++
++ return config_tmo > clnt_tmo ? clnt_tmo : config_tmo;
++}
++
++void failover_adjust_task_timeout(struct rpc_task *task, void *condition)
++{
++ struct rpc_clnt *clnt = NULL;
++ unsigned long tmo;
++ int disable_mpath = enfs_get_config_multipath_state();
++
++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return;
++ }
++
++ clnt = task->tk_client;
++ if (unlikely(clnt == NULL)) {
++ enfs_log_error("task associate client is NULL.\n");
++ return;
++ }
++
++ if (!failover_is_enfs_clnt(clnt)) {
++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
++ return;
++ }
++
++ tmo = failover_get_mulitipath_timeout(clnt);
++ if (tmo == 0) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return;
++ }
++
++ if (task->tk_timeout != 0)
++ task->tk_timeout =
++ task->tk_timeout < tmo ? task->tk_timeout : tmo;
++ else
++ task->tk_timeout = tmo;
++}
++
++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req)
++{
++ struct rpc_clnt *clnt = NULL;
++ int disable_mpath = enfs_get_config_multipath_state();
++
++ if (disable_mpath != ENFS_MULTIPATH_ENABLE) {
++ enfs_log_debug("Multipath is not enabled.\n");
++ return;
++ }
++
++ clnt = task->tk_client;
++ if (unlikely(clnt == NULL)) {
++ enfs_log_error("task associate client is NULL.\n");
++ return;
++ }
++
++ if (!failover_is_enfs_clnt(clnt)) {
++ enfs_log_debug("The clnt is not a enfs-managed type.\n");
++ return;
++ }
++
++ if (!pm_ping_is_test_xprt_task(task))
++ req->rq_timeout = failover_get_mulitipath_timeout(clnt);
++ else {
++ req->rq_timeout = enfs_get_config_path_detect_timeout() * HZ;
++ req->rq_majortimeo = req->rq_timeout + jiffies;
++ }
++
++ /*
++ * when task is retried, the req is new, we lost major-timeout times,
++ * so we have to restore req major
++ * timeouts from the task, if it is stored.
++ */
++ if (task->tk_major_timeo != 0)
++ req->rq_majortimeo = task->tk_major_timeo;
++ else
++ task->tk_major_timeo = req->rq_majortimeo;
++}
+diff --git a/fs/nfs/enfs/failover_time.h b/fs/nfs/enfs/failover_time.h
+new file mode 100644
+index 000000000000..ede25b577a2a
+--- /dev/null
++++ b/fs/nfs/enfs/failover_time.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: failover time header file
++ * Create: 2023-08-02
++ */
++
++#ifndef FAILOVER_TIME_H
++#define FAILOVER_TIME_H
++
++#include <linux/sunrpc/sched.h>
++
++void failover_adjust_task_timeout(struct rpc_task *task, void *condition);
++void failover_init_task_req(struct rpc_task *task, struct rpc_rqst *req);
++
++#endif // FAILOVER_TIME_H
+diff --git a/fs/nfs/enfs/init.h b/fs/nfs/enfs/init.h
+new file mode 100644
+index 000000000000..fdabb9084e19
+--- /dev/null
++++ b/fs/nfs/enfs/init.h
+@@ -0,0 +1,17 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs client init
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#ifndef ENFS_INIT_H
++#define ENFS_INIT_H
++
++#include <linux/types.h>
++
++int32_t enfs_init(void);
++void enfs_fini(void);
++
++#endif
+diff --git a/fs/nfs/enfs/mgmt_init.c b/fs/nfs/enfs/mgmt_init.c
+new file mode 100644
+index 000000000000..75a40c5e0f6c
+--- /dev/null
++++ b/fs/nfs/enfs/mgmt_init.c
+@@ -0,0 +1,22 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: mgmt component init
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#include "mgmt_init.h"
++#include <linux/printk.h>
++#include "enfs_errcode.h"
++#include "enfs_config.h"
++
++int32_t mgmt_init(void)
++{
++ return enfs_config_timer_init();
++}
++
++void mgmt_fini(void)
++{
++ enfs_config_timer_exit();
++}
+diff --git a/fs/nfs/enfs/mgmt_init.h b/fs/nfs/enfs/mgmt_init.h
+new file mode 100644
+index 000000000000..aa78303b9f01
+--- /dev/null
++++ b/fs/nfs/enfs/mgmt_init.h
+@@ -0,0 +1,18 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: mgmt component init
++ * Author: y00583252
++ * Create: 2023-07-31
++ */
++
++#ifndef MGMT_INIT_H
++#define MGMT_INIT_H
++
++#include <linux/types.h>
++
++int32_t mgmt_init(void);
++void mgmt_fini(void);
++
++
++#endif // MGMT_INIT_H
+diff --git a/fs/nfs/enfs/pm_ping.c b/fs/nfs/enfs/pm_ping.c
+new file mode 100644
+index 000000000000..24153cd4c7f3
+--- /dev/null
++++ b/fs/nfs/enfs/pm_ping.c
+@@ -0,0 +1,421 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: path state header file
++ * Author: x00833432
++ * Create: 2023-08-21
++ */
++
++#include "pm_ping.h"
++#include <linux/err.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/printk.h>
++#include <linux/kthread.h>
++#include <linux/nfs.h>
++#include <linux/errno.h>
++#include <linux/rcupdate.h>
++#include <linux/workqueue.h>
++#include <net/netns/generic.h>
++#include <linux/atomic.h>
++#include <linux/sunrpc/clnt.h>
++
++#include "../../../net/sunrpc/netns.h"
++#include "pm_state.h"
++#include "enfs.h"
++#include "enfs_log.h"
++#include "enfs_config.h"
++
++#define SLEEP_INTERVAL 2
++extern unsigned int sunrpc_net_id;
++
++static struct task_struct *pm_ping_timer_thread;
++//protect pint_execute_workq
++static spinlock_t ping_execute_workq_lock;
++// timer for test xprt workqueue
++static struct workqueue_struct *ping_execute_workq;
++// count the ping xprt work on flight
++static atomic_t check_xprt_count;
++
++struct ping_xprt_work {
++ struct rpc_xprt *xprt; // use this specific xprt
++ struct rpc_clnt *clnt; // use this specific rpc_client
++ struct work_struct ping_work;
++};
++
++struct pm_ping_async_callback {
++ void *data;
++ void (*func)(void *data);
++};
++
++// set xprt's enum pm_check_state
++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
++ enum pm_check_state state)
++{
++ struct enfs_xprt_context *ctx = NULL;
++
++ if (IS_ERR(xprt)) {
++ enfs_log_error("The xprt ptr is not exist.\n");
++ return;
++ }
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ xprt_get(xprt);
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ xprt_put(xprt);
++ return;
++ }
++
++ atomic_set(&ctx->path_check_state, state);
++ xprt_put(xprt);
++}
++
++// get xprt's enum pm_check_state
++static enum pm_check_state pm_ping_get_path_check_state(struct rpc_xprt *xprt)
++{
++ struct enfs_xprt_context *ctx = NULL;
++ enum pm_check_state state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return PM_CHECK_UNDEFINE;
++ }
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ return PM_CHECK_UNDEFINE;
++ }
++
++ state = atomic_read(&ctx->path_check_state);
++
++ return state;
++}
++
++static void pm_ping_call_done_callback(void *data)
++{
++ struct pm_ping_async_callback *callback_data =
++ (struct pm_ping_async_callback *)data;
++
++ if (callback_data == NULL)
++ return;
++
++ callback_data->func(callback_data->data);
++
++ kfree(callback_data);
++}
++
++// Default callback for async RPC calls
++static void pm_ping_call_done(struct rpc_task *task, void *data)
++{
++ struct rpc_xprt *xprt = task->tk_xprt;
++
++ atomic_dec(&check_xprt_count);
++ if (task->tk_status >= 0)
++ pm_set_path_state(xprt, PM_STATE_NORMAL);
++ else
++ pm_set_path_state(xprt, PM_STATE_FAULT);
++
++ pm_ping_set_path_check_state(xprt, PM_CHECK_FINISH);
++
++ pm_ping_call_done_callback(data);
++}
++
++// register func to rpc_call_done
++static const struct rpc_call_ops pm_ping_set_status_ops = {
++ .rpc_call_done = pm_ping_call_done,
++};
++
++// execute work which in work_queue
++static void pm_ping_execute_work(struct work_struct *work)
++{
++ int ret = 0;
++
++ // get the work information
++ struct ping_xprt_work *work_info =
++ container_of(work, struct ping_xprt_work, ping_work);
++
++ // if check state is pending
++ if (pm_ping_get_path_check_state(work_info->xprt) == PM_CHECK_WAITING) {
++
++ pm_ping_set_path_check_state(work_info->xprt,
++ PM_CHECK_CHECKING);
++
++ ret = rpc_clnt_test_xprt(work_info->clnt,
++ work_info->xprt,
++ &pm_ping_set_status_ops,
++ NULL,
++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
++
++ if (ret < 0) {
++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
++
++ pm_ping_set_path_check_state(work_info->xprt,
++ PM_CHECK_FINISH);
++
++ } else
++ atomic_inc(&check_xprt_count);
++
++ }
++
++ atomic_dec(&work_info->clnt->cl_count);
++ xprt_put(work_info->xprt);
++ kfree(work_info);
++ work_info = NULL;
++}
++
++static bool pm_ping_workqueue_queue_work(struct work_struct *work)
++{
++ bool ret = false;
++
++ spin_lock(&ping_execute_workq_lock);
++
++ if (ping_execute_workq != NULL)
++ ret = queue_work(ping_execute_workq, work);
++
++ spin_unlock(&ping_execute_workq_lock);
++ return ret;
++}
++
++// init test work and add this work to workqueue
++static int pm_ping_add_work(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt, void *data)
++{
++ struct ping_xprt_work *work_info;
++ bool ret = false;
++
++ if (IS_ERR(xprt) || xprt == NULL) {
++ enfs_log_error("The xprt ptr is not exist.\n");
++ return -EINVAL;
++ }
++
++ if (IS_ERR(clnt) || clnt == NULL) {
++ enfs_log_error("The clnt ptr is not exist.\n");
++ return -EINVAL;
++ }
++
++ if (!xprt->multipath_context) {
++ enfs_log_error("multipath_context is null.\n");
++ return -EINVAL;
++ }
++
++ // check xprt pending status, if pending status equals Finish
++ // means this xprt can inster to work queue
++ if (pm_ping_get_path_check_state(xprt) ==
++ PM_CHECK_FINISH ||
++ pm_ping_get_path_check_state(xprt) ==
++ PM_CHECK_INIT) {
++
++ enfs_log_debug("find xprt pointer. %p\n", xprt);
++ work_info = kzalloc(sizeof(struct ping_xprt_work), GFP_ATOMIC);
++ if (work_info == NULL)
++ return -ENOMEM;
++ work_info->clnt = clnt;
++ atomic_inc(&clnt->cl_count);
++ work_info->xprt = xprt;
++ xprt_get(xprt);
++ INIT_WORK(&work_info->ping_work, pm_ping_execute_work);
++ pm_ping_set_path_check_state(xprt, PM_CHECK_WAITING);
++
++ ret = pm_ping_workqueue_queue_work(&work_info->ping_work);
++ if (!ret) {
++ atomic_dec(&work_info->clnt->cl_count);
++ xprt_put(work_info->xprt);
++ kfree(work_info);
++ return -EINVAL;
++ }
++ }
++ return 0;
++}
++
++// encapsulate pm_ping_add_work()
++static int pm_ping_execute_xprt_test(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt, void *data)
++{
++ pm_ping_add_work(clnt, xprt, NULL);
++ // return 0 for rpc_clnt_iterate_for_each_xprt();
++ // because negative value will stop iterate all xprt
++ // and we need return negative value for debug
++ // Therefore, we need this function to iterate all xprt
++ return 0;
++}
++
++// export to other module add ping work to workqueue
++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
++{
++ int ret;
++
++ ret = pm_ping_add_work(clnt, xprt, NULL);
++ return ret;
++}
++
++// iterate xprt in the client
++static void pm_ping_loop_rpclnt(struct sunrpc_net *sn)
++{
++ struct rpc_clnt *clnt;
++
++ spin_lock(&sn->rpc_client_lock);
++ list_for_each_entry_rcu(clnt, &sn->all_clients, cl_clients) {
++ if (clnt->cl_enfs) {
++ enfs_log_debug("find rpc_clnt. %p\n", clnt);
++ rpc_clnt_iterate_for_each_xprt(clnt,
++ pm_ping_execute_xprt_test, NULL);
++ }
++ }
++ spin_unlock(&sn->rpc_client_lock);
++}
++
++// iterate each clnt in the sunrpc_net
++static void pm_ping_loop_sunrpc_net(void)
++{
++ struct net *net;
++ struct sunrpc_net *sn;
++
++ rcu_read_lock();
++ for_each_net_rcu(net) {
++ sn = net_generic(net, sunrpc_net_id);
++ if (sn == NULL)
++ continue;
++ pm_ping_loop_rpclnt(sn);
++ }
++ rcu_read_unlock();
++}
++
++static int pm_ping_routine(void *data)
++{
++ while (!kthread_should_stop()) {
++ // equale 0 means open multipath
++ if (enfs_get_config_multipath_state() ==
++ ENFS_MULTIPATH_ENABLE)
++ pm_ping_loop_sunrpc_net();
++
++ msleep((unsigned int)
++ enfs_get_config_path_detect_interval() * 1000);
++ }
++ return 0;
++}
++
++// start thread to cycly ping
++static int pm_ping_start(void)
++{
++ pm_ping_timer_thread =
++ kthread_run(pm_ping_routine, NULL, "pm_ping_routine");
++ if (IS_ERR(pm_ping_timer_thread)) {
++ enfs_log_error("Failed to create kernel thread\n");
++ return PTR_ERR(pm_ping_timer_thread);
++ }
++ return 0;
++}
++
++// initialize workqueue
++static int pm_ping_workqueue_init(void)
++{
++ struct workqueue_struct *queue = NULL;
++
++ queue = create_workqueue("pm_ping_workqueue");
++
++ if (queue == NULL) {
++ enfs_log_error("create workqueue failed.\n");
++ return -ENOMEM;
++ }
++
++ spin_lock(&ping_execute_workq_lock);
++ ping_execute_workq = queue;
++ spin_unlock(&ping_execute_workq_lock);
++ enfs_log_info("create workqueue succeeeded.\n");
++ return 0;
++}
++
++static void pm_ping_workqueue_fini(void)
++{
++ struct workqueue_struct *queue = NULL;
++
++ spin_lock(&ping_execute_workq_lock);
++ queue = ping_execute_workq;
++ ping_execute_workq = NULL;
++ spin_unlock(&ping_execute_workq_lock);
++
++ enfs_log_info("delete work queue\n");
++
++ if (queue != NULL) {
++ flush_workqueue(queue);
++ destroy_workqueue(queue);
++ }
++}
++
++// module exit func
++void pm_ping_fini(void)
++{
++ if (pm_ping_timer_thread)
++ kthread_stop(pm_ping_timer_thread);
++
++ pm_ping_workqueue_fini();
++
++ while (atomic_read(&check_xprt_count) != 0)
++ msleep(SLEEP_INTERVAL);
++}
++
++// module init func
++int pm_ping_init(void)
++{
++ int ret;
++
++ atomic_set(&check_xprt_count, 0);
++ ret = pm_ping_workqueue_init();
++ if (ret != 0) {
++ enfs_log_error("PM_PING Module loading failed.\n");
++ return ret;
++ }
++ ret = pm_ping_start();
++ if (ret != 0) {
++ enfs_log_error("PM_PING Module loading failed.\n");
++ pm_ping_workqueue_fini();
++ return ret;
++ }
++
++ return ret;
++}
++
++bool pm_ping_is_test_xprt_task(struct rpc_task *task)
++{
++ return task->tk_ops == &pm_ping_set_status_ops ? true : false;
++}
++
++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt,
++ void (*func)(void *data),
++ void *data)
++{
++ int ret;
++
++ struct pm_ping_async_callback *callback_data =
++ kzalloc(sizeof(struct pm_ping_async_callback), GFP_KERNEL);
++
++ if (callback_data == NULL) {
++ enfs_log_error("failed to mzalloc mem\n");
++ return -ENOMEM;
++ }
++
++ callback_data->data = data;
++ callback_data->func = func;
++ atomic_inc(&check_xprt_count);
++ ret = rpc_clnt_test_xprt(clnt, xprt,
++ &pm_ping_set_status_ops,
++ callback_data,
++ RPC_TASK_ASYNC | RPC_TASK_FIXED);
++
++ if (ret < 0) {
++ enfs_log_debug("ping xprt execute failed ,ret %d", ret);
++ atomic_dec(&check_xprt_count);
++ }
++
++ return ret;
++}
+diff --git a/fs/nfs/enfs/pm_ping.h b/fs/nfs/enfs/pm_ping.h
+new file mode 100644
+index 000000000000..6bcb94bfc836
+--- /dev/null
++++ b/fs/nfs/enfs/pm_ping.h
+@@ -0,0 +1,33 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: nfs configuration
++ * Author: x00833432
++ * Create: 2023-07-27
++ */
++
++#ifndef PM_PING_H
++#define PM_PING_H
++
++#include <linux/sunrpc/clnt.h>
++
++enum pm_check_state {
++ PM_CHECK_INIT, // this xprt never been queued
++ PM_CHECK_WAITING, // this xprt waiting in the queue
++ PM_CHECK_CHECKING, // this xprt is testing
++ PM_CHECK_FINISH, // this xprt has been finished
++ PM_CHECK_UNDEFINE, // undefine multipath struct
++};
++
++int pm_ping_init(void);
++void pm_ping_fini(void);
++int pm_ping_rpc_test_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt);
++void pm_ping_set_path_check_state(struct rpc_xprt *xprt,
++ enum pm_check_state state);
++bool pm_ping_is_test_xprt_task(struct rpc_task *task);
++int pm_ping_rpc_test_xprt_with_callback(struct rpc_clnt *clnt,
++ struct rpc_xprt *xprt,
++ void (*func)(void *data),
++ void *data);
++
++#endif // PM_PING_H
+diff --git a/fs/nfs/enfs/pm_state.c b/fs/nfs/enfs/pm_state.c
+new file mode 100644
+index 000000000000..220621a207a2
+--- /dev/null
++++ b/fs/nfs/enfs/pm_state.c
+@@ -0,0 +1,158 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: path state file
++ * Author: y00583252
++ * Create: 2023-08-12
++ */
++#include "pm_state.h"
++#include <linux/sunrpc/xprt.h>
++
++#include "enfs.h"
++#include "enfs_log.h"
++
++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt)
++{
++ struct enfs_xprt_context *ctx = NULL;
++ enum pm_path_state state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return PM_STATE_UNDEFINED;
++ }
++
++ xprt_get(xprt);
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ xprt_put(xprt);
++ return PM_STATE_UNDEFINED;
++ }
++
++ state = atomic_read(&ctx->path_state);
++
++ xprt_put(xprt);
++
++ return state;
++}
++
++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state)
++{
++ struct enfs_xprt_context *ctx = NULL;
++ enum pm_path_state cur_state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ xprt_get(xprt);
++
++ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
++ if (ctx == NULL) {
++ enfs_log_error("The xprt multipath ctx is not valid.\n");
++ xprt_put(xprt);
++ return;
++ }
++
++ cur_state = atomic_read(&ctx->path_state);
++ if (cur_state == state) {
++ enfs_log_debug("The xprt is already {%d}.\n", state);
++ xprt_put(xprt);
++ return;
++ }
++
++ atomic_set(&ctx->path_state, state);
++ enfs_log_info("The xprt {%p} path state change from {%d} to {%d}.\n",
++ xprt, cur_state, state);
++
++ xprt_put(xprt);
++}
++
++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len)
++{
++ enum pm_path_state state;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ if ((buf == NULL) || (len <= 0)) {
++ enfs_log_error("Buffer is not valid, len=%d.\n", len);
++ return;
++ }
++
++ state = pm_get_path_state(xprt);
++
++ switch (state) {
++ case PM_STATE_INIT:
++ (void)snprintf(buf, len, "Init");
++ break;
++ case PM_STATE_NORMAL:
++ (void)snprintf(buf, len, "Normal");
++ break;
++ case PM_STATE_FAULT:
++ (void)snprintf(buf, len, "Fault");
++ break;
++ default:
++ (void)snprintf(buf, len, "Unknown");
++ break;
++ }
++}
++
++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len)
++{
++ int i;
++ unsigned long state;
++ static unsigned long xprt_mask[] = {
++ XPRT_LOCKED, XPRT_CONNECTED,
++ XPRT_CONNECTING, XPRT_CLOSE_WAIT,
++ XPRT_BOUND, XPRT_BINDING, XPRT_CLOSING,
++ XPRT_CONGESTED};
++
++ static const char *const xprt_state_desc[] = {
++ "LOCKED", "CONNECTED", "CONNECTING",
++ "CLOSE_WAIT", "BOUND", "BINDING",
++ "CLOSING", "CONGESTED"};
++ int pos = 0;
++ int ret = 0;
++
++ if (xprt == NULL) {
++ enfs_log_error("The xprt is not valid.\n");
++ return;
++ }
++
++ if ((buf == NULL) || (len <= 0)) {
++ enfs_log_error(
++ "Xprt state buffer is not valid, len=%d.\n",
++ len);
++ return;
++ }
++
++ xprt_get(xprt);
++ state = READ_ONCE(xprt->state);
++ xprt_put(xprt);
++
++ for (i = 0; i < ARRAY_SIZE(xprt_mask); ++i) {
++ if (pos >= len)
++ break;
++
++ if (!test_bit(xprt_mask[i], &state))
++ continue;
++
++ if (pos == 0)
++ ret = snprintf(buf, len, "%s", xprt_state_desc[i]);
++ else
++ ret = snprintf(buf + pos, len - pos, "|%s",
++ xprt_state_desc[i]);
++
++ if (ret < 0) {
++ enfs_log_error("format state failed, ret %d.\n", ret);
++ break;
++ }
++
++ pos += ret;
++ }
++}
+diff --git a/fs/nfs/enfs/pm_state.h b/fs/nfs/enfs/pm_state.h
+new file mode 100644
+index 000000000000..f5f52e5ab91d
+--- /dev/null
++++ b/fs/nfs/enfs/pm_state.h
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
++ * Description: path state header file
++ * Author: y00583252
++ * Create: 2023-08-12
++ */
++
++#ifndef PM_STATE_H
++#define PM_STATE_H
++
++#include <linux/types.h>
++#include <linux/sunrpc/xprt.h>
++
++enum pm_path_state {
++ PM_STATE_INIT,
++ PM_STATE_NORMAL,
++ PM_STATE_FAULT,
++ PM_STATE_UNDEFINED // xprt is not multipath xprt
++};
++
++void pm_set_path_state(struct rpc_xprt *xprt, enum pm_path_state state);
++enum pm_path_state pm_get_path_state(struct rpc_xprt *xprt);
++
++void pm_get_path_state_desc(struct rpc_xprt *xprt, char *buf, int len);
++void pm_get_xprt_state_desc(struct rpc_xprt *xprt, char *buf, int len);
++
++#endif // PM_STATE_H
diff --git a/0006-add_enfs_compile_option.patch b/0006-add_enfs_compile_option.patch
new file mode 100644
index 0000000..ff3bc0e
--- /dev/null
+++ b/0006-add_enfs_compile_option.patch
@@ -0,0 +1,70 @@
+diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig
+index b04256636d4b..ae53510c0627 100644
+--- a/arch/arm64/configs/openeuler_defconfig
++++ b/arch/arm64/configs/openeuler_defconfig
+@@ -5344,6 +5344,7 @@ CONFIG_LOCKD=m
+ CONFIG_LOCKD_V4=y
+ CONFIG_NFS_ACL_SUPPORT=m
+ CONFIG_NFS_COMMON=y
++# CONFIG_ENFS is not set
+ CONFIG_SUNRPC=m
+ CONFIG_SUNRPC_GSS=m
+ CONFIG_SUNRPC_BACKCHANNEL=y
+diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig
+index 59baeb2973af..ccc317f7fdb2 100644
+--- a/arch/x86/configs/openeuler_defconfig
++++ b/arch/x86/configs/openeuler_defconfig
+@@ -6825,6 +6825,7 @@ CONFIG_LOCKD=m
+ CONFIG_LOCKD_V4=y
+ CONFIG_NFS_ACL_SUPPORT=m
+ CONFIG_NFS_COMMON=y
++CONFIG_ENFS=y
+ CONFIG_SUNRPC=m
+ CONFIG_SUNRPC_GSS=m
+ CONFIG_SUNRPC_BACKCHANNEL=y
+diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
+index e55f86713948..872c9b7671b1 100644
+--- a/fs/nfs/Kconfig
++++ b/fs/nfs/Kconfig
+@@ -196,3 +196,14 @@ config NFS_DEBUG
+ depends on NFS_FS && SUNRPC_DEBUG
+ select CRC32
+ default y
++
++config ENFS
++ tristate "NFS client support for ENFS"
++ depends on NFS_FS
++ default n
++ help
++ This option enables support multipath of the NFS protocol
++ in the kernel's NFS client.
++ This feature will improve performance and reliability.
++
++ If sure, say Y.
+diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
+index c587e3c4c6a6..19d0ac2ba3b8 100644
+--- a/fs/nfs/Makefile
++++ b/fs/nfs/Makefile
+@@ -12,6 +12,7 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
+ nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
+ nfs-$(CONFIG_SYSCTL) += sysctl.o
+ nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
++nfs-$(CONFIG_ENFS) += enfs_adapter.o
+
+ obj-$(CONFIG_NFS_V2) += nfsv2.o
+ nfsv2-y := nfs2super.o proc.o nfs2xdr.o
+@@ -34,3 +35,5 @@ nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o
+ obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
+ obj-$(CONFIG_PNFS_BLOCK) += blocklayout/
+ obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/
++
++obj-$(CONFIG_ENFS) += enfs/
+diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
+index 090658c3da12..fe4e3b28c5d1 100644
+--- a/net/sunrpc/Makefile
++++ b/net/sunrpc/Makefile
+@@ -19,3 +19,4 @@ sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
+ sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o
+ sunrpc-$(CONFIG_PROC_FS) += stats.o
+ sunrpc-$(CONFIG_SYSCTL) += sysctl.o
++sunrpc-$(CONFIG_ENFS) += sunrpc_enfs_adapter.o
--
2.25.0.windows.1
1
0

[PATCH openEuler-1.0-LTS] netfilter: ipset: add the missing IP_SET_HASH_WITH_NET0 macro for ip_set_hash_netportnet.c
by Lu Wei 25 Sep '23
by Lu Wei 25 Sep '23
25 Sep '23
From: Kyle Zeng <zengyhkyle(a)gmail.com>
mainline inclusion
from mainline-v4.20-rc2
commit 886503f34d63e681662057448819edb5b1057a97
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I83QCZ
CVE: CVE-2023-42753
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
---------------------------
The missing IP_SET_HASH_WITH_NET0 macro in ip_set_hash_netportnet can
lead to the use of wrong `CIDR_POS(c)` for calculating array offsets,
which can lead to integer underflow. As a result, it leads to slab
out-of-bound access.
This patch adds back the IP_SET_HASH_WITH_NET0 macro to
ip_set_hash_netportnet to address the issue.
Fixes: 886503f34d63 ("netfilter: ipset: actually allow allowable CIDR 0 in hash:net,port,net")
Suggested-by: Jozsef Kadlecsik <kadlec(a)netfilter.org>
Signed-off-by: Kyle Zeng <zengyhkyle(a)gmail.com>
Acked-by: Jozsef Kadlecsik <kadlec(a)netfilter.org>
Signed-off-by: Florian Westphal <fw(a)strlen.de>
Signed-off-by: Lu Wei <luwei32(a)huawei.com>
---
net/netfilter/ipset/ip_set_hash_netportnet.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 613e18e720a4..9290a4d7b862 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -39,6 +39,7 @@ MODULE_ALIAS("ip_set_hash:net,port,net");
#define IP_SET_HASH_WITH_PROTO
#define IP_SET_HASH_WITH_NETS
#define IPSET_NET_COUNT 2
+#define IP_SET_HASH_WITH_NET0
/* IPv4 variant */
--
2.34.1
2
1

25 Sep '23
LoongArch inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I6BWFP
--------------------------------
Signed-off-by: Juxin Gao <gaojuxin(a)loongson.cn>
Signed-off-by: Ming Wang <wangming01(a)loongson.cn>
---
drivers/gpio/Kconfig | 3 +-
drivers/gpio/gpio-loongson.c | 414 ++++++++++++++++++++++++++++-------
2 files changed, 341 insertions(+), 76 deletions(-)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index f45c6a36551c..be0cf9c87cd6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -377,7 +377,8 @@ config GPIO_LOGICVC
config GPIO_LOONGSON
bool "Loongson-2/3 GPIO support"
- depends on CPU_LOONGSON2EF || CPU_LOONGSON64
+ depends on CPU_LOONGSON2EF || CPU_LOONGSON64 || LOONGARCH
+ default m
help
Driver for GPIO functionality on Loongson-2F/3A/3B processors.
diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c
index a42145873cc9..a3a3d647a043 100644
--- a/drivers/gpio/gpio-loongson.c
+++ b/drivers/gpio/gpio-loongson.c
@@ -1,13 +1,13 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Loongson-2F/3A/3B GPIO Support
+ * Loongson-3A/3B/3C/7A GPIO Support
*
- * Copyright (c) 2008 Richard Liu, STMicroelectronics <richard.liu(a)st.com>
- * Copyright (c) 2008-2010 Arnaud Patard <apatard(a)mandriva.com>
- * Copyright (c) 2013 Hongbing Hu <huhb(a)lemote.com>
- * Copyright (c) 2014 Huacai Chen <chenhc(a)lemote.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*/
+#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -16,120 +16,384 @@
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
+#include <linux/property.h>
#include <asm/types.h>
-#include <loongson.h>
-#define STLS2F_N_GPIO 4
-#define STLS3A_N_GPIO 16
+/* ============== Data structrues =============== */
-#ifdef CONFIG_CPU_LOONGSON64
-#define LOONGSON_N_GPIO STLS3A_N_GPIO
-#else
-#define LOONGSON_N_GPIO STLS2F_N_GPIO
-#endif
+/* gpio data */
+struct platform_gpio_data {
+ u32 gpio_conf;
+ u32 gpio_out;
+ u32 gpio_in;
+ u32 in_start_bit;
+ u32 support_irq;
+ char *label;
+ int gpio_base;
+ int ngpio;
+};
+
+#define GPIO_IO_CONF(x) (x->base + x->conf_offset)
+#define GPIO_OUT(x) (x->base + x->out_offset)
+#define GPIO_IN(x) (x->base + x->in_offset)
+
+#define LS7A_GPIO_OEN_BYTE(x, gpio) (x->base + x->conf_offset + gpio)
+#define LS7A_GPIO_OUT_BYTE(x, gpio) (x->base + x->out_offset + gpio)
+#define LS7A_GPIO_IN_BYTE(x, gpio) (x->base + x->in_offset + gpio)
+
+struct loongson_gpio_chip {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ void __iomem *base;
+ int conf_offset;
+ int out_offset;
+ int in_offset;
+ int in_start_bit;
+ u16 *gsi_idx_map;
+ u16 mapsize;
+ bool support_irq;
+};
/*
- * Offset into the register where we read lines, we write them from offset 0.
- * This offset is the only thing that stand between us and using
- * GPIO_GENERIC.
+ * GPIO primitives.
*/
-#define LOONGSON_GPIO_IN_OFFSET 16
+static int loongson_gpio_request(struct gpio_chip *chip, unsigned int pin)
+{
+ if (pin >= chip->ngpio)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+static inline void
+__set_direction(struct loongson_gpio_chip *lgpio, unsigned int pin, int input)
+{
+ u64 temp;
+ u8 value;
-static DEFINE_SPINLOCK(gpio_lock);
+ if (!strcmp(lgpio->chip.label, "loongson,loongson3-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0007", 8)) {
+ temp = readq(GPIO_IO_CONF(lgpio));
+ if (input)
+ temp |= 1ULL << pin;
+ else
+ temp &= ~(1ULL << pin);
+ writeq(temp, GPIO_IO_CONF(lgpio));
+ return;
+ }
+ if (!strcmp(lgpio->chip.label, "loongson,ls7a-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0002", 8)) {
+ if (input)
+ value = 1;
+ else
+ value = 0;
+ writeb(value, LS7A_GPIO_OEN_BYTE(lgpio, pin));
+ return;
+ }
+}
-static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+static void __set_level(struct loongson_gpio_chip *lgpio, unsigned int pin, int high)
{
- u32 val;
+ u64 temp;
+ u8 value;
- spin_lock(&gpio_lock);
- val = LOONGSON_GPIODATA;
- spin_unlock(&gpio_lock);
+ /* If GPIO controller is on 3A,then... */
+ if (!strcmp(lgpio->chip.label, "loongson,loongson3-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0007", 8)) {
+ temp = readq(GPIO_OUT(lgpio));
+ if (high)
+ temp |= 1ULL << pin;
+ else
+ temp &= ~(1ULL << pin);
+ writeq(temp, GPIO_OUT(lgpio));
+ return;
+ }
- return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET));
+ if (!strcmp(lgpio->chip.label, "loongson,ls7a-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0002", 8)) {
+ if (high)
+ value = 1;
+ else
+ value = 0;
+ writeb(value, LS7A_GPIO_OUT_BYTE(lgpio, pin));
+ return;
+ }
}
-static void loongson_gpio_set_value(struct gpio_chip *chip,
- unsigned gpio, int value)
+static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
{
- u32 val;
+ unsigned long flags;
+ struct loongson_gpio_chip *lgpio =
+ container_of(chip, struct loongson_gpio_chip, chip);
- spin_lock(&gpio_lock);
- val = LOONGSON_GPIODATA;
- if (value)
- val |= BIT(gpio);
- else
- val &= ~BIT(gpio);
- LOONGSON_GPIODATA = val;
- spin_unlock(&gpio_lock);
+ spin_lock_irqsave(&lgpio->lock, flags);
+ __set_direction(lgpio, pin, 1);
+ spin_unlock_irqrestore(&lgpio->lock, flags);
+
+ return 0;
}
-static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+static int loongson_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int pin, int value)
{
- u32 temp;
+ struct loongson_gpio_chip *lgpio =
+ container_of(chip, struct loongson_gpio_chip, chip);
+ unsigned long flags;
- spin_lock(&gpio_lock);
- temp = LOONGSON_GPIOIE;
- temp |= BIT(gpio);
- LOONGSON_GPIOIE = temp;
- spin_unlock(&gpio_lock);
+ spin_lock_irqsave(&lgpio->lock, flags);
+ __set_level(lgpio, pin, value);
+ __set_direction(lgpio, pin, 0);
+ spin_unlock_irqrestore(&lgpio->lock, flags);
return 0;
}
-static int loongson_gpio_direction_output(struct gpio_chip *chip,
- unsigned gpio, int level)
+static int loongson_gpio_get(struct gpio_chip *chip, unsigned int pin)
{
- u32 temp;
+ struct loongson_gpio_chip *lgpio =
+ container_of(chip, struct loongson_gpio_chip, chip);
+ u64 temp;
+ u8 value;
+
+ /* GPIO controller in 3A is different for 7A */
+ if (!strcmp(lgpio->chip.label, "loongson,loongson3-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0007", 8)) {
+ temp = readq(GPIO_IN(lgpio));
+ return ((temp & (1ULL << (pin + lgpio->in_start_bit))) != 0);
+ }
+
+ if (!strcmp(lgpio->chip.label, "loongson,ls7a-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0002", 8)) {
+ value = readb(LS7A_GPIO_IN_BYTE(lgpio, pin));
+ return (value & 1);
+ }
+
+ return -ENXIO;
+}
+
+static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
+{
+ struct loongson_gpio_chip *lgpio =
+ container_of(chip, struct loongson_gpio_chip, chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&lgpio->lock, flags);
+ __set_level(lgpio, pin, value);
+ spin_unlock_irqrestore(&lgpio->lock, flags);
+}
+
+static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct platform_device *pdev =
+ container_of(chip->parent, struct platform_device, dev);
+ struct loongson_gpio_chip *lgpio =
+ container_of(chip, struct loongson_gpio_chip, chip);
+
+ if (offset >= chip->ngpio)
+ return -EINVAL;
+
+ if ((lgpio->gsi_idx_map != NULL) && (offset < lgpio->mapsize))
+ offset = lgpio->gsi_idx_map[offset];
+
+ return platform_get_irq(pdev, offset);
+}
+
+static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgpio,
+ struct device_node *np,
+ void __iomem *base)
+{
+ lgpio->chip.request = loongson_gpio_request;
+ lgpio->chip.direction_input = loongson_gpio_direction_input;
+ lgpio->chip.get = loongson_gpio_get;
+ lgpio->chip.direction_output = loongson_gpio_direction_output;
+ lgpio->chip.set = loongson_gpio_set;
+ lgpio->chip.can_sleep = 0;
+ lgpio->chip.fwnode = dev_fwnode(dev);
+ lgpio->chip.parent = dev;
+ spin_lock_init(&lgpio->lock);
+ lgpio->base = (void __iomem *)base;
+
+ if (!strcmp(lgpio->chip.label, "loongson,ls7a-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0002", 8) ||
+ !strcmp(lgpio->chip.label, "loongson,loongson3-gpio") ||
+ !strncmp(lgpio->chip.label, "LOON0007", 8)) {
- loongson_gpio_set_value(chip, gpio, level);
- spin_lock(&gpio_lock);
- temp = LOONGSON_GPIOIE;
- temp &= ~BIT(gpio);
- LOONGSON_GPIOIE = temp;
- spin_unlock(&gpio_lock);
+ lgpio->chip.to_irq = loongson_gpio_to_irq;
+ }
+ gpiochip_add(&lgpio->chip);
return 0;
}
+
+static void of_loongson_gpio_get_props(struct device_node *np,
+ struct loongson_gpio_chip *lgpio)
+{
+ const char *name;
+
+ of_property_read_u32(np, "ngpios", (u32 *)&lgpio->chip.ngpio);
+ of_property_read_u32(np, "gpio_base", (u32 *)&lgpio->chip.base);
+ of_property_read_u32(np, "conf_offset", (u32 *)&lgpio->conf_offset);
+ of_property_read_u32(np, "out_offset", (u32 *)&lgpio->out_offset);
+ of_property_read_u32(np, "in_offset", (u32 *)&lgpio->in_offset);
+ of_property_read_string(np, "compatible", &name);
+ if (!strcmp(name, "loongson,loongson3-gpio")) {
+ of_property_read_u32(np, "in_start_bit",
+ (u32 *)&lgpio->in_start_bit);
+ if (of_property_read_bool(np, "support_irq"))
+ lgpio->support_irq = true;
+ }
+ lgpio->chip.label = kstrdup(name, GFP_KERNEL);
+}
+
+static void acpi_loongson_gpio_get_props(struct platform_device *pdev,
+ struct loongson_gpio_chip *lgpio)
+{
+
+ struct device *dev = &pdev->dev;
+ int rval;
+
+ device_property_read_u32(dev, "ngpios", (u32 *)&lgpio->chip.ngpio);
+ device_property_read_u32(dev, "gpio_base", (u32 *)&lgpio->chip.base);
+ device_property_read_u32(dev, "conf_offset", (u32 *)&lgpio->conf_offset);
+ device_property_read_u32(dev, "out_offset", (u32 *)&lgpio->out_offset);
+ device_property_read_u32(dev, "in_offset", (u32 *)&lgpio->in_offset);
+ rval = device_property_read_u16_array(dev, "gsi_idx_map", NULL, 0);
+ if (rval > 0) {
+ lgpio->gsi_idx_map =
+ kmalloc_array(rval, sizeof(*lgpio->gsi_idx_map),
+ GFP_KERNEL);
+ if (unlikely(!lgpio->gsi_idx_map)) {
+ dev_err(dev, "Alloc gsi_idx_map fail!\n");
+ } else {
+ lgpio->mapsize = rval;
+ device_property_read_u16_array(dev, "gsi_idx_map",
+ lgpio->gsi_idx_map, lgpio->mapsize);
+ }
+ }
+ if (!strcmp(pdev->name, "LOON0007")) {
+ device_property_read_u32(dev, "in_start_bit",
+ (u32 *)&lgpio->in_start_bit);
+ if (device_property_read_bool(dev, "support_irq"))
+ lgpio->support_irq = true;
+ }
+ lgpio->chip.label = kstrdup(pdev->name, GFP_KERNEL);
+}
+
+static void platform_loongson_gpio_get_props(struct platform_device *pdev,
+ struct loongson_gpio_chip *lgpio)
+{
+ struct platform_gpio_data *gpio_data =
+ (struct platform_gpio_data *)pdev->dev.platform_data;
+
+ lgpio->chip.ngpio = gpio_data->ngpio;
+ lgpio->chip.base = gpio_data->gpio_base;
+ lgpio->conf_offset = gpio_data->gpio_conf;
+ lgpio->out_offset = gpio_data->gpio_out;
+ lgpio->in_offset = gpio_data->gpio_in;
+ if (!strcmp(gpio_data->label, "loongson,loongson3-gpio")) {
+ lgpio->in_start_bit = gpio_data->in_start_bit;
+ lgpio->support_irq = gpio_data->support_irq;
+ }
+ lgpio->chip.label = kstrdup(gpio_data->label, GFP_KERNEL);
+}
+
static int loongson_gpio_probe(struct platform_device *pdev)
{
- struct gpio_chip *gc;
+ struct resource *iores;
+ void __iomem *base;
+ struct loongson_gpio_chip *lgpio;
+ struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
+ int ret = 0;
- gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
- if (!gc)
+ lgpio = kzalloc(sizeof(struct loongson_gpio_chip), GFP_KERNEL);
+ if (!lgpio)
return -ENOMEM;
- gc->label = "loongson-gpio-chip";
- gc->base = 0;
- gc->ngpio = LOONGSON_N_GPIO;
- gc->get = loongson_gpio_get_value;
- gc->set = loongson_gpio_set_value;
- gc->direction_input = loongson_gpio_direction_input;
- gc->direction_output = loongson_gpio_direction_output;
+ if (np)
+ of_loongson_gpio_get_props(np, lgpio);
+ else if (ACPI_COMPANION(&pdev->dev))
+ acpi_loongson_gpio_get_props(pdev, lgpio);
+ else
+ platform_loongson_gpio_get_props(pdev, lgpio);
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores) {
+ ret = -ENODEV;
+ goto out;
+ }
+ if (!request_mem_region(iores->start, resource_size(iores),
+ pdev->name)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ base = ioremap(iores->start, resource_size(iores));
+ if (!base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ platform_set_drvdata(pdev, lgpio);
+ loongson_gpio_init(dev, lgpio, np, base);
- return gpiochip_add_data(gc, NULL);
+ return 0;
+out:
+ pr_err("%s: %s: missing mandatory property\n", __func__, np->name);
+ return ret;
}
-static struct platform_driver loongson_gpio_driver = {
+static int loongson_gpio_remove(struct platform_device *pdev)
+{
+ struct loongson_gpio_chip *lgpio = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ gpiochip_remove(&lgpio->chip);
+ iounmap(lgpio->base);
+ kfree(lgpio->gsi_idx_map);
+ kfree(lgpio);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, resource_size(mem));
+ return 0;
+}
+
+static const struct of_device_id loongson_gpio_dt_ids[] = {
+ { .compatible = "loongson,loongson3-gpio"},
+ { .compatible = "loongson,ls7a-gpio"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, loongson_gpio_dt_ids);
+
+static const struct acpi_device_id loongson_gpio_acpi_match[] = {
+ {"LOON0002"},
+ {"LOON0007"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, loongson_gpio_acpi_match);
+
+static struct platform_driver ls_gpio_driver = {
.driver = {
.name = "loongson-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = loongson_gpio_dt_ids,
+ .acpi_match_table = ACPI_PTR(loongson_gpio_acpi_match),
},
.probe = loongson_gpio_probe,
+ .remove = loongson_gpio_remove,
};
static int __init loongson_gpio_setup(void)
{
- struct platform_device *pdev;
- int ret;
-
- ret = platform_driver_register(&loongson_gpio_driver);
- if (ret) {
- pr_err("error registering loongson GPIO driver\n");
- return ret;
- }
+ return platform_driver_register(&ls_gpio_driver);
+}
+subsys_initcall(loongson_gpio_setup);
- pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0);
- return PTR_ERR_OR_ZERO(pdev);
+static void __exit loongson_gpio_driver(void)
+{
+ platform_driver_unregister(&ls_gpio_driver);
}
-postcore_initcall(loongson_gpio_setup);
+module_exit(loongson_gpio_driver);
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_DESCRIPTION("LOONGSON GPIO");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:loongson_gpio");
--
2.39.2
2
1

25 Sep '23
From: Shuchang Li <lishuchang(a)hust.edu.cn>
stable inclusion
from stable-v5.10.180
commit bab8dc38b1a0a12bc064fc064269033bdcf5b88e
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I7ZCDZ
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=…
--------------------------------
[ Upstream commit 91a0c0c1413239d0548b5aac4c82f38f6d53a91e ]
When if_type equals zero and pci_resource_start(pdev, PCI_64BIT_BAR4)
returns false, drbl_regs_memmap_p is not remapped. This passes a NULL
pointer to iounmap(), which can trigger a WARN() on certain arches.
When if_type equals six and pci_resource_start(pdev, PCI_64BIT_BAR4)
returns true, drbl_regs_memmap_p may has been remapped and
ctrl_regs_memmap_p is not remapped. This is a resource leak and passes a
NULL pointer to iounmap().
To fix these issues, we need to add null checks before iounmap(), and
change some goto labels.
Fixes: 1351e69fc6db ("scsi: lpfc: Add push-to-adapter support to sli4")
Signed-off-by: Shuchang Li <lishuchang(a)hust.edu.cn>
Link: https://lore.kernel.org/r/20230404072133.1022-1-lishuchang@hust.edu.cn
Reviewed-by: Justin Tee <justin.tee(a)broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen(a)oracle.com>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Signed-off-by: Yong Hu <yong.hu(a)windriver.com>
---
drivers/scsi/lpfc/lpfc_init.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 17200b453cbb..1bb3c96a04bd 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -10477,7 +10477,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
goto out_iounmap_all;
} else {
error = -ENOMEM;
- goto out_iounmap_all;
+ goto out_iounmap_ctrl;
}
}
@@ -10495,7 +10495,7 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
dev_err(&pdev->dev,
"ioremap failed for SLI4 HBA dpp registers.\n");
error = -ENOMEM;
- goto out_iounmap_ctrl;
+ goto out_iounmap_all;
}
phba->pci_bar4_memmap_p = phba->sli4_hba.dpp_regs_memmap_p;
}
@@ -10520,9 +10520,11 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba)
return 0;
out_iounmap_all:
- iounmap(phba->sli4_hba.drbl_regs_memmap_p);
+ if (phba->sli4_hba.drbl_regs_memmap_p)
+ iounmap(phba->sli4_hba.drbl_regs_memmap_p);
out_iounmap_ctrl:
- iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
+ if (phba->sli4_hba.ctrl_regs_memmap_p)
+ iounmap(phba->sli4_hba.ctrl_regs_memmap_p);
out_iounmap_conf:
iounmap(phba->sli4_hba.conf_regs_memmap_p);
--
2.34.1
2
1

[PATCH OLK-5.10] scsi: lpfc: Prevent lpfc_debugfs_lockstat_write() buffer overflow
by Yong Hu 25 Sep '23
by Yong Hu 25 Sep '23
25 Sep '23
From: Justin Tee <justin.tee(a)broadcom.com>
stable inclusion
from stable-v5.10.181
commit e0e7faee3a7dd6f51350cda64997116a247eb045
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I7ZCDZ
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=…
--------------------------------
[ Upstream commit c6087b82a9146826564a55c5ca0164cac40348f5 ]
A static code analysis tool flagged the possibility of buffer overflow when
using copy_from_user() for a debugfs entry.
Currently, it is possible that copy_from_user() copies more bytes than what
would fit in the mybuf char array. Add a min() restriction check between
sizeof(mybuf) - 1 and nbytes passed from the userspace buffer to protect
against buffer overflow.
Link: https://lore.kernel.org/r/20230301231626.9621-2-justintee8345@gmail.com
Signed-off-by: Justin Tee <justin.tee(a)broadcom.com>
Signed-off-by: Martin K. Petersen <martin.petersen(a)oracle.com>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Signed-off-by: Yong Hu <yong.hu(a)windriver.com>
---
drivers/scsi/lpfc/lpfc_debugfs.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index fbc76d69ea0b..2b77cbbcdccb 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -2159,10 +2159,13 @@ lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf,
char mybuf[64];
char *pbuf;
int i;
+ size_t bsize;
memset(mybuf, 0, sizeof(mybuf));
- if (copy_from_user(mybuf, buf, nbytes))
+ bsize = min(nbytes, (sizeof(mybuf) - 1));
+
+ if (copy_from_user(mybuf, buf, bsize))
return -EFAULT;
pbuf = &mybuf[0];
@@ -2183,7 +2186,7 @@ lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf,
qp->lock_conflict.wq_access = 0;
}
}
- return nbytes;
+ return bsize;
}
#endif
--
2.34.1
2
1

25 Sep '23
yangyinglu (5):
LoongArch: add kernel setvirtmap for runtime
LoongArch: Old BPI compatibility
LoongArch: Fix virtual machine startup error
LoongArch: Fixed EIOINTC structure members
LoongArch: use arch specific phys_to_dma
arch/loongarch/Kconfig | 1 +
arch/loongarch/include/asm/addrspace.h | 1 +
arch/loongarch/include/asm/efi.h | 1 +
arch/loongarch/include/asm/irq.h | 1 +
arch/loongarch/include/asm/loongarch.h | 1 +
arch/loongarch/kernel/Makefile | 1 +
arch/loongarch/kernel/acpi.c | 7 +-
arch/loongarch/kernel/dma.c | 26 +-
arch/loongarch/kernel/efi.c | 175 ++++++++-
arch/loongarch/kernel/env.c | 6 +
arch/loongarch/kernel/irq.c | 25 +-
arch/loongarch/kernel/legacy_boot.c | 484 +++++++++++++++++++++++++
arch/loongarch/kernel/legacy_boot.h | 90 +++++
arch/loongarch/kernel/mem.c | 26 +-
arch/loongarch/kernel/numa.c | 39 +-
arch/loongarch/kernel/reset.c | 3 +-
arch/loongarch/kernel/setup.c | 18 +-
arch/loongarch/kernel/smp.c | 6 +-
arch/loongarch/pci/acpi.c | 147 +++++++-
drivers/firmware/efi/Makefile | 1 +
drivers/irqchip/irq-loongarch-cpu.c | 7 +-
drivers/irqchip/irq-loongson-eiointc.c | 46 ++-
drivers/irqchip/irq-loongson-pch-pic.c | 5 +
23 files changed, 1075 insertions(+), 42 deletions(-)
create mode 100644 arch/loongarch/kernel/legacy_boot.c
create mode 100644 arch/loongarch/kernel/legacy_boot.h
--
2.20.1
2
6
Ming Wang (5):
rtc: Add rtc driver for the Loongson family chips
LoongArch: kdump: Add memory reservation for old kernel
LoongArch: kexec: Add compatibility with old interfaces
LoongArch: Fix kdump failure on v40 interface specification
LoongArch: kdump: Add high memory reservation
arch/loongarch/kernel/machine_kexec.c | 45 ++-
arch/loongarch/kernel/setup.c | 94 +++++-
drivers/rtc/Kconfig | 13 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-loongson.c | 397 ++++++++++++++++++++++++++
5 files changed, 535 insertions(+), 15 deletions(-)
create mode 100644 drivers/rtc/rtc-loongson.c
--
2.39.2
2
6