Kernel
Threads by month
- ----- 2025 -----
- July
- June
- May
- 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
- 57 participants
- 19202 discussions

[PATCH OLK-5.10] netfilter: ipset: add the missing IP_SET_HASH_WITH_NET0 macro for ip_set_hash_netportnet.c
by Lu Wei 09 Oct '23
by Lu Wei 09 Oct '23
09 Oct '23
From: Kyle Zeng <zengyhkyle(a)gmail.com>
mainline inclusion
from mainline-v6.6-rc1
commit 050d91c03b28ca479df13dfb02bcd2c60dd6a878
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 144346faffc1..b8ec2c414a5f 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -35,6 +35,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

09 Oct '23
Hongchen Zhang (13):
LoongArch: fix ls2k500 bmc not work when installing iso
LS7A2000 : Add quirk for OHCI device rev 0x02
PCI: Check if entry->offset already exist for mem resource
PCI: Check if the pci controller can use both CFG0 and CFG1 mode to
access configuration space
PCI: PM: Fix pcie mrrs restoring
pci: fix kabi error caused by pm_suspend_target_state
LoongArch: Fixed some pcie card not scanning properly
pci/quirks: ls7a2000: fix pm transition of devices under pcie port
LS7A2000: PCIE: Fixup GPU card error
pci: fix X server auto probe fail when both ast and etnaviv drm
present
LoongArch: pci root bridige set acpi companion only when not
acpi_disabled.
LoongArch: Fix secondary bridge routing errors
pci: irq: Add early_param pci_irq_limit to limit pci irq numbers
arch/loongarch/pci/acpi.c | 23 ++--
drivers/gpu/drm/loongson/loongson_module.c | 15 +++
drivers/irqchip/irq-loongson-pch-pic.c | 6 +-
drivers/pci/controller/pci-loongson.c | 147 ++++++++++++++++++++-
drivers/pci/msi/msi.c | 25 ++++
drivers/pci/pci.c | 20 ++-
6 files changed, 217 insertions(+), 19 deletions(-)
--
2.33.0
2
14

09 Oct '23
From: "Borislav Petkov (AMD)" <bp(a)alien8.de>
stable inclusion
from stable-v5.10.189
commit 073a28a9b50662991e7d6956c2cf2fc5d54f28cd
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I7RQ67
CVE: CVE-2023-20569
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
--------------------------------
Upstream commit: 0e52740ffd10c6c316837c6c128f460f1aaba1ea
There was never a doubt in my mind that they would not fit into a single
u32 eventually.
Signed-off-by: Borislav Petkov (AMD) <bp(a)alien8.de>
Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Conflicts:
arch/x86/include/asm/cpufeatures.h
tools/arch/x86/include/asm/cpufeatures.h
Signed-off-by: Jialin Zhang <zhangjialin11(a)huawei.com>
---
arch/x86/include/asm/cpufeatures.h | 2 +-
tools/arch/x86/include/asm/cpufeatures.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 1a6680712025..f51fc445a24b 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -14,7 +14,7 @@
* Defines x86 CPU feature bits
*/
#define NCAPINTS 19 /* N 32-bit words worth of info */
-#define NBUGINTS 1 /* N 32-bit bug flags */
+#define NBUGINTS 2 /* N 32-bit bug flags */
/*
* Note: If the comment begins with a quoted string, that string is used
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 238287abee0c..6186aebff9c0 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -14,7 +14,7 @@
* Defines x86 CPU feature bits
*/
#define NCAPINTS 19 /* N 32-bit words worth of info */
-#define NBUGINTS 1 /* N 32-bit bug flags */
+#define NBUGINTS 2 /* N 32-bit bug flags */
/*
* Note: If the comment begins with a quoted string, that string is used
--
2.25.1
1
26
From: JiangShui Yang <yangjiangshui(a)h-partners.com>
Weili Qian (3):
uacce: fix NULL pointer when unbind device
uacce: cleanup some unused codes
uacce: remove unused file 'dev_state'
drivers/crypto/hisilicon/qm.c | 12 -----
drivers/misc/uacce/uacce.c | 87 +++++++++++------------------------
include/linux/uacce.h | 17 -------
3 files changed, 27 insertions(+), 89 deletions(-)
--
2.33.0
2
4

[PATCH OLK-5.10 00/19] Introduce PBHA and PBHA bit0 to control the usage of HBM Cache precisely
by Wupeng Ma 09 Oct '23
by Wupeng Ma 09 Oct '23
09 Oct '23
From: Ma Wupeng <mawupeng1(a)huawei.com>
Patch 1: move FDT init out of kaslr_early_init for future use.
Patch 2 to 8: enable feature PBHA for arm64.
Patch 9-18: Control the usage of HBM cache for kernel and task precisely.
Patch 19: Enable feature PBHA for arm64 by default.
James Morse (7):
KVM: arm64: Detect and enable PBHA for stage2
dt-bindings: Rename the description of cpu nodes cpu.yaml
dt-bindings: arm: Add binding for Page Based Hardware Attributes
arm64: cpufeature: Enable PBHA bits for stage1
arm64: mm: Add pgprot_pbha() to allow drivers to request PBHA values
KVM: arm64: Configure PBHA bits for stage2
Documentation: arm64: Describe the support and expectations for PBHA
Ma Wupeng (11):
arm64: cpufeature: Enable PBHA for stage1 early via FDT
arm64: mm: Detect and enable PBHA bit0 at early startup
arm64: mm: Update kernel pte entries if pbha bit0 enabled
arm64: mm: Show PBHA bit 59 as PBHA0 in ptdump
arm64: mm: Introduce VM_PBHA_BIT0 to enable pbha bit0 for single vma
arm64: mm: Set PBHA0 bit for VM_PBHA_BIT0
arm64: mm: Introduce sysfs interface to bypass whole task
arm64: mm: Set flag VM_PBHA_BIT0 for global init task
arm64: mm: Introduce prctl to control pbha behavior
arm64: mm: Introduce kernel param pbha
openeuler: configs: arm64: Enable PBHA by default
Marc Zyngier (1):
arm64: Extract early FDT mapping from kaslr_early_init()
.../admin-guide/kernel-parameters.txt | 8 +
Documentation/arm64/index.rst | 1 +
Documentation/arm64/pbha.rst | 85 +++
.../devicetree/bindings/arm/cpu.yaml | 537 ++++++++++++++++
.../devicetree/bindings/arm/cpus.yaml | 584 +++---------------
arch/arm64/Kconfig | 20 +
arch/arm64/configs/openeuler_defconfig | 1 +
arch/arm64/include/asm/cpucaps.h | 3 +
arch/arm64/include/asm/cpufeature.h | 15 +
arch/arm64/include/asm/kvm_arm.h | 1 +
arch/arm64/include/asm/kvm_pgtable.h | 9 +
arch/arm64/include/asm/mman.h | 10 +
arch/arm64/include/asm/pgtable-hwdef.h | 6 +
arch/arm64/include/asm/pgtable.h | 26 +
arch/arm64/include/asm/setup.h | 3 +
arch/arm64/include/uapi/asm/mman.h | 1 +
arch/arm64/kernel/cpufeature.c | 258 ++++++++
arch/arm64/kernel/head.S | 6 +-
arch/arm64/kernel/image-vars.h | 3 +
arch/arm64/kernel/kaslr.c | 7 +-
arch/arm64/kernel/setup.c | 15 +
arch/arm64/kvm/reset.c | 15 +-
arch/arm64/mm/hugetlbpage.c | 2 +
arch/arm64/mm/mmu.c | 14 +-
arch/arm64/mm/ptdump.c | 5 +
.../firmware/efi/libstub/efi-stub-helper.c | 3 +
drivers/firmware/efi/libstub/fdt.c | 57 ++
drivers/soc/hisilicon/Makefile | 1 +
drivers/soc/hisilicon/pbha.c | 204 ++++++
fs/proc/base.c | 103 +++
fs/proc/task_mmu.c | 3 +
include/linux/mm.h | 8 +-
include/linux/pbha.h | 67 ++
include/uapi/asm-generic/mman-common.h | 1 +
include/uapi/linux/prctl.h | 2 +
kernel/sys.c | 10 +
mm/memory.c | 4 +
mm/vmalloc.c | 5 +
38 files changed, 1580 insertions(+), 523 deletions(-)
create mode 100644 Documentation/arm64/pbha.rst
create mode 100644 Documentation/devicetree/bindings/arm/cpu.yaml
create mode 100644 drivers/soc/hisilicon/pbha.c
create mode 100644 include/linux/pbha.h
--
2.25.1
2
20
stable inclusion
from stable-v5.10.195
commit 7c8ddcdab1b900bed69cad6beef477fff116289e
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I869PY
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
---------------------------
[ Upstream commit ac28b1ec6135649b5d78b028e47264cb3ebca5ea ]
I got the below warning when do fuzzing test:
unregister_netdevice: waiting for bond0 to become free. Usage count = 2
It can be repoduced via:
ip link add bond0 type bond
sysctl -w net.ipv4.conf.bond0.promote_secondaries=1
ip addr add 4.117.174.103/0 scope 0x40 dev bond0
ip addr add 192.168.100.111/255.255.255.254 scope 0 dev bond0
ip addr add 0.0.0.4/0 scope 0x40 secondary dev bond0
ip addr del 4.117.174.103/0 scope 0x40 dev bond0
ip link delete bond0 type bond
In this reproduction test case, an incorrect 'last_prim' is found in
__inet_del_ifa(), as a result, the secondary address(0.0.0.4/0 scope 0x40)
is lost. The memory of the secondary address is leaked and the reference of
in_device and net_device is leaked.
Fix this problem:
Look for 'last_prim' starting at location of the deleted IP and inserting
the promoted IP into the location of 'last_prim'.
Fixes: 0ff60a45678e ("[IPV4]: Fix secondary IP addresses after promotion")
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
Signed-off-by: Julian Anastasov <ja(a)ssi.bg>
Signed-off-by: David S. Miller <davem(a)davemloft.net>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
---
net/ipv4/devinet.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 88b6120878cd..da1ca8081c03 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -351,14 +351,14 @@ static void __inet_del_ifa(struct in_device *in_dev,
{
struct in_ifaddr *promote = NULL;
struct in_ifaddr *ifa, *ifa1;
- struct in_ifaddr *last_prim;
+ struct in_ifaddr __rcu **last_prim;
struct in_ifaddr *prev_prom = NULL;
int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
ASSERT_RTNL();
ifa1 = rtnl_dereference(*ifap);
- last_prim = rtnl_dereference(in_dev->ifa_list);
+ last_prim = ifap;
if (in_dev->dead)
goto no_promotions;
@@ -372,7 +372,7 @@ static void __inet_del_ifa(struct in_device *in_dev,
while ((ifa = rtnl_dereference(*ifap1)) != NULL) {
if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
ifa1->ifa_scope <= ifa->ifa_scope)
- last_prim = ifa;
+ last_prim = &ifa->ifa_next;
if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
ifa1->ifa_mask != ifa->ifa_mask ||
@@ -436,9 +436,9 @@ static void __inet_del_ifa(struct in_device *in_dev,
rcu_assign_pointer(prev_prom->ifa_next, next_sec);
- last_sec = rtnl_dereference(last_prim->ifa_next);
+ last_sec = rtnl_dereference(*last_prim);
rcu_assign_pointer(promote->ifa_next, last_sec);
- rcu_assign_pointer(last_prim->ifa_next, promote);
+ rcu_assign_pointer(*last_prim, promote);
}
promote->ifa_flags &= ~IFA_F_SECONDARY;
--
2.34.1
2
1

[PATCH 01/26] [Backport] tools headers cpufeatures: Sync with the kernel sources
by Jialin Zhang 08 Oct '23
by Jialin Zhang 08 Oct '23
08 Oct '23
From: Arnaldo Carvalho de Melo <acme(a)redhat.com>
stable inclusion
from stable-v5.10.189
commit 9b7fe7c6fbc007564f97805ff45882e79f0c70d0
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I7RQ67
CVE: CVE-2023-20569
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
--------------------------------
commit 1a9bcadd0058a3e81c1beca48e5e08dee9446a01 upstream.
To pick the changes from:
3b9c723ed7cfa4e1 ("KVM: SVM: Add support for SVM instruction address check change")
b85a0425d8056f3b ("Enumerate AVX Vector Neural Network instructions")
fb35d30fe5b06cc2 ("x86/cpufeatures: Assign dedicated feature word for CPUID_0x8000001F[EAX]")
This only causes these perf files to be rebuilt:
CC /tmp/build/perf/bench/mem-memcpy-x86-64-asm.o
CC /tmp/build/perf/bench/mem-memset-x86-64-asm.o
And addresses this perf build warning:
Warning: Kernel ABI header at 'tools/arch/x86/include/asm/cpufeatures.h' differs from latest version at 'arch/x86/include/asm/cpufeatures.h'
diff -u tools/arch/x86/include/asm/cpufeatures.h arch/x86/include/asm/cpufeatures.h
Cc: Borislav Petkov <bp(a)suse.de>
Cc: Kyung Min Park <kyung.min.park(a)intel.com>
Cc: Paolo Bonzini <pbonzini(a)redhat.com>
Cc: Sean Christopherson <seanjc(a)google.com>
Cc: Wei Huang <wei.huang2(a)amd.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme(a)redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Conflicts:
tools/arch/x86/include/asm/cpufeatures.h
Signed-off-by: Jialin Zhang <zhangjialin11(a)huawei.com>
---
tools/arch/x86/include/asm/cpufeatures.h | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 1a139412f78e..76a860a3d754 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -96,7 +96,7 @@
#define X86_FEATURE_SYSCALL32 ( 3*32+14) /* "" syscall in IA32 userspace */
#define X86_FEATURE_SYSENTER32 ( 3*32+15) /* "" sysenter in IA32 userspace */
#define X86_FEATURE_REP_GOOD ( 3*32+16) /* REP microcode works well */
-#define X86_FEATURE_SME_COHERENT ( 3*32+17) /* "" AMD hardware-enforced cache coherency */
+/* FREE! ( 3*32+17) */
#define X86_FEATURE_LFENCE_RDTSC ( 3*32+18) /* "" LFENCE synchronizes RDTSC */
#define X86_FEATURE_ACC_POWER ( 3*32+19) /* AMD Accumulated Power Mechanism */
#define X86_FEATURE_NOPL ( 3*32+20) /* The NOPL (0F 1F) instructions */
@@ -201,7 +201,7 @@
#define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 7) /* Effectively INVPCID && CR4.PCIDE=1 */
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
-#define X86_FEATURE_SME ( 7*32+10) /* AMD Secure Memory Encryption */
+/* FREE! ( 7*32+10) */
#define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */
#define X86_FEATURE_KERNEL_IBRS ( 7*32+12) /* "" Set/clear IBRS on kernel entry/exit */
#define X86_FEATURE_RSB_VMEXIT ( 7*32+13) /* "" Fill RSB on VM-Exit */
@@ -211,7 +211,7 @@
#define X86_FEATURE_SSBD ( 7*32+17) /* Speculative Store Bypass Disable */
#define X86_FEATURE_MBA ( 7*32+18) /* Memory Bandwidth Allocation */
#define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* "" Fill RSB on context switches */
-#define X86_FEATURE_SEV ( 7*32+20) /* AMD Secure Encrypted Virtualization */
+/* FREE! ( 7*32+20) */
#define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */
#define X86_FEATURE_USE_IBRS_FW ( 7*32+22) /* "" Use IBRS during runtime firmware calls */
#define X86_FEATURE_SPEC_STORE_BYPASS_DISABLE ( 7*32+23) /* "" Disable Speculative Store Bypass. */
@@ -236,7 +236,6 @@
#define X86_FEATURE_EPT_AD ( 8*32+17) /* Intel Extended Page Table access-dirty bit */
#define X86_FEATURE_VMCALL ( 8*32+18) /* "" Hypervisor supports the VMCALL instruction */
#define X86_FEATURE_VMW_VMMCALL ( 8*32+19) /* "" VMware prefers VMMCALL hypercall instruction */
-#define X86_FEATURE_SEV_ES ( 8*32+20) /* AMD Secure Encrypted Virtualization - Encrypted State */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
@@ -302,6 +301,7 @@
#define X86_FEATURE_MSR_TSX_CTRL (11*32+18) /* "" MSR IA32_TSX_CTRL (Intel) implemented */
/* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */
+#define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */
#define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* AVX512 BFLOAT16 instructions */
/* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */
@@ -346,6 +346,7 @@
#define X86_FEATURE_AVIC (15*32+13) /* Virtual Interrupt Controller */
#define X86_FEATURE_V_VMSAVE_VMLOAD (15*32+15) /* Virtual VMSAVE VMLOAD */
#define X86_FEATURE_VGIF (15*32+16) /* Virtual GIF */
+#define X86_FEATURE_SVME_ADDR_CHK (15*32+28) /* "" SVME addr check */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (ECX), word 16 */
#define X86_FEATURE_AVX512VBMI (16*32+ 1) /* AVX512 Vector Bit Manipulation instructions*/
@@ -374,6 +375,13 @@
#define X86_FEATURE_SUCCOR (17*32+ 1) /* Uncorrectable error containment and recovery */
#define X86_FEATURE_SMCA (17*32+ 3) /* Scalable MCA */
+/* AMD-defined memory encryption features, CPUID level 0x8000001f (EAX), word 19 */
+#define X86_FEATURE_SME (17*32+27) /* AMD Secure Memory Encryption */
+#define X86_FEATURE_SEV (17*32+28) /* AMD Secure Encrypted Virtualization */
+#define X86_FEATURE_VM_PAGE_FLUSH (17*32+29) /* "" VM Page Flush MSR is supported */
+#define X86_FEATURE_SEV_ES (17*32+30) /* AMD Secure Encrypted Virtualization - Encrypted State */
+#define X86_FEATURE_SME_COHERENT (17*32+31) /* "" AMD hardware-enforced cache coherency */
+
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
#define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */
--
2.25.1
1
25
From: Ye Bin <yebin10(a)huawei.com>
stable inclusion
from stable-v4.19.273
commit 965bad2bf1afef64ec16249da676dc7310cca32e
category: bugfix
bugzilla: 188627, https://gitee.com/openeuler/kernel/issues/I85XNK
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=…
--------------------------------
[ Upstream commit d6a95db3c7ad160bc16b89e36449705309b52bcb ]
There's issue as follows when do fault injection:
WARNING: CPU: 1 PID: 14870 at include/linux/quotaops.h:51 dquot_disable+0x13b7/0x18c0
Modules linked in:
CPU: 1 PID: 14870 Comm: fsconfig Not tainted 6.3.0-next-20230505-00006-g5107a9c821af-dirty #541
RIP: 0010:dquot_disable+0x13b7/0x18c0
RSP: 0018:ffffc9000acc79e0 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88825e41b980
RDX: 0000000000000000 RSI: ffff88825e41b980 RDI: 0000000000000002
RBP: ffff888179f68000 R08: ffffffff82087ca7 R09: 0000000000000000
R10: 0000000000000001 R11: ffffed102f3ed026 R12: ffff888179f68130
R13: ffff888179f68110 R14: dffffc0000000000 R15: ffff888179f68118
FS: 00007f450a073740(0000) GS:ffff88882fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffe96f2efd8 CR3: 000000025c8ad000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
dquot_load_quota_sb+0xd53/0x1060
dquot_resume+0x172/0x230
ext4_reconfigure+0x1dc6/0x27b0
reconfigure_super+0x515/0xa90
__x64_sys_fsconfig+0xb19/0xd20
do_syscall_64+0x39/0xb0
entry_SYSCALL_64_after_hwframe+0x63/0xcd
Above issue may happens as follows:
ProcessA ProcessB ProcessC
sys_fsconfig
vfs_fsconfig_locked
reconfigure_super
ext4_remount
dquot_suspend -> suspend all type quota
sys_fsconfig
vfs_fsconfig_locked
reconfigure_super
ext4_remount
dquot_resume
ret = dquot_load_quota_sb
add_dquot_ref
do_open -> open file O_RDWR
vfs_open
do_dentry_open
get_write_access
atomic_inc_unless_negative(&inode->i_writecount)
ext4_file_open
dquot_file_open
dquot_initialize
__dquot_initialize
dqget
atomic_inc(&dquot->dq_count);
__dquot_initialize
__dquot_initialize
dqget
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
ext4_acquire_dquot
-> Return error DQ_ACTIVE_B flag isn't set
dquot_disable
invalidate_dquots
if (atomic_read(&dquot->dq_count))
dqgrab
WARN_ON_ONCE(!test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
-> Trigger warning
In the above scenario, 'dquot->dq_flags' has no DQ_ACTIVE_B is normal when
dqgrab().
To solve above issue just replace the dqgrab() use in invalidate_dquots() with
atomic_inc(&dquot->dq_count).
Signed-off-by: Ye Bin <yebin10(a)huawei.com>
Signed-off-by: Jan Kara <jack(a)suse.cz>
Message-Id: <20230605140731.2427629-3-yebin10(a)huawei.com>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Signed-off-by: Long Li <leo.lilong(a)huawei.com>
---
fs/quota/dquot.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index ca7a38ce46f5..b763b87dc2d0 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -577,7 +577,7 @@ static void invalidate_dquots(struct super_block *sb, int type)
goto restart;
}
- dqgrab(dquot);
+ atomic_inc(&dquot->dq_count);
spin_unlock(&dq_list_lock);
/*
* Once dqput() wakes us up, we know it's time to free
--
2.31.1
2
1

08 Oct '23
mainline inclusion
from mainline-v6.6-rc2
commit ac28b1ec6135649b5d78b028e47264cb3ebca5ea
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I869PY
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
---------------------------
I got the below warning when do fuzzing test:
unregister_netdevice: waiting for bond0 to become free. Usage count = 2
It can be repoduced via:
ip link add bond0 type bond
sysctl -w net.ipv4.conf.bond0.promote_secondaries=1
ip addr add 4.117.174.103/0 scope 0x40 dev bond0
ip addr add 192.168.100.111/255.255.255.254 scope 0 dev bond0
ip addr add 0.0.0.4/0 scope 0x40 secondary dev bond0
ip addr del 4.117.174.103/0 scope 0x40 dev bond0
ip link delete bond0 type bond
In this reproduction test case, an incorrect 'last_prim' is found in
__inet_del_ifa(), as a result, the secondary address(0.0.0.4/0 scope 0x40)
is lost. The memory of the secondary address is leaked and the reference of
in_device and net_device is leaked.
Fix this problem:
Look for 'last_prim' starting at location of the deleted IP and inserting
the promoted IP into the location of 'last_prim'.
Fixes: 0ff60a45678e ("[IPV4]: Fix secondary IP addresses after promotion")
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
Signed-off-by: Julian Anastasov <ja(a)ssi.bg>
Signed-off-by: David S. Miller <davem(a)davemloft.net>
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
Conflicts:
net/ipv4/devinet.c
---
net/ipv4/devinet.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 12a2cea9d606..64f0fa0be370 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -341,12 +341,13 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
{
struct in_ifaddr *promote = NULL;
struct in_ifaddr *ifa, *ifa1 = *ifap;
- struct in_ifaddr *last_prim = in_dev->ifa_list;
+ struct in_ifaddr **last_prim;
struct in_ifaddr *prev_prom = NULL;
int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
ASSERT_RTNL();
+ last_prim = ifap;
if (in_dev->dead)
goto no_promotions;
@@ -360,7 +361,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
while ((ifa = *ifap1) != NULL) {
if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
ifa1->ifa_scope <= ifa->ifa_scope)
- last_prim = ifa;
+ last_prim = &ifa->ifa_next;
if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
ifa1->ifa_mask != ifa->ifa_mask ||
@@ -420,8 +421,8 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
if (prev_prom) {
prev_prom->ifa_next = promote->ifa_next;
- promote->ifa_next = last_prim->ifa_next;
- last_prim->ifa_next = promote;
+ promote->ifa_next = *last_prim;
+ *last_prim = promote;
}
promote->ifa_flags &= ~IFA_F_SECONDARY;
--
2.34.1
2
1

08 Oct '23
From: Christopher Bednarz <christopher.n.bednarz(a)intel.com>
mainline inclusion
from mainline-v6.6-rc1
commit bb6d73d9add68ad270888db327514384dfa44958
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I7YFVN
CVE: CVE-2023-25775
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
---------------------------
Currently irdma allows zero-length STAGs to be programmed in HW during
the kernel mode fast register flow. Zero-length MR or STAG registration
disable HW memory length checks.
Improve gaps in bounds checking in irdma by preventing zero-length STAG or
MR registrations except if the IB_PD_UNSAFE_GLOBAL_RKEY is set.
This addresses the disclosure CVE-2023-25775.
Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs")
Signed-off-by: Christopher Bednarz <christopher.n.bednarz(a)intel.com>
Signed-off-by: Shiraz Saleem <shiraz.saleem(a)intel.com>
Link: https://lore.kernel.org/r/20230818144838.1758-1-shiraz.saleem@intel.com
Signed-off-by: Leon Romanovsky <leon(a)kernel.org>
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
Conflicts:
drivers/infiniband/hw/i40iw/i40iw_ctrl.c
drivers/infiniband/hw/i40iw/i40iw_type.h
drivers/infiniband/hw/i40iw/i40iw_verbs.c
drivers/infiniband/hw/irdma/ctrl.c
drivers/infiniband/hw/irdma/type.h
drivers/infiniband/hw/irdma/verbs.c
---
drivers/infiniband/hw/i40iw/i40iw_ctrl.c | 6 ++++++
drivers/infiniband/hw/i40iw/i40iw_type.h | 2 ++
drivers/infiniband/hw/i40iw/i40iw_verbs.c | 10 ++++++++--
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
index 86d3f8aff329..2b18bb36e4e3 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
@@ -3033,6 +3033,9 @@ static enum i40iw_status_code i40iw_sc_alloc_stag(
u64 header;
enum i40iw_page_size page_size;
+ if (!info->total_len && !info->all_memory)
+ return -EINVAL;
+
page_size = (info->page_size == 0x200000) ? I40IW_PAGE_SIZE_2M : I40IW_PAGE_SIZE_4K;
cqp = dev->cqp;
wqe = i40iw_sc_cqp_get_next_send_wqe(cqp, scratch);
@@ -3091,6 +3094,9 @@ static enum i40iw_status_code i40iw_sc_mr_reg_non_shared(
u8 addr_type;
enum i40iw_page_size page_size;
+ if (!info->total_len && !info->all_memory)
+ return -EINVAL;
+
page_size = (info->page_size == 0x200000) ? I40IW_PAGE_SIZE_2M : I40IW_PAGE_SIZE_4K;
if (info->access_rights & (I40IW_ACCESS_FLAGS_REMOTEREAD_ONLY |
I40IW_ACCESS_FLAGS_REMOTEWRITE_ONLY))
diff --git a/drivers/infiniband/hw/i40iw/i40iw_type.h b/drivers/infiniband/hw/i40iw/i40iw_type.h
index c3babf3cbb8e..341aa6b1b6c1 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_type.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_type.h
@@ -786,6 +786,7 @@ struct i40iw_allocate_stag_info {
bool use_hmc_fcn_index;
u8 hmc_fcn_index;
bool use_pf_rid;
+ bool all_memory;
};
struct i40iw_reg_ns_stag_info {
@@ -804,6 +805,7 @@ struct i40iw_reg_ns_stag_info {
bool use_hmc_fcn_index;
u8 hmc_fcn_index;
bool use_pf_rid;
+ bool all_memory;
};
struct i40iw_fast_reg_stag_info {
diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
index 533f3caecb7a..89654dc91d81 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
@@ -1494,7 +1494,8 @@ static int i40iw_handle_q_mem(struct i40iw_device *iwdev,
static int i40iw_hw_alloc_stag(struct i40iw_device *iwdev, struct i40iw_mr *iwmr)
{
struct i40iw_allocate_stag_info *info;
- struct i40iw_pd *iwpd = to_iwpd(iwmr->ibmr.pd);
+ struct ib_pd *pd = iwmr->ibmr.pd;
+ struct i40iw_pd *iwpd = to_iwpd(pd);
enum i40iw_status_code status;
int err = 0;
struct i40iw_cqp_request *cqp_request;
@@ -1511,6 +1512,7 @@ static int i40iw_hw_alloc_stag(struct i40iw_device *iwdev, struct i40iw_mr *iwmr
info->stag_idx = iwmr->stag >> I40IW_CQPSQ_STAG_IDX_SHIFT;
info->pd_id = iwpd->sc_pd.pd_id;
info->total_len = iwmr->length;
+ info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY;
info->remote_access = true;
cqp_info->cqp_cmd = OP_ALLOC_STAG;
cqp_info->post_sq = 1;
@@ -1563,6 +1565,8 @@ static struct ib_mr *i40iw_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type,
iwmr->type = IW_MEMREG_TYPE_MEM;
palloc = &iwpbl->pble_alloc;
iwmr->page_cnt = max_num_sg;
+ /* Use system PAGE_SIZE as the sg page sizes are unknown at this point */
+ iwmr->length = max_num_sg * PAGE_SIZE;
mutex_lock(&iwdev->pbl_mutex);
status = i40iw_get_pble(&iwdev->sc_dev, iwdev->pble_rsrc, palloc, iwmr->page_cnt);
mutex_unlock(&iwdev->pbl_mutex);
@@ -1659,7 +1663,8 @@ static int i40iw_hwreg_mr(struct i40iw_device *iwdev,
{
struct i40iw_pbl *iwpbl = &iwmr->iwpbl;
struct i40iw_reg_ns_stag_info *stag_info;
- struct i40iw_pd *iwpd = to_iwpd(iwmr->ibmr.pd);
+ struct ib_pd *pd = iwmr->ibmr.pd;
+ struct i40iw_pd *iwpd = to_iwpd(pd);
struct i40iw_pble_alloc *palloc = &iwpbl->pble_alloc;
enum i40iw_status_code status;
int err = 0;
@@ -1679,6 +1684,7 @@ static int i40iw_hwreg_mr(struct i40iw_device *iwdev,
stag_info->total_len = iwmr->length;
stag_info->access_rights = access;
stag_info->pd_id = iwpd->sc_pd.pd_id;
+ stag_info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY;
stag_info->addr_type = I40IW_ADDR_TYPE_VA_BASED;
stag_info->page_size = iwmr->page_size;
--
2.34.1
2
1

[PATCH openEuler-1.0-LTS v2] RDMA/irdma: Prevent zero-length STAG registration
by Liu Jian 08 Oct '23
by Liu Jian 08 Oct '23
08 Oct '23
From: Christopher Bednarz <christopher.n.bednarz(a)intel.com>
mainline inclusion
from mainline-v6.6-rc1
commit bb6d73d9add68ad270888db327514384dfa44958
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I7YFVN
CVE: CVE-2023-25775
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
---------------------------
Currently irdma allows zero-length STAGs to be programmed in HW during
the kernel mode fast register flow. Zero-length MR or STAG registration
disable HW memory length checks.
Improve gaps in bounds checking in irdma by preventing zero-length STAG or
MR registrations except if the IB_PD_UNSAFE_GLOBAL_RKEY is set.
This addresses the disclosure CVE-2023-25775.
Fixes: b48c24c2d710 ("RDMA/irdma: Implement device supported verb APIs")
Signed-off-by: Christopher Bednarz <christopher.n.bednarz(a)intel.com>
Signed-off-by: Shiraz Saleem <shiraz.saleem(a)intel.com>
Link: https://lore.kernel.org/r/20230818144838.1758-1-shiraz.saleem@intel.com
Signed-off-by: Leon Romanovsky <leon(a)kernel.org>
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
Conflicts:
drivers/infiniband/hw/i40iw/i40iw_ctrl.c
drivers/infiniband/hw/i40iw/i40iw_type.h
drivers/infiniband/hw/i40iw/i40iw_verbs.c
drivers/infiniband/hw/irdma/ctrl.c
drivers/infiniband/hw/irdma/type.h
drivers/infiniband/hw/irdma/verbs.c
---
drivers/infiniband/hw/i40iw/i40iw_ctrl.c | 6 ++++++
drivers/infiniband/hw/i40iw/i40iw_type.h | 2 ++
drivers/infiniband/hw/i40iw/i40iw_verbs.c | 10 ++++++++--
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
index 4d841a3c68f3..026557aa2307 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c
@@ -2945,6 +2945,9 @@ static enum i40iw_status_code i40iw_sc_alloc_stag(
u64 header;
enum i40iw_page_size page_size;
+ if (!info->total_len && !info->all_memory)
+ return -EINVAL;
+
page_size = (info->page_size == 0x200000) ? I40IW_PAGE_SIZE_2M : I40IW_PAGE_SIZE_4K;
cqp = dev->cqp;
wqe = i40iw_sc_cqp_get_next_send_wqe(cqp, scratch);
@@ -3003,6 +3006,9 @@ static enum i40iw_status_code i40iw_sc_mr_reg_non_shared(
u8 addr_type;
enum i40iw_page_size page_size;
+ if (!info->total_len && !info->all_memory)
+ return -EINVAL;
+
page_size = (info->page_size == 0x200000) ? I40IW_PAGE_SIZE_2M : I40IW_PAGE_SIZE_4K;
if (info->access_rights & (I40IW_ACCESS_FLAGS_REMOTEREAD_ONLY |
I40IW_ACCESS_FLAGS_REMOTEWRITE_ONLY))
diff --git a/drivers/infiniband/hw/i40iw/i40iw_type.h b/drivers/infiniband/hw/i40iw/i40iw_type.h
index adc8d2ec523d..5c4e2f206105 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_type.h
+++ b/drivers/infiniband/hw/i40iw/i40iw_type.h
@@ -779,6 +779,7 @@ struct i40iw_allocate_stag_info {
bool use_hmc_fcn_index;
u8 hmc_fcn_index;
bool use_pf_rid;
+ bool all_memory;
};
struct i40iw_reg_ns_stag_info {
@@ -797,6 +798,7 @@ struct i40iw_reg_ns_stag_info {
bool use_hmc_fcn_index;
u8 hmc_fcn_index;
bool use_pf_rid;
+ bool all_memory;
};
struct i40iw_fast_reg_stag_info {
diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
index a5e3349b8a7c..9cf8bf2c87e7 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
@@ -1603,7 +1603,8 @@ static int i40iw_handle_q_mem(struct i40iw_device *iwdev,
static int i40iw_hw_alloc_stag(struct i40iw_device *iwdev, struct i40iw_mr *iwmr)
{
struct i40iw_allocate_stag_info *info;
- struct i40iw_pd *iwpd = to_iwpd(iwmr->ibmr.pd);
+ struct ib_pd *pd = iwmr->ibmr.pd;
+ struct i40iw_pd *iwpd = to_iwpd(pd);
enum i40iw_status_code status;
int err = 0;
struct i40iw_cqp_request *cqp_request;
@@ -1620,6 +1621,7 @@ static int i40iw_hw_alloc_stag(struct i40iw_device *iwdev, struct i40iw_mr *iwmr
info->stag_idx = iwmr->stag >> I40IW_CQPSQ_STAG_IDX_SHIFT;
info->pd_id = iwpd->sc_pd.pd_id;
info->total_len = iwmr->length;
+ info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY;
info->remote_access = true;
cqp_info->cqp_cmd = OP_ALLOC_STAG;
cqp_info->post_sq = 1;
@@ -1673,6 +1675,8 @@ static struct ib_mr *i40iw_alloc_mr(struct ib_pd *pd,
iwmr->type = IW_MEMREG_TYPE_MEM;
palloc = &iwpbl->pble_alloc;
iwmr->page_cnt = max_num_sg;
+ /* Use system PAGE_SIZE as the sg page sizes are unknown at this point */
+ iwmr->length = max_num_sg * PAGE_SIZE;
mutex_lock(&iwdev->pbl_mutex);
status = i40iw_get_pble(&iwdev->sc_dev, iwdev->pble_rsrc, palloc, iwmr->page_cnt);
mutex_unlock(&iwdev->pbl_mutex);
@@ -1769,7 +1773,8 @@ static int i40iw_hwreg_mr(struct i40iw_device *iwdev,
{
struct i40iw_pbl *iwpbl = &iwmr->iwpbl;
struct i40iw_reg_ns_stag_info *stag_info;
- struct i40iw_pd *iwpd = to_iwpd(iwmr->ibmr.pd);
+ struct ib_pd *pd = iwmr->ibmr.pd;
+ struct i40iw_pd *iwpd = to_iwpd(pd);
struct i40iw_pble_alloc *palloc = &iwpbl->pble_alloc;
enum i40iw_status_code status;
int err = 0;
@@ -1789,6 +1794,7 @@ static int i40iw_hwreg_mr(struct i40iw_device *iwdev,
stag_info->total_len = iwmr->length;
stag_info->access_rights = access;
stag_info->pd_id = iwpd->sc_pd.pd_id;
+ stag_info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY;
stag_info->addr_type = I40IW_ADDR_TYPE_VA_BASED;
stag_info->page_size = iwmr->page_size;
--
2.34.1
2
1

08 Oct '23
Backport lts commit f916e5988ae4 ("bonding: fix macvlan over
alb bond support").
Hangbin Liu (1):
bonding: fix macvlan over alb bond support
Jakub Kicinski (1):
net: remove bond_slave_has_mac_rcu()
drivers/net/bonding/bond_alb.c | 6 +++---
include/net/bonding.h | 25 +------------------------
2 files changed, 4 insertions(+), 27 deletions(-)
--
2.25.1
2
3

[PATCH openEuler-1.0-LTS 0/2] PCI: acpiphp: linux-4.19.y bugfixes backport
by Jialin Zhang 08 Oct '23
by Jialin Zhang 08 Oct '23
08 Oct '23
Igor Mammedov (2):
PCI: acpiphp: Reassign resources on bridge if necessary
PCI: acpiphp: Use pci_assign_unassigned_bridge_resources() only for
non-root bus
drivers/pci/hotplug/acpiphp_glue.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
--
2.25.1
2
3
Baokun Li (1):
xfs: propagate the return value of xfs_log_force() to avoid soft
lockup
Colin Ian King (2):
xfs: remove redundant initializations of pointers drop_leaf and
save_leaf
xfs: remove redundant pointer lip
Darrick J. Wong (9):
xfs: use setattr_copy to set vfs inode attributes
xfs: remove kmem_zone typedef
xfs: rename _zone variables to _cache
xfs: compact deferred intent item structures
xfs: create slab caches for frequently-used deferred items
xfs: rename xfs_bmap_add_free to xfs_free_extent_later
xfs: reduce the size of struct xfs_extent_free_item
xfs: remove unused parameter from refcount code
xfs: pass xfs_extent_free_item directly through the log intent code
Dave Chinner (19):
xfs: don't assert fail on perag references on teardown
xfs: set prealloc flag in xfs_alloc_file_space()
xfs: validity check agbnos on the AGFL
xfs: validate block number being freed before adding to xefi
xfs: don't reverse order of items in bulk AIL insertion
xfs: use deferred frees for btree block freeing
xfs: pass alloc flags through to xfs_extent_busy_flush()
xfs: allow extent free intents to be retried
xfs: don't block in busy flushing when freeing extents
xfs: journal geometry is not properly bounds checked
xfs: AGF length has never been bounds checked
xfs: fix bounds check in xfs_defer_agfl_block()
xfs: block reservation too large for minleft allocation
xfs: punching delalloc extents on write failure is racy
xfs: use byte ranges for write cleanup ranges
xfs,iomap: move delalloc punching to iomap
iomap: buffered write failure should not truncate the page cache
xfs: xfs_bmap_punch_delalloc_range() should take a byte range
xfs: fix off-by-one-block in xfs_discard_folio()
Gaosheng Cui (1):
xfs: remove xfs_setattr_time() declaration
Guo Xuenan (1):
xfs: set minleft correctly for randomly sparse inode allocations
Jiapeng Chong (1):
xfs: Remove redundant assignment to busy
Long Li (6):
xfs: fix dir3 block read verify fail during log recover
Revert "xfs: propagate the return value of xfs_log_force() to avoid
soft lockup"
xfs: xfs_trans_cancel() path must check for log shutdown
xfs: don't verify agf length when log recovery
xfs: shutdown to ensure submits buffers on LSN boundaries
xfs: update the last_sync_lsn with ctx start lsn
yangerkun (4):
xfs: keep growfs sb log item active until ail flush success
xfs: fix xfs shutdown since we reserve more blocks in agfl fixup
xfs: longest free extent no need consider postalloc
xfs: shutdown xfs once inode double free
fs/xfs/kmem.h | 4 -
fs/xfs/libxfs/xfs_alloc.c | 390 +++++++++++++++++++++--------
fs/xfs/libxfs/xfs_alloc.h | 51 +++-
fs/xfs/libxfs/xfs_alloc_btree.c | 2 +-
fs/xfs/libxfs/xfs_attr_leaf.c | 2 -
fs/xfs/libxfs/xfs_bmap.c | 90 +++----
fs/xfs/libxfs/xfs_bmap.h | 37 +--
fs/xfs/libxfs/xfs_bmap_btree.c | 27 +-
fs/xfs/libxfs/xfs_btree.c | 4 +-
fs/xfs/libxfs/xfs_btree.h | 2 +-
fs/xfs/libxfs/xfs_da_btree.c | 6 +-
fs/xfs/libxfs/xfs_da_btree.h | 3 +-
fs/xfs/libxfs/xfs_defer.c | 70 +++++-
fs/xfs/libxfs/xfs_defer.h | 3 +
fs/xfs/libxfs/xfs_ialloc.c | 32 ++-
fs/xfs/libxfs/xfs_ialloc_btree.c | 8 +-
fs/xfs/libxfs/xfs_inode_fork.c | 4 +-
fs/xfs/libxfs/xfs_inode_fork.h | 2 +-
fs/xfs/libxfs/xfs_refcount.c | 56 +++--
fs/xfs/libxfs/xfs_refcount.h | 7 +-
fs/xfs/libxfs/xfs_refcount_btree.c | 11 +-
fs/xfs/libxfs/xfs_rmap.c | 21 +-
fs/xfs/libxfs/xfs_rmap.h | 7 +-
fs/xfs/libxfs/xfs_rmap_btree.c | 2 +-
fs/xfs/libxfs/xfs_sb.c | 56 ++++-
fs/xfs/libxfs/xfs_types.c | 23 ++
fs/xfs/libxfs/xfs_types.h | 2 +
fs/xfs/xfs_aops.c | 32 +--
fs/xfs/xfs_bmap_item.c | 16 +-
fs/xfs/xfs_bmap_item.h | 6 +-
fs/xfs/xfs_bmap_util.c | 19 +-
fs/xfs/xfs_bmap_util.h | 2 +-
fs/xfs/xfs_buf.c | 16 +-
fs/xfs/xfs_buf_item.c | 10 +-
fs/xfs/xfs_buf_item.h | 11 +-
fs/xfs/xfs_buf_item_recover.c | 9 +-
fs/xfs/xfs_dquot.c | 26 +-
fs/xfs/xfs_extent_busy.c | 36 ++-
fs/xfs/xfs_extent_busy.h | 6 +-
fs/xfs/xfs_extfree_item.c | 137 +++++++---
fs/xfs/xfs_extfree_item.h | 6 +-
fs/xfs/xfs_file.c | 8 -
fs/xfs/xfs_icache.c | 8 +-
fs/xfs/xfs_icreate_item.c | 6 +-
fs/xfs/xfs_icreate_item.h | 2 +-
fs/xfs/xfs_inode.c | 2 +-
fs/xfs/xfs_inode.h | 2 +-
fs/xfs/xfs_inode_item.c | 6 +-
fs/xfs/xfs_inode_item.h | 2 +-
fs/xfs/xfs_iomap.c | 292 ++++++++++++++++++---
fs/xfs/xfs_iops.c | 56 +----
fs/xfs/xfs_iops.h | 1 -
fs/xfs/xfs_log.c | 72 +++---
fs/xfs/xfs_log_priv.h | 2 +-
fs/xfs/xfs_log_recover.c | 6 +-
fs/xfs/xfs_mount.c | 12 +-
fs/xfs/xfs_mru_cache.c | 2 +-
fs/xfs/xfs_pnfs.c | 3 +-
fs/xfs/xfs_qm.h | 2 +-
fs/xfs/xfs_refcount_item.c | 16 +-
fs/xfs/xfs_refcount_item.h | 6 +-
fs/xfs/xfs_reflink.c | 7 +-
fs/xfs/xfs_rmap_item.c | 16 +-
fs/xfs/xfs_rmap_item.h | 6 +-
fs/xfs/xfs_super.c | 233 ++++++++---------
fs/xfs/xfs_trans.c | 24 +-
fs/xfs/xfs_trans.h | 2 +-
fs/xfs/xfs_trans_ail.c | 5 +-
fs/xfs/xfs_trans_dquot.c | 4 +-
mm/filemap.c | 1 +
70 files changed, 1358 insertions(+), 700 deletions(-)
--
2.31.1
2
45

[PATCH openEuler-1.0-LTS] mm: memory-failure: use rcu lock instead of tasklist_lock when collect_procs()
by Tong Tiangen 08 Oct '23
by Tong Tiangen 08 Oct '23
08 Oct '23
mainline inclusion
from mainline-v6.6-rc1
commit d256d1cd8da1cbc4615de69df71c87ce623fec2f
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I85WL9
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
--------------------------------
We found a softlock issue in our test, analyzed the logs, and found that
the relevant CPU call trace as follows:
CPU0:
_do_fork
-> copy_process()
-> write_lock_irq(&tasklist_lock) //Disable irq,waiting for
//tasklist_lock
CPU1:
wp_page_copy()
->pte_offset_map_lock()
-> spin_lock(&page->ptl); //Hold page->ptl
-> ptep_clear_flush()
-> flush_tlb_others() ...
-> smp_call_function_many()
-> arch_send_call_function_ipi_mask()
-> csd_lock_wait() //Waiting for other CPUs respond
//IPI
CPU2:
collect_procs_anon()
-> read_lock(&tasklist_lock) //Hold tasklist_lock
->for_each_process(tsk)
-> page_mapped_in_vma()
-> page_vma_mapped_walk()
-> map_pte()
->spin_lock(&page->ptl) //Waiting for page->ptl
We can see that CPU1 waiting for CPU0 respond IPI,CPU0 waiting for CPU2
unlock tasklist_lock, CPU2 waiting for CPU1 unlock page->ptl. As a result,
softlockup is triggered.
For collect_procs_anon(), what we're doing is task list iteration, during
the iteration, with the help of call_rcu(), the task_struct object is freed
only after one or more grace periods elapse. the logic as follows:
release_task()
-> __exit_signal()
-> __unhash_process()
-> list_del_rcu()
-> put_task_struct_rcu_user()
-> call_rcu(&task->rcu, delayed_put_task_struct)
delayed_put_task_struct()
-> put_task_struct()
-> if (refcount_sub_and_test())
__put_task_struct()
-> free_task()
Therefore, under the protection of the rcu lock, we can safely use
get_task_struct() to ensure a safe reference to task_struct during the
iteration.
By removing the use of tasklist_lock in task list iteration, we can break
the softlock chain above.
The same logic can also be applied to:
- collect_procs_file()
- collect_procs_fsdax()
- collect_procs_ksm()
Link: https://lkml.kernel.org/r/20230828022527.241693-1-tongtiangen@huawei.com
Signed-off-by: Tong Tiangen <tongtiangen(a)huawei.com>
Acked-by: Naoya Horiguchi <naoya.horiguchi(a)nec.com>
Cc: Kefeng Wang <wangkefeng.wang(a)huawei.com>
Cc: Matthew Wilcox (Oracle) <willy(a)infradead.org>
Cc: Miaohe Lin <linmiaohe(a)huawei.com>
Cc: Paul E. McKenney <paulmck(a)kernel.org>
Signed-off-by: Andrew Morton <akpm(a)linux-foundation.org>
Conflicts:
mm/filemap.c
mm/ksm.c
mm/memory-failure.c
Signed-off-by: Tong Tiangen <tongtiangen(a)huawei.com>
---
mm/filemap.c | 3 ---
mm/memory-failure.c | 12 ++++++------
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/mm/filemap.c b/mm/filemap.c
index f33504b784a2..5bf4645256cb 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -166,9 +166,6 @@ static void page_cache_kill(struct page *page)
* bdi.wb->list_lock (zap_pte_range->set_page_dirty)
* ->inode->i_lock (zap_pte_range->set_page_dirty)
* ->private_lock (zap_pte_range->__set_page_dirty_buffers)
- *
- * ->i_mmap_rwsem
- * ->tasklist_lock (memory_failure, collect_procs_ao)
*/
static int page_cache_tree_insert(struct address_space *mapping,
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index fff715a27253..8924d7d9bffa 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -427,8 +427,8 @@ static void kill_procs(struct list_head *to_kill, int forcekill, bool fail,
* on behalf of the thread group. Return task_struct of the (first found)
* dedicated thread if found, and return NULL otherwise.
*
- * We already hold read_lock(&tasklist_lock) in the caller, so we don't
- * have to call rcu_read_lock/unlock() in this function.
+ * We already hold rcu lock in the caller, so we don't have to call
+ * rcu_read_lock/unlock() in this function.
*/
static struct task_struct *find_early_kill_thread(struct task_struct *tsk)
{
@@ -478,7 +478,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
return;
pgoff = page_to_pgoff(page);
- read_lock(&tasklist_lock);
+ rcu_read_lock();
for_each_process (tsk) {
struct anon_vma_chain *vmac;
struct task_struct *t = task_early_kill(tsk, force_early);
@@ -494,7 +494,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill,
add_to_kill(t, page, vma, to_kill, tkc);
}
}
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
page_unlock_anon_vma_read(av);
}
@@ -509,7 +509,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
struct address_space *mapping = page->mapping;
i_mmap_lock_read(mapping);
- read_lock(&tasklist_lock);
+ rcu_read_lock();
for_each_process(tsk) {
pgoff_t pgoff = page_to_pgoff(page);
struct task_struct *t = task_early_kill(tsk, force_early);
@@ -529,7 +529,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill,
add_to_kill(t, page, vma, to_kill, tkc);
}
}
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
i_mmap_unlock_read(mapping);
}
--
2.25.1
2
1

[PATCH openEuler-1.0-LTS v2] x86/topology: Fix erroneous smp_num_siblings on Intel Hybrid platforms
by Yu Liao 08 Oct '23
by Yu Liao 08 Oct '23
08 Oct '23
From: Zhang Rui <rui.zhang(a)intel.com>
stable inclusion
from stable-v4.19.293
commit f5867e5b6a780710251318cb2047e24c49935e43
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I85XNK
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
--------------------------------
[ Upstream commit edc0a2b5957652f4685ef3516f519f84807087db ]
Traditionally, all CPUs in a system have identical numbers of SMT
siblings. That changes with hybrid processors where some logical CPUs
have a sibling and others have none.
Today, the CPU boot code sets the global variable smp_num_siblings when
every CPU thread is brought up. The last thread to boot will overwrite
it with the number of siblings of *that* thread. That last thread to
boot will "win". If the thread is a Pcore, smp_num_siblings == 2. If it
is an Ecore, smp_num_siblings == 1.
smp_num_siblings describes if the *system* supports SMT. It should
specify the maximum number of SMT threads among all cores.
Ensure that smp_num_siblings represents the system-wide maximum number
of siblings by always increasing its value. Never allow it to decrease.
On MeteorLake-P platform, this fixes a problem that the Ecore CPUs are
not updated in any cpu sibling map because the system is treated as an
UP system when probing Ecore CPUs.
Below shows part of the CPU topology information before and after the
fix, for both Pcore and Ecore CPU (cpu0 is Pcore, cpu 12 is Ecore).
...
-/sys/devices/system/cpu/cpu0/topology/package_cpus:000fff
-/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-11
+/sys/devices/system/cpu/cpu0/topology/package_cpus:3fffff
+/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-21
...
-/sys/devices/system/cpu/cpu12/topology/package_cpus:001000
-/sys/devices/system/cpu/cpu12/topology/package_cpus_list:12
+/sys/devices/system/cpu/cpu12/topology/package_cpus:3fffff
+/sys/devices/system/cpu/cpu12/topology/package_cpus_list:0-21
Notice that the "before" 'package_cpus_list' has only one CPU. This
means that userspace tools like lscpu will see a little laptop like
an 11-socket system:
-Core(s) per socket: 1
-Socket(s): 11
+Core(s) per socket: 16
+Socket(s): 1
This is also expected to make the scheduler do rather wonky things
too.
[ dhansen: remove CPUID detail from changelog, add end user effects ]
CC: stable(a)kernel.org
Fixes: bbb65d2d365e ("x86: use cpuid vector 0xb when available for detecting cpu topology")
Fixes: 95f3d39ccf7a ("x86/cpu/topology: Provide detect_extended_topology_early()")
Suggested-by: Len Brown <len.brown(a)intel.com>
Signed-off-by: Zhang Rui <rui.zhang(a)intel.com>
Signed-off-by: Dave Hansen <dave.hansen(a)linux.intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz(a)infradead.org>
Link: https://lore.kernel.org/all/20230323015640.27906-1-rui.zhang%40intel.com
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Conflicts:
arch/x86/kernel/cpu/topology.c
Signed-off-by: Yu Liao <liaoyu15(a)huawei.com>
---
arch/x86/kernel/cpu/topology.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c
index 7c130bdfd746..5c15e1329fcb 100644
--- a/arch/x86/kernel/cpu/topology.c
+++ b/arch/x86/kernel/cpu/topology.c
@@ -77,7 +77,7 @@ int detect_extended_topology_early(struct cpuinfo_x86 *c)
* initial apic id, which also represents 32-bit extended x2apic id.
*/
c->initial_apicid = edx;
- smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
#endif
return 0;
}
@@ -106,7 +106,8 @@ int detect_extended_topology(struct cpuinfo_x86 *c)
*/
cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
c->initial_apicid = edx;
- core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
die_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
--
2.25.1
2
1

[PATCH openEuler-1.0-LTS] x86/topology: Fix erroneous smp_num_siblings on Intel Hybrid platforms
by Yu Liao 07 Oct '23
by Yu Liao 07 Oct '23
07 Oct '23
From: Zhang Rui <rui.zhang(a)intel.com>
stable inclusion
from stable-v4.19.293
commit f5867e5b6a780710251318cb2047e24c49935e43
category: bugfix
bugzilla: 188350
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
--------------------------------
[ Upstream commit edc0a2b5957652f4685ef3516f519f84807087db ]
Traditionally, all CPUs in a system have identical numbers of SMT
siblings. That changes with hybrid processors where some logical CPUs
have a sibling and others have none.
Today, the CPU boot code sets the global variable smp_num_siblings when
every CPU thread is brought up. The last thread to boot will overwrite
it with the number of siblings of *that* thread. That last thread to
boot will "win". If the thread is a Pcore, smp_num_siblings == 2. If it
is an Ecore, smp_num_siblings == 1.
smp_num_siblings describes if the *system* supports SMT. It should
specify the maximum number of SMT threads among all cores.
Ensure that smp_num_siblings represents the system-wide maximum number
of siblings by always increasing its value. Never allow it to decrease.
On MeteorLake-P platform, this fixes a problem that the Ecore CPUs are
not updated in any cpu sibling map because the system is treated as an
UP system when probing Ecore CPUs.
Below shows part of the CPU topology information before and after the
fix, for both Pcore and Ecore CPU (cpu0 is Pcore, cpu 12 is Ecore).
...
-/sys/devices/system/cpu/cpu0/topology/package_cpus:000fff
-/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-11
+/sys/devices/system/cpu/cpu0/topology/package_cpus:3fffff
+/sys/devices/system/cpu/cpu0/topology/package_cpus_list:0-21
...
-/sys/devices/system/cpu/cpu12/topology/package_cpus:001000
-/sys/devices/system/cpu/cpu12/topology/package_cpus_list:12
+/sys/devices/system/cpu/cpu12/topology/package_cpus:3fffff
+/sys/devices/system/cpu/cpu12/topology/package_cpus_list:0-21
Notice that the "before" 'package_cpus_list' has only one CPU. This
means that userspace tools like lscpu will see a little laptop like
an 11-socket system:
-Core(s) per socket: 1
-Socket(s): 11
+Core(s) per socket: 16
+Socket(s): 1
This is also expected to make the scheduler do rather wonky things
too.
[ dhansen: remove CPUID detail from changelog, add end user effects ]
CC: stable(a)kernel.org
Fixes: bbb65d2d365e ("x86: use cpuid vector 0xb when available for detecting cpu topology")
Fixes: 95f3d39ccf7a ("x86/cpu/topology: Provide detect_extended_topology_early()")
Suggested-by: Len Brown <len.brown(a)intel.com>
Signed-off-by: Zhang Rui <rui.zhang(a)intel.com>
Signed-off-by: Dave Hansen <dave.hansen(a)linux.intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz(a)infradead.org>
Link: https://lore.kernel.org/all/20230323015640.27906-1-rui.zhang%40intel.com
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Conflicts:
arch/x86/kernel/cpu/topology.c
Signed-off-by: Yu Liao <liaoyu15(a)huawei.com>
---
arch/x86/kernel/cpu/topology.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c
index 7c130bdfd746..5c15e1329fcb 100644
--- a/arch/x86/kernel/cpu/topology.c
+++ b/arch/x86/kernel/cpu/topology.c
@@ -77,7 +77,7 @@ int detect_extended_topology_early(struct cpuinfo_x86 *c)
* initial apic id, which also represents 32-bit extended x2apic id.
*/
c->initial_apicid = edx;
- smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
#endif
return 0;
}
@@ -106,7 +106,8 @@ int detect_extended_topology(struct cpuinfo_x86 *c)
*/
cpuid_count(leaf, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
c->initial_apicid = edx;
- core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ smp_num_siblings = max_t(int, smp_num_siblings, LEVEL_MAX_SIBLINGS(ebx));
core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
die_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
--
2.25.1
2
1
New architectural features and CPUID bits related to the
Speculative Return Stack Overflow (SRSO) vulnerability.
Arnaldo Carvalho de Melo (2):
tools headers cpufeatures: Sync with the kernel sources
tools arch x86: Sync the msr-index.h copy with the kernel sources
Borislav Petkov (AMD) (9):
x86/bugs: Increase the x86 bugs vector size to two u32s
x86/srso: Add a Speculative RAS Overflow mitigation
x86/srso: Add IBPB_BRTYPE support
x86/srso: Add SRSO_NO support
x86/srso: Add IBPB
x86/srso: Add IBPB on VMEXIT
x86/srso: Tie SBPB bit setting to microcode patch detection
x86/srso: Disable the mitigation on unaffected configurations
x86/srso: Correct the mitigation status when SMT is disabled
Jialin Zhang (1):
x86/cpufeatures: Fix abi breakage caused by NCAPINTS in cpufeature
header file.
Josh Poimboeuf (2):
x86/srso: Fix return thunks in generated code
objtool: Add frame-pointer-specific function ignore
Kim Phillips (1):
x86/cpu, kvm: Add support for CPUID_80000021_EAX
Nick Desaulniers (1):
x86/srso: Fix build breakage with the LLVM linker
Nikolay Borisov (1):
kabi: Allow extra bugsints (bsc#1213927).
Peter Zijlstra (10):
x86/ibt: Add ANNOTATE_NOENDBR
x86/cpu: Fix __x86_return_thunk symbol type
x86/cpu: Fix up srso_safe_ret() and __x86_return_thunk()
x86/alternative: Make custom return thunk unconditional
x86/cpu: Clean up SRSO return thunk mess
x86/cpu: Rename original retbleed methods
x86/cpu: Rename srso_(.*)_alias to srso_alias_\1
x86/cpu: Cleanup the untrain mess
objtool/x86: Fixup frame-pointer vs rethunk
objtool/x86: Fix SRSO mess
Sean Christopherson (1):
x86/retpoline: Don't clobber RFLAGS during srso_safe_ret()
Documentation/admin-guide/hw-vuln/index.rst | 1 +
Documentation/admin-guide/hw-vuln/srso.rst | 133 ++++++++++++
.../admin-guide/kernel-parameters.txt | 11 +
arch/x86/Kconfig | 7 +
arch/x86/include/asm/cpufeature.h | 23 +-
arch/x86/include/asm/cpufeatures.h | 15 +-
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/include/asm/nospec-branch.h | 34 ++-
arch/x86/include/asm/processor.h | 7 +-
arch/x86/kernel/alternative.c | 2 +-
arch/x86/kernel/cpu/amd.c | 19 ++
arch/x86/kernel/cpu/bugs.c | 197 ++++++++++++++++++
arch/x86/kernel/cpu/common.c | 17 +-
arch/x86/kernel/cpu/mkcapflags.sh | 2 +-
arch/x86/kernel/cpu/proc.c | 2 +-
arch/x86/kernel/cpu/scattered.c | 3 +
arch/x86/kernel/vmlinux.lds.S | 38 +++-
arch/x86/kvm/cpuid.c | 3 +
arch/x86/kvm/svm/svm.c | 4 +-
arch/x86/kvm/svm/vmenter.S | 3 +
arch/x86/lib/retpoline.S | 135 ++++++++++--
drivers/base/cpu.c | 8 +
include/linux/cpu.h | 2 +
include/linux/objtool.h | 28 +++
tools/arch/x86/include/asm/cpufeatures.h | 16 +-
tools/arch/x86/include/asm/msr-index.h | 1 +
tools/include/linux/objtool.h | 28 +++
tools/objtool/arch.h | 1 +
tools/objtool/arch/x86/decode.c | 6 +
tools/objtool/check.c | 41 +++-
tools/objtool/elf.h | 1 +
.../perf/trace/beauty/tracepoints/x86_msr.sh | 2 +-
32 files changed, 740 insertions(+), 51 deletions(-)
create mode 100644 Documentation/admin-guide/hw-vuln/srso.rst
--
2.25.1
3
30
From: Kyle Zeng <zengyhkyle(a)gmail.com>
mainline inclusion
from mainline-v6.6-rc3
commit 0113d9c9d1ccc07f5a3710dac4aa24b6d711278c
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I85DZB
CVE: CVE-2023-42754
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
--------------------------------
Currently, we assume the skb is associated with a device before calling
__ip_options_compile, which is not always the case if it is re-routed by
ipvs.
When skb->dev is NULL, dev_net(skb->dev) will become null-dereference.
This patch adds a check for the edge case and switch to use the net_device
from the rtable when skb->dev is NULL.
Fixes: ed0de45a1008 ("ipv4: recompile ip options in ipv4_link_failure")
Suggested-by: David Ahern <dsahern(a)kernel.org>
Signed-off-by: Kyle Zeng <zengyhkyle(a)gmail.com>
Cc: Stephen Suryaputra <ssuryaextr(a)gmail.com>
Cc: Vadim Fedorenko <vfedorenko(a)novek.ru>
Reviewed-by: David Ahern <dsahern(a)kernel.org>
Signed-off-by: David S. Miller <davem(a)davemloft.net>
Signed-off-by: Lu Wei <luwei32(a)huawei.com>
---
net/ipv4/route.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 68f67bf99ed7..86096e2e43b0 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1212,6 +1212,7 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
static void ipv4_send_dest_unreach(struct sk_buff *skb)
{
+ struct net_device *dev;
struct ip_options opt;
int res;
@@ -1229,7 +1230,8 @@ static void ipv4_send_dest_unreach(struct sk_buff *skb)
opt.optlen = ip_hdr(skb)->ihl * 4 - sizeof(struct iphdr);
rcu_read_lock();
- res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL);
+ dev = skb->dev ? skb->dev : skb_rtable(skb)->dst.dev;
+ res = __ip_options_compile(dev_net(dev), &opt, skb, NULL);
rcu_read_unlock();
if (res)
--
2.34.1
2
1
New architectural features and CPUID bits related to the
Speculative Return Stack Overflow (SRSO) vulnerability.
Arnaldo Carvalho de Melo (2):
tools headers cpufeatures: Sync with the kernel sources
tools arch x86: Sync the msr-index.h copy with the kernel sources
Borislav Petkov (AMD) (9):
x86/bugs: Increase the x86 bugs vector size to two u32s
x86/srso: Add a Speculative RAS Overflow mitigation
x86/srso: Add IBPB_BRTYPE support
x86/srso: Add SRSO_NO support
x86/srso: Add IBPB
x86/srso: Add IBPB on VMEXIT
x86/srso: Tie SBPB bit setting to microcode patch detection
x86/srso: Disable the mitigation on unaffected configurations
x86/srso: Correct the mitigation status when SMT is disabled
Jialin Zhang (1):
x86/cpufeatures: Fix abi breakage caused by NCAPINTS in cpufeature
header file.
Josh Poimboeuf (2):
x86/srso: Fix return thunks in generated code
objtool: Add frame-pointer-specific function ignore
Kim Phillips (1):
x86/cpu, kvm: Add support for CPUID_80000021_EAX
Nick Desaulniers (1):
x86/srso: Fix build breakage with the LLVM linker
Nikolay Borisov (1):
kabi: Allow extra bugsints (bsc#1213927).
Peter Zijlstra (10):
x86/ibt: Add ANNOTATE_NOENDBR
x86/cpu: Fix __x86_return_thunk symbol type
x86/cpu: Fix up srso_safe_ret() and __x86_return_thunk()
x86/alternative: Make custom return thunk unconditional
x86/cpu: Clean up SRSO return thunk mess
x86/cpu: Rename original retbleed methods
x86/cpu: Rename srso_(.*)_alias to srso_alias_\1
x86/cpu: Cleanup the untrain mess
objtool/x86: Fixup frame-pointer vs rethunk
objtool/x86: Fix SRSO mess
Sean Christopherson (1):
x86/retpoline: Don't clobber RFLAGS during srso_safe_ret()
Documentation/admin-guide/hw-vuln/index.rst | 1 +
Documentation/admin-guide/hw-vuln/srso.rst | 133 ++++++++++++
.../admin-guide/kernel-parameters.txt | 11 +
arch/x86/Kconfig | 7 +
arch/x86/include/asm/cpufeature.h | 23 +-
arch/x86/include/asm/cpufeatures.h | 15 +-
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/include/asm/nospec-branch.h | 34 ++-
arch/x86/include/asm/processor.h | 7 +-
arch/x86/kernel/alternative.c | 2 +-
arch/x86/kernel/cpu/amd.c | 19 ++
arch/x86/kernel/cpu/bugs.c | 197 ++++++++++++++++++
arch/x86/kernel/cpu/common.c | 17 +-
arch/x86/kernel/cpu/mkcapflags.sh | 2 +-
arch/x86/kernel/cpu/proc.c | 2 +-
arch/x86/kernel/cpu/scattered.c | 3 +
arch/x86/kernel/vmlinux.lds.S | 38 +++-
arch/x86/kvm/cpuid.c | 3 +
arch/x86/kvm/svm/svm.c | 4 +-
arch/x86/kvm/svm/vmenter.S | 3 +
arch/x86/lib/retpoline.S | 135 ++++++++++--
drivers/base/cpu.c | 8 +
include/linux/cpu.h | 2 +
include/linux/objtool.h | 28 +++
tools/arch/x86/include/asm/cpufeatures.h | 16 +-
tools/arch/x86/include/asm/msr-index.h | 1 +
tools/include/linux/objtool.h | 28 +++
tools/objtool/arch.h | 1 +
tools/objtool/arch/x86/decode.c | 6 +
tools/objtool/check.c | 41 +++-
tools/objtool/elf.h | 1 +
.../perf/trace/beauty/tracepoints/x86_msr.sh | 2 +-
32 files changed, 740 insertions(+), 51 deletions(-)
create mode 100644 Documentation/admin-guide/hw-vuln/srso.rst
--
2.25.1
1
28
From: Jamal Hadi Salim <jhs(a)mojatatu.com>
stable inclusion
from stable-v5.10.196
commit 8db844077ec9912d75952c80d76da71fc2412852
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I84B2W
CVE: CVE-2023-42755
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/patch/?id=…
--------------------------------
commit 265b4da82dbf5df04bee5a5d46b7474b1aaf326a upstream.
The rsvp classifier has served us well for about a quarter of a century but has
has not been getting much maintenance attention due to lack of known users.
Signed-off-by: Jamal Hadi Salim <jhs(a)mojatatu.com>
Acked-by: Jiri Pirko <jiri(a)nvidia.com>
Signed-off-by: Paolo Abeni <pabeni(a)redhat.com>
Signed-off-by: Kyle Zeng <zengyhkyle(a)gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Signed-off-by: Dong Chenchen <dongchenchen2(a)huawei.com>
---
net/sched/Kconfig | 28 --
net/sched/Makefile | 2 -
net/sched/cls_rsvp.c | 24 --
net/sched/cls_rsvp.h | 777 ------------------------------------------
net/sched/cls_rsvp6.c | 24 --
5 files changed, 855 deletions(-)
delete mode 100644 net/sched/cls_rsvp.c
delete mode 100644 net/sched/cls_rsvp.h
delete mode 100644 net/sched/cls_rsvp6.c
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 697522371914..2046c16b29f0 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -548,34 +548,6 @@ config CLS_U32_MARK
help
Say Y here to be able to use netfilter marks as u32 key.
-config NET_CLS_RSVP
- tristate "IPv4 Resource Reservation Protocol (RSVP)"
- select NET_CLS
- help
- The Resource Reservation Protocol (RSVP) permits end systems to
- request a minimum and maximum data flow rate for a connection; this
- is important for real time data such as streaming sound or video.
-
- Say Y here if you want to be able to classify outgoing packets based
- on their RSVP requests.
-
- To compile this code as a module, choose M here: the
- module will be called cls_rsvp.
-
-config NET_CLS_RSVP6
- tristate "IPv6 Resource Reservation Protocol (RSVP6)"
- select NET_CLS
- help
- The Resource Reservation Protocol (RSVP) permits end systems to
- request a minimum and maximum data flow rate for a connection; this
- is important for real time data such as streaming sound or video.
-
- Say Y here if you want to be able to classify outgoing packets based
- on their RSVP requests and you are using the IPv6 protocol.
-
- To compile this code as a module, choose M here: the
- module will be called cls_rsvp6.
-
config NET_CLS_FLOW
tristate "Flow classifier"
select NET_CLS
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 4311fdb21119..df2bcd785f7d 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -68,8 +68,6 @@ obj-$(CONFIG_NET_SCH_TAPRIO) += sch_taprio.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
obj-$(CONFIG_NET_CLS_FW) += cls_fw.o
-obj-$(CONFIG_NET_CLS_RSVP) += cls_rsvp.o
-obj-$(CONFIG_NET_CLS_RSVP6) += cls_rsvp6.o
obj-$(CONFIG_NET_CLS_BASIC) += cls_basic.o
obj-$(CONFIG_NET_CLS_FLOW) += cls_flow.o
obj-$(CONFIG_NET_CLS_CGROUP) += cls_cgroup.o
diff --git a/net/sched/cls_rsvp.c b/net/sched/cls_rsvp.c
deleted file mode 100644
index de1c1d4da597..000000000000
--- a/net/sched/cls_rsvp.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * net/sched/cls_rsvp.c Special RSVP packet classifier for IPv4.
- *
- * Authors: Alexey Kuznetsov, <kuznet(a)ms2.inr.ac.ru>
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/skbuff.h>
-#include <net/ip.h>
-#include <net/netlink.h>
-#include <net/act_api.h>
-#include <net/pkt_cls.h>
-
-#define RSVP_DST_LEN 1
-#define RSVP_ID "rsvp"
-#define RSVP_OPS cls_rsvp_ops
-
-#include "cls_rsvp.h"
-MODULE_LICENSE("GPL");
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
deleted file mode 100644
index d36949d9382c..000000000000
--- a/net/sched/cls_rsvp.h
+++ /dev/null
@@ -1,777 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * net/sched/cls_rsvp.h Template file for RSVPv[46] classifiers.
- *
- * Authors: Alexey Kuznetsov, <kuznet(a)ms2.inr.ac.ru>
- */
-
-/*
- Comparing to general packet classification problem,
- RSVP needs only sevaral relatively simple rules:
-
- * (dst, protocol) are always specified,
- so that we are able to hash them.
- * src may be exact, or may be wildcard, so that
- we can keep a hash table plus one wildcard entry.
- * source port (or flow label) is important only if src is given.
-
- IMPLEMENTATION.
-
- We use a two level hash table: The top level is keyed by
- destination address and protocol ID, every bucket contains a list
- of "rsvp sessions", identified by destination address, protocol and
- DPI(="Destination Port ID"): triple (key, mask, offset).
-
- Every bucket has a smaller hash table keyed by source address
- (cf. RSVP flowspec) and one wildcard entry for wildcard reservations.
- Every bucket is again a list of "RSVP flows", selected by
- source address and SPI(="Source Port ID" here rather than
- "security parameter index"): triple (key, mask, offset).
-
-
- NOTE 1. All the packets with IPv6 extension headers (but AH and ESP)
- and all fragmented packets go to the best-effort traffic class.
-
-
- NOTE 2. Two "port id"'s seems to be redundant, rfc2207 requires
- only one "Generalized Port Identifier". So that for classic
- ah, esp (and udp,tcp) both *pi should coincide or one of them
- should be wildcard.
-
- At first sight, this redundancy is just a waste of CPU
- resources. But DPI and SPI add the possibility to assign different
- priorities to GPIs. Look also at note 4 about tunnels below.
-
-
- NOTE 3. One complication is the case of tunneled packets.
- We implement it as following: if the first lookup
- matches a special session with "tunnelhdr" value not zero,
- flowid doesn't contain the true flow ID, but the tunnel ID (1...255).
- In this case, we pull tunnelhdr bytes and restart lookup
- with tunnel ID added to the list of keys. Simple and stupid 8)8)
- It's enough for PIMREG and IPIP.
-
-
- NOTE 4. Two GPIs make it possible to parse even GRE packets.
- F.e. DPI can select ETH_P_IP (and necessary flags to make
- tunnelhdr correct) in GRE protocol field and SPI matches
- GRE key. Is it not nice? 8)8)
-
-
- Well, as result, despite its simplicity, we get a pretty
- powerful classification engine. */
-
-
-struct rsvp_head {
- u32 tmap[256/32];
- u32 hgenerator;
- u8 tgenerator;
- struct rsvp_session __rcu *ht[256];
- struct rcu_head rcu;
-};
-
-struct rsvp_session {
- struct rsvp_session __rcu *next;
- __be32 dst[RSVP_DST_LEN];
- struct tc_rsvp_gpi dpi;
- u8 protocol;
- u8 tunnelid;
- /* 16 (src,sport) hash slots, and one wildcard source slot */
- struct rsvp_filter __rcu *ht[16 + 1];
- struct rcu_head rcu;
-};
-
-
-struct rsvp_filter {
- struct rsvp_filter __rcu *next;
- __be32 src[RSVP_DST_LEN];
- struct tc_rsvp_gpi spi;
- u8 tunnelhdr;
-
- struct tcf_result res;
- struct tcf_exts exts;
-
- u32 handle;
- struct rsvp_session *sess;
- struct rcu_work rwork;
-};
-
-static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
-{
- unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
-
- h ^= h>>16;
- h ^= h>>8;
- return (h ^ protocol ^ tunnelid) & 0xFF;
-}
-
-static inline unsigned int hash_src(__be32 *src)
-{
- unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
-
- h ^= h>>16;
- h ^= h>>8;
- h ^= h>>4;
- return h & 0xF;
-}
-
-#define RSVP_APPLY_RESULT() \
-{ \
- int r = tcf_exts_exec(skb, &f->exts, res); \
- if (r < 0) \
- continue; \
- else if (r > 0) \
- return r; \
-}
-
-static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
- struct tcf_result *res)
-{
- struct rsvp_head *head = rcu_dereference_bh(tp->root);
- struct rsvp_session *s;
- struct rsvp_filter *f;
- unsigned int h1, h2;
- __be32 *dst, *src;
- u8 protocol;
- u8 tunnelid = 0;
- u8 *xprt;
-#if RSVP_DST_LEN == 4
- struct ipv6hdr *nhptr;
-
- if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
- return -1;
- nhptr = ipv6_hdr(skb);
-#else
- struct iphdr *nhptr;
-
- if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
- return -1;
- nhptr = ip_hdr(skb);
-#endif
-restart:
-
-#if RSVP_DST_LEN == 4
- src = &nhptr->saddr.s6_addr32[0];
- dst = &nhptr->daddr.s6_addr32[0];
- protocol = nhptr->nexthdr;
- xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
-#else
- src = &nhptr->saddr;
- dst = &nhptr->daddr;
- protocol = nhptr->protocol;
- xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
- if (ip_is_fragment(nhptr))
- return -1;
-#endif
-
- h1 = hash_dst(dst, protocol, tunnelid);
- h2 = hash_src(src);
-
- for (s = rcu_dereference_bh(head->ht[h1]); s;
- s = rcu_dereference_bh(s->next)) {
- if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
- protocol == s->protocol &&
- !(s->dpi.mask &
- (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
-#if RSVP_DST_LEN == 4
- dst[0] == s->dst[0] &&
- dst[1] == s->dst[1] &&
- dst[2] == s->dst[2] &&
-#endif
- tunnelid == s->tunnelid) {
-
- for (f = rcu_dereference_bh(s->ht[h2]); f;
- f = rcu_dereference_bh(f->next)) {
- if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
- !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
-#if RSVP_DST_LEN == 4
- &&
- src[0] == f->src[0] &&
- src[1] == f->src[1] &&
- src[2] == f->src[2]
-#endif
- ) {
- *res = f->res;
- RSVP_APPLY_RESULT();
-
-matched:
- if (f->tunnelhdr == 0)
- return 0;
-
- tunnelid = f->res.classid;
- nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
- goto restart;
- }
- }
-
- /* And wildcard bucket... */
- for (f = rcu_dereference_bh(s->ht[16]); f;
- f = rcu_dereference_bh(f->next)) {
- *res = f->res;
- RSVP_APPLY_RESULT();
- goto matched;
- }
- return -1;
- }
- }
- return -1;
-}
-
-static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- struct rsvp_session *s;
- struct rsvp_filter __rcu **ins;
- struct rsvp_filter *pins;
- unsigned int h1 = h & 0xFF;
- unsigned int h2 = (h >> 8) & 0xFF;
-
- for (s = rtnl_dereference(head->ht[h1]); s;
- s = rtnl_dereference(s->next)) {
- for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
- ins = &pins->next, pins = rtnl_dereference(*ins)) {
- if (pins->handle == h) {
- RCU_INIT_POINTER(n->next, pins->next);
- rcu_assign_pointer(*ins, n);
- return;
- }
- }
- }
-
- /* Something went wrong if we are trying to replace a non-existant
- * node. Mind as well halt instead of silently failing.
- */
- BUG_ON(1);
-}
-
-static void *rsvp_get(struct tcf_proto *tp, u32 handle)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- struct rsvp_session *s;
- struct rsvp_filter *f;
- unsigned int h1 = handle & 0xFF;
- unsigned int h2 = (handle >> 8) & 0xFF;
-
- if (h2 > 16)
- return NULL;
-
- for (s = rtnl_dereference(head->ht[h1]); s;
- s = rtnl_dereference(s->next)) {
- for (f = rtnl_dereference(s->ht[h2]); f;
- f = rtnl_dereference(f->next)) {
- if (f->handle == handle)
- return f;
- }
- }
- return NULL;
-}
-
-static int rsvp_init(struct tcf_proto *tp)
-{
- struct rsvp_head *data;
-
- data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
- if (data) {
- rcu_assign_pointer(tp->root, data);
- return 0;
- }
- return -ENOBUFS;
-}
-
-static void __rsvp_delete_filter(struct rsvp_filter *f)
-{
- tcf_exts_destroy(&f->exts);
- tcf_exts_put_net(&f->exts);
- kfree(f);
-}
-
-static void rsvp_delete_filter_work(struct work_struct *work)
-{
- struct rsvp_filter *f = container_of(to_rcu_work(work),
- struct rsvp_filter,
- rwork);
- rtnl_lock();
- __rsvp_delete_filter(f);
- rtnl_unlock();
-}
-
-static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
-{
- tcf_unbind_filter(tp, &f->res);
- /* all classifiers are required to call tcf_exts_destroy() after rcu
- * grace period, since converted-to-rcu actions are relying on that
- * in cleanup() callback
- */
- if (tcf_exts_get_net(&f->exts))
- tcf_queue_work(&f->rwork, rsvp_delete_filter_work);
- else
- __rsvp_delete_filter(f);
-}
-
-static void rsvp_destroy(struct tcf_proto *tp, bool rtnl_held,
- struct netlink_ext_ack *extack)
-{
- struct rsvp_head *data = rtnl_dereference(tp->root);
- int h1, h2;
-
- if (data == NULL)
- return;
-
- for (h1 = 0; h1 < 256; h1++) {
- struct rsvp_session *s;
-
- while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
- RCU_INIT_POINTER(data->ht[h1], s->next);
-
- for (h2 = 0; h2 <= 16; h2++) {
- struct rsvp_filter *f;
-
- while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
- rcu_assign_pointer(s->ht[h2], f->next);
- rsvp_delete_filter(tp, f);
- }
- }
- kfree_rcu(s, rcu);
- }
- }
- kfree_rcu(data, rcu);
-}
-
-static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last,
- bool rtnl_held, struct netlink_ext_ack *extack)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- struct rsvp_filter *nfp, *f = arg;
- struct rsvp_filter __rcu **fp;
- unsigned int h = f->handle;
- struct rsvp_session __rcu **sp;
- struct rsvp_session *nsp, *s = f->sess;
- int i, h1;
-
- fp = &s->ht[(h >> 8) & 0xFF];
- for (nfp = rtnl_dereference(*fp); nfp;
- fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
- if (nfp == f) {
- RCU_INIT_POINTER(*fp, f->next);
- rsvp_delete_filter(tp, f);
-
- /* Strip tree */
-
- for (i = 0; i <= 16; i++)
- if (s->ht[i])
- goto out;
-
- /* OK, session has no flows */
- sp = &head->ht[h & 0xFF];
- for (nsp = rtnl_dereference(*sp); nsp;
- sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
- if (nsp == s) {
- RCU_INIT_POINTER(*sp, s->next);
- kfree_rcu(s, rcu);
- goto out;
- }
- }
-
- break;
- }
- }
-
-out:
- *last = true;
- for (h1 = 0; h1 < 256; h1++) {
- if (rcu_access_pointer(head->ht[h1])) {
- *last = false;
- break;
- }
- }
-
- return 0;
-}
-
-static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
-{
- struct rsvp_head *data = rtnl_dereference(tp->root);
- int i = 0xFFFF;
-
- while (i-- > 0) {
- u32 h;
-
- if ((data->hgenerator += 0x10000) == 0)
- data->hgenerator = 0x10000;
- h = data->hgenerator|salt;
- if (!rsvp_get(tp, h))
- return h;
- }
- return 0;
-}
-
-static int tunnel_bts(struct rsvp_head *data)
-{
- int n = data->tgenerator >> 5;
- u32 b = 1 << (data->tgenerator & 0x1F);
-
- if (data->tmap[n] & b)
- return 0;
- data->tmap[n] |= b;
- return 1;
-}
-
-static void tunnel_recycle(struct rsvp_head *data)
-{
- struct rsvp_session __rcu **sht = data->ht;
- u32 tmap[256/32];
- int h1, h2;
-
- memset(tmap, 0, sizeof(tmap));
-
- for (h1 = 0; h1 < 256; h1++) {
- struct rsvp_session *s;
- for (s = rtnl_dereference(sht[h1]); s;
- s = rtnl_dereference(s->next)) {
- for (h2 = 0; h2 <= 16; h2++) {
- struct rsvp_filter *f;
-
- for (f = rtnl_dereference(s->ht[h2]); f;
- f = rtnl_dereference(f->next)) {
- if (f->tunnelhdr == 0)
- continue;
- data->tgenerator = f->res.classid;
- tunnel_bts(data);
- }
- }
- }
- }
-
- memcpy(data->tmap, tmap, sizeof(tmap));
-}
-
-static u32 gen_tunnel(struct rsvp_head *data)
-{
- int i, k;
-
- for (k = 0; k < 2; k++) {
- for (i = 255; i > 0; i--) {
- if (++data->tgenerator == 0)
- data->tgenerator = 1;
- if (tunnel_bts(data))
- return data->tgenerator;
- }
- tunnel_recycle(data);
- }
- return 0;
-}
-
-static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
- [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
- [TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) },
- [TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) },
- [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
-};
-
-static int rsvp_change(struct net *net, struct sk_buff *in_skb,
- struct tcf_proto *tp, unsigned long base,
- u32 handle,
- struct nlattr **tca,
- void **arg, bool ovr, bool rtnl_held,
- struct netlink_ext_ack *extack)
-{
- struct rsvp_head *data = rtnl_dereference(tp->root);
- struct rsvp_filter *f, *nfp;
- struct rsvp_filter __rcu **fp;
- struct rsvp_session *nsp, *s;
- struct rsvp_session __rcu **sp;
- struct tc_rsvp_pinfo *pinfo = NULL;
- struct nlattr *opt = tca[TCA_OPTIONS];
- struct nlattr *tb[TCA_RSVP_MAX + 1];
- struct tcf_exts e;
- unsigned int h1, h2;
- __be32 *dst;
- int err;
-
- if (opt == NULL)
- return handle ? -EINVAL : 0;
-
- err = nla_parse_nested_deprecated(tb, TCA_RSVP_MAX, opt, rsvp_policy,
- NULL);
- if (err < 0)
- return err;
-
- err = tcf_exts_init(&e, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
- if (err < 0)
- return err;
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr, true,
- extack);
- if (err < 0)
- goto errout2;
-
- f = *arg;
- if (f) {
- /* Node exists: adjust only classid */
- struct rsvp_filter *n;
-
- if (f->handle != handle && handle)
- goto errout2;
-
- n = kmemdup(f, sizeof(*f), GFP_KERNEL);
- if (!n) {
- err = -ENOMEM;
- goto errout2;
- }
-
- err = tcf_exts_init(&n->exts, net, TCA_RSVP_ACT,
- TCA_RSVP_POLICE);
- if (err < 0) {
- kfree(n);
- goto errout2;
- }
-
- if (tb[TCA_RSVP_CLASSID]) {
- n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
- tcf_bind_filter(tp, &n->res, base);
- }
-
- tcf_exts_change(&n->exts, &e);
- rsvp_replace(tp, n, handle);
- return 0;
- }
-
- /* Now more serious part... */
- err = -EINVAL;
- if (handle)
- goto errout2;
- if (tb[TCA_RSVP_DST] == NULL)
- goto errout2;
-
- err = -ENOBUFS;
- f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
- if (f == NULL)
- goto errout2;
-
- err = tcf_exts_init(&f->exts, net, TCA_RSVP_ACT, TCA_RSVP_POLICE);
- if (err < 0)
- goto errout;
- h2 = 16;
- if (tb[TCA_RSVP_SRC]) {
- memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
- h2 = hash_src(f->src);
- }
- if (tb[TCA_RSVP_PINFO]) {
- pinfo = nla_data(tb[TCA_RSVP_PINFO]);
- f->spi = pinfo->spi;
- f->tunnelhdr = pinfo->tunnelhdr;
- }
- if (tb[TCA_RSVP_CLASSID])
- f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
-
- dst = nla_data(tb[TCA_RSVP_DST]);
- h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
-
- err = -ENOMEM;
- if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
- goto errout;
-
- if (f->tunnelhdr) {
- err = -EINVAL;
- if (f->res.classid > 255)
- goto errout;
-
- err = -ENOMEM;
- if (f->res.classid == 0 &&
- (f->res.classid = gen_tunnel(data)) == 0)
- goto errout;
- }
-
- for (sp = &data->ht[h1];
- (s = rtnl_dereference(*sp)) != NULL;
- sp = &s->next) {
- if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
- pinfo && pinfo->protocol == s->protocol &&
- memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
-#if RSVP_DST_LEN == 4
- dst[0] == s->dst[0] &&
- dst[1] == s->dst[1] &&
- dst[2] == s->dst[2] &&
-#endif
- pinfo->tunnelid == s->tunnelid) {
-
-insert:
- /* OK, we found appropriate session */
-
- fp = &s->ht[h2];
-
- f->sess = s;
- if (f->tunnelhdr == 0)
- tcf_bind_filter(tp, &f->res, base);
-
- tcf_exts_change(&f->exts, &e);
-
- fp = &s->ht[h2];
- for (nfp = rtnl_dereference(*fp); nfp;
- fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
- __u32 mask = nfp->spi.mask & f->spi.mask;
-
- if (mask != f->spi.mask)
- break;
- }
- RCU_INIT_POINTER(f->next, nfp);
- rcu_assign_pointer(*fp, f);
-
- *arg = f;
- return 0;
- }
- }
-
- /* No session found. Create new one. */
-
- err = -ENOBUFS;
- s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
- if (s == NULL)
- goto errout;
- memcpy(s->dst, dst, sizeof(s->dst));
-
- if (pinfo) {
- s->dpi = pinfo->dpi;
- s->protocol = pinfo->protocol;
- s->tunnelid = pinfo->tunnelid;
- }
- sp = &data->ht[h1];
- for (nsp = rtnl_dereference(*sp); nsp;
- sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
- if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
- break;
- }
- RCU_INIT_POINTER(s->next, nsp);
- rcu_assign_pointer(*sp, s);
-
- goto insert;
-
-errout:
- tcf_exts_destroy(&f->exts);
- kfree(f);
-errout2:
- tcf_exts_destroy(&e);
- return err;
-}
-
-static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg,
- bool rtnl_held)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- unsigned int h, h1;
-
- if (arg->stop)
- return;
-
- for (h = 0; h < 256; h++) {
- struct rsvp_session *s;
-
- for (s = rtnl_dereference(head->ht[h]); s;
- s = rtnl_dereference(s->next)) {
- for (h1 = 0; h1 <= 16; h1++) {
- struct rsvp_filter *f;
-
- for (f = rtnl_dereference(s->ht[h1]); f;
- f = rtnl_dereference(f->next)) {
- if (arg->count < arg->skip) {
- arg->count++;
- continue;
- }
- if (arg->fn(tp, f, arg) < 0) {
- arg->stop = 1;
- return;
- }
- arg->count++;
- }
- }
- }
- }
-}
-
-static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
- struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
-{
- struct rsvp_filter *f = fh;
- struct rsvp_session *s;
- struct nlattr *nest;
- struct tc_rsvp_pinfo pinfo;
-
- if (f == NULL)
- return skb->len;
- s = f->sess;
-
- t->tcm_handle = f->handle;
-
- nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
- if (nest == NULL)
- goto nla_put_failure;
-
- if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
- goto nla_put_failure;
- pinfo.dpi = s->dpi;
- pinfo.spi = f->spi;
- pinfo.protocol = s->protocol;
- pinfo.tunnelid = s->tunnelid;
- pinfo.tunnelhdr = f->tunnelhdr;
- pinfo.pad = 0;
- if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
- goto nla_put_failure;
- if (f->res.classid &&
- nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
- goto nla_put_failure;
- if (((f->handle >> 8) & 0xFF) != 16 &&
- nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
- goto nla_put_failure;
-
- if (tcf_exts_dump(skb, &f->exts) < 0)
- goto nla_put_failure;
-
- nla_nest_end(skb, nest);
-
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
- goto nla_put_failure;
- return skb->len;
-
-nla_put_failure:
- nla_nest_cancel(skb, nest);
- return -1;
-}
-
-static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
- unsigned long base)
-{
- struct rsvp_filter *f = fh;
-
- if (f && f->res.classid == classid) {
- if (cl)
- __tcf_bind_filter(q, &f->res, base);
- else
- __tcf_unbind_filter(q, &f->res);
- }
-}
-
-static struct tcf_proto_ops RSVP_OPS __read_mostly = {
- .kind = RSVP_ID,
- .classify = rsvp_classify,
- .init = rsvp_init,
- .destroy = rsvp_destroy,
- .get = rsvp_get,
- .change = rsvp_change,
- .delete = rsvp_delete,
- .walk = rsvp_walk,
- .dump = rsvp_dump,
- .bind_class = rsvp_bind_class,
- .owner = THIS_MODULE,
-};
-
-static int __init init_rsvp(void)
-{
- return register_tcf_proto_ops(&RSVP_OPS);
-}
-
-static void __exit exit_rsvp(void)
-{
- unregister_tcf_proto_ops(&RSVP_OPS);
-}
-
-module_init(init_rsvp)
-module_exit(exit_rsvp)
diff --git a/net/sched/cls_rsvp6.c b/net/sched/cls_rsvp6.c
deleted file mode 100644
index 64078846000e..000000000000
--- a/net/sched/cls_rsvp6.c
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * net/sched/cls_rsvp6.c Special RSVP packet classifier for IPv6.
- *
- * Authors: Alexey Kuznetsov, <kuznet(a)ms2.inr.ac.ru>
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ipv6.h>
-#include <linux/skbuff.h>
-#include <net/act_api.h>
-#include <net/pkt_cls.h>
-#include <net/netlink.h>
-
-#define RSVP_DST_LEN 4
-#define RSVP_ID "rsvp6"
-#define RSVP_OPS cls_rsvp6_ops
-
-#include "cls_rsvp.h"
-MODULE_LICENSE("GPL");
--
2.25.1
2
1
From: Jamal Hadi Salim <jhs(a)mojatatu.com>
stable inclusion
from stable-v4.19.294
commit 6ca0ea6a46e7a2d70fb1b1f6a886efe2b2365e16
category: bugfix
bugzilla: 189257, https://gitee.com/src-openeuler/kernel/issues/I84B2W
CVE: CVE-2023-42755
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/patch/?id=…
--------------------------------
commit 265b4da82dbf5df04bee5a5d46b7474b1aaf326a upstream.
The rsvp classifier has served us well for about a quarter of a century but has
has not been getting much maintenance attention due to lack of known users.
Signed-off-by: Jamal Hadi Salim <jhs(a)mojatatu.com>
Acked-by: Jiri Pirko <jiri(a)nvidia.com>
Signed-off-by: Paolo Abeni <pabeni(a)redhat.com>
Signed-off-by: Kyle Zeng <zengyhkyle(a)gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Signed-off-by: Dong Chenchen <dongchenchen2(a)huawei.com>
---
net/sched/Kconfig | 28 --
net/sched/Makefile | 2 -
net/sched/cls_rsvp.c | 28 --
net/sched/cls_rsvp.h | 770 ------------------------------------------
net/sched/cls_rsvp6.c | 28 --
5 files changed, 856 deletions(-)
delete mode 100644 net/sched/cls_rsvp.c
delete mode 100644 net/sched/cls_rsvp.h
delete mode 100644 net/sched/cls_rsvp6.c
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 4547022ed7f4..7698a8974a47 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -503,34 +503,6 @@ config CLS_U32_MARK
---help---
Say Y here to be able to use netfilter marks as u32 key.
-config NET_CLS_RSVP
- tristate "IPv4 Resource Reservation Protocol (RSVP)"
- select NET_CLS
- ---help---
- The Resource Reservation Protocol (RSVP) permits end systems to
- request a minimum and maximum data flow rate for a connection; this
- is important for real time data such as streaming sound or video.
-
- Say Y here if you want to be able to classify outgoing packets based
- on their RSVP requests.
-
- To compile this code as a module, choose M here: the
- module will be called cls_rsvp.
-
-config NET_CLS_RSVP6
- tristate "IPv6 Resource Reservation Protocol (RSVP6)"
- select NET_CLS
- ---help---
- The Resource Reservation Protocol (RSVP) permits end systems to
- request a minimum and maximum data flow rate for a connection; this
- is important for real time data such as streaming sound or video.
-
- Say Y here if you want to be able to classify outgoing packets based
- on their RSVP requests and you are using the IPv6 protocol.
-
- To compile this code as a module, choose M here: the
- module will be called cls_rsvp6.
-
config NET_CLS_FLOW
tristate "Flow classifier"
select NET_CLS
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 5eed580cdb42..3139c32e1947 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -61,8 +61,6 @@ obj-$(CONFIG_NET_SCH_ETF) += sch_etf.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
obj-$(CONFIG_NET_CLS_FW) += cls_fw.o
-obj-$(CONFIG_NET_CLS_RSVP) += cls_rsvp.o
-obj-$(CONFIG_NET_CLS_RSVP6) += cls_rsvp6.o
obj-$(CONFIG_NET_CLS_BASIC) += cls_basic.o
obj-$(CONFIG_NET_CLS_FLOW) += cls_flow.o
obj-$(CONFIG_NET_CLS_CGROUP) += cls_cgroup.o
diff --git a/net/sched/cls_rsvp.c b/net/sched/cls_rsvp.c
deleted file mode 100644
index cbb5e0d600f3..000000000000
--- a/net/sched/cls_rsvp.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * net/sched/cls_rsvp.c Special RSVP packet classifier for IPv4.
- *
- * 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.
- *
- * Authors: Alexey Kuznetsov, <kuznet(a)ms2.inr.ac.ru>
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/skbuff.h>
-#include <net/ip.h>
-#include <net/netlink.h>
-#include <net/act_api.h>
-#include <net/pkt_cls.h>
-
-#define RSVP_DST_LEN 1
-#define RSVP_ID "rsvp"
-#define RSVP_OPS cls_rsvp_ops
-
-#include "cls_rsvp.h"
-MODULE_LICENSE("GPL");
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
deleted file mode 100644
index a1e9f7cbcffc..000000000000
--- a/net/sched/cls_rsvp.h
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
- * net/sched/cls_rsvp.h Template file for RSVPv[46] classifiers.
- *
- * 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.
- *
- * Authors: Alexey Kuznetsov, <kuznet(a)ms2.inr.ac.ru>
- */
-
-/*
- Comparing to general packet classification problem,
- RSVP needs only sevaral relatively simple rules:
-
- * (dst, protocol) are always specified,
- so that we are able to hash them.
- * src may be exact, or may be wildcard, so that
- we can keep a hash table plus one wildcard entry.
- * source port (or flow label) is important only if src is given.
-
- IMPLEMENTATION.
-
- We use a two level hash table: The top level is keyed by
- destination address and protocol ID, every bucket contains a list
- of "rsvp sessions", identified by destination address, protocol and
- DPI(="Destination Port ID"): triple (key, mask, offset).
-
- Every bucket has a smaller hash table keyed by source address
- (cf. RSVP flowspec) and one wildcard entry for wildcard reservations.
- Every bucket is again a list of "RSVP flows", selected by
- source address and SPI(="Source Port ID" here rather than
- "security parameter index"): triple (key, mask, offset).
-
-
- NOTE 1. All the packets with IPv6 extension headers (but AH and ESP)
- and all fragmented packets go to the best-effort traffic class.
-
-
- NOTE 2. Two "port id"'s seems to be redundant, rfc2207 requires
- only one "Generalized Port Identifier". So that for classic
- ah, esp (and udp,tcp) both *pi should coincide or one of them
- should be wildcard.
-
- At first sight, this redundancy is just a waste of CPU
- resources. But DPI and SPI add the possibility to assign different
- priorities to GPIs. Look also at note 4 about tunnels below.
-
-
- NOTE 3. One complication is the case of tunneled packets.
- We implement it as following: if the first lookup
- matches a special session with "tunnelhdr" value not zero,
- flowid doesn't contain the true flow ID, but the tunnel ID (1...255).
- In this case, we pull tunnelhdr bytes and restart lookup
- with tunnel ID added to the list of keys. Simple and stupid 8)8)
- It's enough for PIMREG and IPIP.
-
-
- NOTE 4. Two GPIs make it possible to parse even GRE packets.
- F.e. DPI can select ETH_P_IP (and necessary flags to make
- tunnelhdr correct) in GRE protocol field and SPI matches
- GRE key. Is it not nice? 8)8)
-
-
- Well, as result, despite its simplicity, we get a pretty
- powerful classification engine. */
-
-
-struct rsvp_head {
- u32 tmap[256/32];
- u32 hgenerator;
- u8 tgenerator;
- struct rsvp_session __rcu *ht[256];
- struct rcu_head rcu;
-};
-
-struct rsvp_session {
- struct rsvp_session __rcu *next;
- __be32 dst[RSVP_DST_LEN];
- struct tc_rsvp_gpi dpi;
- u8 protocol;
- u8 tunnelid;
- /* 16 (src,sport) hash slots, and one wildcard source slot */
- struct rsvp_filter __rcu *ht[16 + 1];
- struct rcu_head rcu;
-};
-
-
-struct rsvp_filter {
- struct rsvp_filter __rcu *next;
- __be32 src[RSVP_DST_LEN];
- struct tc_rsvp_gpi spi;
- u8 tunnelhdr;
-
- struct tcf_result res;
- struct tcf_exts exts;
-
- u32 handle;
- struct rsvp_session *sess;
- struct rcu_work rwork;
-};
-
-static inline unsigned int hash_dst(__be32 *dst, u8 protocol, u8 tunnelid)
-{
- unsigned int h = (__force __u32)dst[RSVP_DST_LEN - 1];
-
- h ^= h>>16;
- h ^= h>>8;
- return (h ^ protocol ^ tunnelid) & 0xFF;
-}
-
-static inline unsigned int hash_src(__be32 *src)
-{
- unsigned int h = (__force __u32)src[RSVP_DST_LEN-1];
-
- h ^= h>>16;
- h ^= h>>8;
- h ^= h>>4;
- return h & 0xF;
-}
-
-#define RSVP_APPLY_RESULT() \
-{ \
- int r = tcf_exts_exec(skb, &f->exts, res); \
- if (r < 0) \
- continue; \
- else if (r > 0) \
- return r; \
-}
-
-static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
- struct tcf_result *res)
-{
- struct rsvp_head *head = rcu_dereference_bh(tp->root);
- struct rsvp_session *s;
- struct rsvp_filter *f;
- unsigned int h1, h2;
- __be32 *dst, *src;
- u8 protocol;
- u8 tunnelid = 0;
- u8 *xprt;
-#if RSVP_DST_LEN == 4
- struct ipv6hdr *nhptr;
-
- if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
- return -1;
- nhptr = ipv6_hdr(skb);
-#else
- struct iphdr *nhptr;
-
- if (!pskb_network_may_pull(skb, sizeof(*nhptr)))
- return -1;
- nhptr = ip_hdr(skb);
-#endif
-restart:
-
-#if RSVP_DST_LEN == 4
- src = &nhptr->saddr.s6_addr32[0];
- dst = &nhptr->daddr.s6_addr32[0];
- protocol = nhptr->nexthdr;
- xprt = ((u8 *)nhptr) + sizeof(struct ipv6hdr);
-#else
- src = &nhptr->saddr;
- dst = &nhptr->daddr;
- protocol = nhptr->protocol;
- xprt = ((u8 *)nhptr) + (nhptr->ihl<<2);
- if (ip_is_fragment(nhptr))
- return -1;
-#endif
-
- h1 = hash_dst(dst, protocol, tunnelid);
- h2 = hash_src(src);
-
- for (s = rcu_dereference_bh(head->ht[h1]); s;
- s = rcu_dereference_bh(s->next)) {
- if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN - 1] &&
- protocol == s->protocol &&
- !(s->dpi.mask &
- (*(u32 *)(xprt + s->dpi.offset) ^ s->dpi.key)) &&
-#if RSVP_DST_LEN == 4
- dst[0] == s->dst[0] &&
- dst[1] == s->dst[1] &&
- dst[2] == s->dst[2] &&
-#endif
- tunnelid == s->tunnelid) {
-
- for (f = rcu_dereference_bh(s->ht[h2]); f;
- f = rcu_dereference_bh(f->next)) {
- if (src[RSVP_DST_LEN-1] == f->src[RSVP_DST_LEN - 1] &&
- !(f->spi.mask & (*(u32 *)(xprt + f->spi.offset) ^ f->spi.key))
-#if RSVP_DST_LEN == 4
- &&
- src[0] == f->src[0] &&
- src[1] == f->src[1] &&
- src[2] == f->src[2]
-#endif
- ) {
- *res = f->res;
- RSVP_APPLY_RESULT();
-
-matched:
- if (f->tunnelhdr == 0)
- return 0;
-
- tunnelid = f->res.classid;
- nhptr = (void *)(xprt + f->tunnelhdr - sizeof(*nhptr));
- goto restart;
- }
- }
-
- /* And wildcard bucket... */
- for (f = rcu_dereference_bh(s->ht[16]); f;
- f = rcu_dereference_bh(f->next)) {
- *res = f->res;
- RSVP_APPLY_RESULT();
- goto matched;
- }
- return -1;
- }
- }
- return -1;
-}
-
-static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- struct rsvp_session *s;
- struct rsvp_filter __rcu **ins;
- struct rsvp_filter *pins;
- unsigned int h1 = h & 0xFF;
- unsigned int h2 = (h >> 8) & 0xFF;
-
- for (s = rtnl_dereference(head->ht[h1]); s;
- s = rtnl_dereference(s->next)) {
- for (ins = &s->ht[h2], pins = rtnl_dereference(*ins); ;
- ins = &pins->next, pins = rtnl_dereference(*ins)) {
- if (pins->handle == h) {
- RCU_INIT_POINTER(n->next, pins->next);
- rcu_assign_pointer(*ins, n);
- return;
- }
- }
- }
-
- /* Something went wrong if we are trying to replace a non-existant
- * node. Mind as well halt instead of silently failing.
- */
- BUG_ON(1);
-}
-
-static void *rsvp_get(struct tcf_proto *tp, u32 handle)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- struct rsvp_session *s;
- struct rsvp_filter *f;
- unsigned int h1 = handle & 0xFF;
- unsigned int h2 = (handle >> 8) & 0xFF;
-
- if (h2 > 16)
- return NULL;
-
- for (s = rtnl_dereference(head->ht[h1]); s;
- s = rtnl_dereference(s->next)) {
- for (f = rtnl_dereference(s->ht[h2]); f;
- f = rtnl_dereference(f->next)) {
- if (f->handle == handle)
- return f;
- }
- }
- return NULL;
-}
-
-static int rsvp_init(struct tcf_proto *tp)
-{
- struct rsvp_head *data;
-
- data = kzalloc(sizeof(struct rsvp_head), GFP_KERNEL);
- if (data) {
- rcu_assign_pointer(tp->root, data);
- return 0;
- }
- return -ENOBUFS;
-}
-
-static void __rsvp_delete_filter(struct rsvp_filter *f)
-{
- tcf_exts_destroy(&f->exts);
- tcf_exts_put_net(&f->exts);
- kfree(f);
-}
-
-static void rsvp_delete_filter_work(struct work_struct *work)
-{
- struct rsvp_filter *f = container_of(to_rcu_work(work),
- struct rsvp_filter,
- rwork);
- rtnl_lock();
- __rsvp_delete_filter(f);
- rtnl_unlock();
-}
-
-static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
-{
- tcf_unbind_filter(tp, &f->res);
- /* all classifiers are required to call tcf_exts_destroy() after rcu
- * grace period, since converted-to-rcu actions are relying on that
- * in cleanup() callback
- */
- if (tcf_exts_get_net(&f->exts))
- tcf_queue_work(&f->rwork, rsvp_delete_filter_work);
- else
- __rsvp_delete_filter(f);
-}
-
-static void rsvp_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
-{
- struct rsvp_head *data = rtnl_dereference(tp->root);
- int h1, h2;
-
- if (data == NULL)
- return;
-
- for (h1 = 0; h1 < 256; h1++) {
- struct rsvp_session *s;
-
- while ((s = rtnl_dereference(data->ht[h1])) != NULL) {
- RCU_INIT_POINTER(data->ht[h1], s->next);
-
- for (h2 = 0; h2 <= 16; h2++) {
- struct rsvp_filter *f;
-
- while ((f = rtnl_dereference(s->ht[h2])) != NULL) {
- rcu_assign_pointer(s->ht[h2], f->next);
- rsvp_delete_filter(tp, f);
- }
- }
- kfree_rcu(s, rcu);
- }
- }
- kfree_rcu(data, rcu);
-}
-
-static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last,
- struct netlink_ext_ack *extack)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- struct rsvp_filter *nfp, *f = arg;
- struct rsvp_filter __rcu **fp;
- unsigned int h = f->handle;
- struct rsvp_session __rcu **sp;
- struct rsvp_session *nsp, *s = f->sess;
- int i, h1;
-
- fp = &s->ht[(h >> 8) & 0xFF];
- for (nfp = rtnl_dereference(*fp); nfp;
- fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
- if (nfp == f) {
- RCU_INIT_POINTER(*fp, f->next);
- rsvp_delete_filter(tp, f);
-
- /* Strip tree */
-
- for (i = 0; i <= 16; i++)
- if (s->ht[i])
- goto out;
-
- /* OK, session has no flows */
- sp = &head->ht[h & 0xFF];
- for (nsp = rtnl_dereference(*sp); nsp;
- sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
- if (nsp == s) {
- RCU_INIT_POINTER(*sp, s->next);
- kfree_rcu(s, rcu);
- goto out;
- }
- }
-
- break;
- }
- }
-
-out:
- *last = true;
- for (h1 = 0; h1 < 256; h1++) {
- if (rcu_access_pointer(head->ht[h1])) {
- *last = false;
- break;
- }
- }
-
- return 0;
-}
-
-static unsigned int gen_handle(struct tcf_proto *tp, unsigned salt)
-{
- struct rsvp_head *data = rtnl_dereference(tp->root);
- int i = 0xFFFF;
-
- while (i-- > 0) {
- u32 h;
-
- if ((data->hgenerator += 0x10000) == 0)
- data->hgenerator = 0x10000;
- h = data->hgenerator|salt;
- if (!rsvp_get(tp, h))
- return h;
- }
- return 0;
-}
-
-static int tunnel_bts(struct rsvp_head *data)
-{
- int n = data->tgenerator >> 5;
- u32 b = 1 << (data->tgenerator & 0x1F);
-
- if (data->tmap[n] & b)
- return 0;
- data->tmap[n] |= b;
- return 1;
-}
-
-static void tunnel_recycle(struct rsvp_head *data)
-{
- struct rsvp_session __rcu **sht = data->ht;
- u32 tmap[256/32];
- int h1, h2;
-
- memset(tmap, 0, sizeof(tmap));
-
- for (h1 = 0; h1 < 256; h1++) {
- struct rsvp_session *s;
- for (s = rtnl_dereference(sht[h1]); s;
- s = rtnl_dereference(s->next)) {
- for (h2 = 0; h2 <= 16; h2++) {
- struct rsvp_filter *f;
-
- for (f = rtnl_dereference(s->ht[h2]); f;
- f = rtnl_dereference(f->next)) {
- if (f->tunnelhdr == 0)
- continue;
- data->tgenerator = f->res.classid;
- tunnel_bts(data);
- }
- }
- }
- }
-
- memcpy(data->tmap, tmap, sizeof(tmap));
-}
-
-static u32 gen_tunnel(struct rsvp_head *data)
-{
- int i, k;
-
- for (k = 0; k < 2; k++) {
- for (i = 255; i > 0; i--) {
- if (++data->tgenerator == 0)
- data->tgenerator = 1;
- if (tunnel_bts(data))
- return data->tgenerator;
- }
- tunnel_recycle(data);
- }
- return 0;
-}
-
-static const struct nla_policy rsvp_policy[TCA_RSVP_MAX + 1] = {
- [TCA_RSVP_CLASSID] = { .type = NLA_U32 },
- [TCA_RSVP_DST] = { .len = RSVP_DST_LEN * sizeof(u32) },
- [TCA_RSVP_SRC] = { .len = RSVP_DST_LEN * sizeof(u32) },
- [TCA_RSVP_PINFO] = { .len = sizeof(struct tc_rsvp_pinfo) },
-};
-
-static int rsvp_change(struct net *net, struct sk_buff *in_skb,
- struct tcf_proto *tp, unsigned long base,
- u32 handle,
- struct nlattr **tca,
- void **arg, bool ovr, struct netlink_ext_ack *extack)
-{
- struct rsvp_head *data = rtnl_dereference(tp->root);
- struct rsvp_filter *f, *nfp;
- struct rsvp_filter __rcu **fp;
- struct rsvp_session *nsp, *s;
- struct rsvp_session __rcu **sp;
- struct tc_rsvp_pinfo *pinfo = NULL;
- struct nlattr *opt = tca[TCA_OPTIONS];
- struct nlattr *tb[TCA_RSVP_MAX + 1];
- struct tcf_exts e;
- unsigned int h1, h2;
- __be32 *dst;
- int err;
-
- if (opt == NULL)
- return handle ? -EINVAL : 0;
-
- err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL);
- if (err < 0)
- return err;
-
- err = tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
- if (err < 0)
- return err;
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr, extack);
- if (err < 0)
- goto errout2;
-
- f = *arg;
- if (f) {
- /* Node exists: adjust only classid */
- struct rsvp_filter *n;
-
- if (f->handle != handle && handle)
- goto errout2;
-
- n = kmemdup(f, sizeof(*f), GFP_KERNEL);
- if (!n) {
- err = -ENOMEM;
- goto errout2;
- }
-
- err = tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
- if (err < 0) {
- kfree(n);
- goto errout2;
- }
-
- if (tb[TCA_RSVP_CLASSID]) {
- n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
- tcf_bind_filter(tp, &n->res, base);
- }
-
- tcf_exts_change(&n->exts, &e);
- rsvp_replace(tp, n, handle);
- return 0;
- }
-
- /* Now more serious part... */
- err = -EINVAL;
- if (handle)
- goto errout2;
- if (tb[TCA_RSVP_DST] == NULL)
- goto errout2;
-
- err = -ENOBUFS;
- f = kzalloc(sizeof(struct rsvp_filter), GFP_KERNEL);
- if (f == NULL)
- goto errout2;
-
- err = tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
- if (err < 0)
- goto errout;
- h2 = 16;
- if (tb[TCA_RSVP_SRC]) {
- memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
- h2 = hash_src(f->src);
- }
- if (tb[TCA_RSVP_PINFO]) {
- pinfo = nla_data(tb[TCA_RSVP_PINFO]);
- f->spi = pinfo->spi;
- f->tunnelhdr = pinfo->tunnelhdr;
- }
- if (tb[TCA_RSVP_CLASSID])
- f->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
-
- dst = nla_data(tb[TCA_RSVP_DST]);
- h1 = hash_dst(dst, pinfo ? pinfo->protocol : 0, pinfo ? pinfo->tunnelid : 0);
-
- err = -ENOMEM;
- if ((f->handle = gen_handle(tp, h1 | (h2<<8))) == 0)
- goto errout;
-
- if (f->tunnelhdr) {
- err = -EINVAL;
- if (f->res.classid > 255)
- goto errout;
-
- err = -ENOMEM;
- if (f->res.classid == 0 &&
- (f->res.classid = gen_tunnel(data)) == 0)
- goto errout;
- }
-
- for (sp = &data->ht[h1];
- (s = rtnl_dereference(*sp)) != NULL;
- sp = &s->next) {
- if (dst[RSVP_DST_LEN-1] == s->dst[RSVP_DST_LEN-1] &&
- pinfo && pinfo->protocol == s->protocol &&
- memcmp(&pinfo->dpi, &s->dpi, sizeof(s->dpi)) == 0 &&
-#if RSVP_DST_LEN == 4
- dst[0] == s->dst[0] &&
- dst[1] == s->dst[1] &&
- dst[2] == s->dst[2] &&
-#endif
- pinfo->tunnelid == s->tunnelid) {
-
-insert:
- /* OK, we found appropriate session */
-
- fp = &s->ht[h2];
-
- f->sess = s;
- if (f->tunnelhdr == 0)
- tcf_bind_filter(tp, &f->res, base);
-
- tcf_exts_change(&f->exts, &e);
-
- fp = &s->ht[h2];
- for (nfp = rtnl_dereference(*fp); nfp;
- fp = &nfp->next, nfp = rtnl_dereference(*fp)) {
- __u32 mask = nfp->spi.mask & f->spi.mask;
-
- if (mask != f->spi.mask)
- break;
- }
- RCU_INIT_POINTER(f->next, nfp);
- rcu_assign_pointer(*fp, f);
-
- *arg = f;
- return 0;
- }
- }
-
- /* No session found. Create new one. */
-
- err = -ENOBUFS;
- s = kzalloc(sizeof(struct rsvp_session), GFP_KERNEL);
- if (s == NULL)
- goto errout;
- memcpy(s->dst, dst, sizeof(s->dst));
-
- if (pinfo) {
- s->dpi = pinfo->dpi;
- s->protocol = pinfo->protocol;
- s->tunnelid = pinfo->tunnelid;
- }
- sp = &data->ht[h1];
- for (nsp = rtnl_dereference(*sp); nsp;
- sp = &nsp->next, nsp = rtnl_dereference(*sp)) {
- if ((nsp->dpi.mask & s->dpi.mask) != s->dpi.mask)
- break;
- }
- RCU_INIT_POINTER(s->next, nsp);
- rcu_assign_pointer(*sp, s);
-
- goto insert;
-
-errout:
- tcf_exts_destroy(&f->exts);
- kfree(f);
-errout2:
- tcf_exts_destroy(&e);
- return err;
-}
-
-static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg)
-{
- struct rsvp_head *head = rtnl_dereference(tp->root);
- unsigned int h, h1;
-
- if (arg->stop)
- return;
-
- for (h = 0; h < 256; h++) {
- struct rsvp_session *s;
-
- for (s = rtnl_dereference(head->ht[h]); s;
- s = rtnl_dereference(s->next)) {
- for (h1 = 0; h1 <= 16; h1++) {
- struct rsvp_filter *f;
-
- for (f = rtnl_dereference(s->ht[h1]); f;
- f = rtnl_dereference(f->next)) {
- if (arg->count < arg->skip) {
- arg->count++;
- continue;
- }
- if (arg->fn(tp, f, arg) < 0) {
- arg->stop = 1;
- return;
- }
- arg->count++;
- }
- }
- }
- }
-}
-
-static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh,
- struct sk_buff *skb, struct tcmsg *t)
-{
- struct rsvp_filter *f = fh;
- struct rsvp_session *s;
- struct nlattr *nest;
- struct tc_rsvp_pinfo pinfo;
-
- if (f == NULL)
- return skb->len;
- s = f->sess;
-
- t->tcm_handle = f->handle;
-
- nest = nla_nest_start(skb, TCA_OPTIONS);
- if (nest == NULL)
- goto nla_put_failure;
-
- if (nla_put(skb, TCA_RSVP_DST, sizeof(s->dst), &s->dst))
- goto nla_put_failure;
- pinfo.dpi = s->dpi;
- pinfo.spi = f->spi;
- pinfo.protocol = s->protocol;
- pinfo.tunnelid = s->tunnelid;
- pinfo.tunnelhdr = f->tunnelhdr;
- pinfo.pad = 0;
- if (nla_put(skb, TCA_RSVP_PINFO, sizeof(pinfo), &pinfo))
- goto nla_put_failure;
- if (f->res.classid &&
- nla_put_u32(skb, TCA_RSVP_CLASSID, f->res.classid))
- goto nla_put_failure;
- if (((f->handle >> 8) & 0xFF) != 16 &&
- nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
- goto nla_put_failure;
-
- if (tcf_exts_dump(skb, &f->exts) < 0)
- goto nla_put_failure;
-
- nla_nest_end(skb, nest);
-
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
- goto nla_put_failure;
- return skb->len;
-
-nla_put_failure:
- nla_nest_cancel(skb, nest);
- return -1;
-}
-
-static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl)
-{
- struct rsvp_filter *f = fh;
-
- if (f && f->res.classid == classid)
- f->res.class = cl;
-}
-
-static struct tcf_proto_ops RSVP_OPS __read_mostly = {
- .kind = RSVP_ID,
- .classify = rsvp_classify,
- .init = rsvp_init,
- .destroy = rsvp_destroy,
- .get = rsvp_get,
- .change = rsvp_change,
- .delete = rsvp_delete,
- .walk = rsvp_walk,
- .dump = rsvp_dump,
- .bind_class = rsvp_bind_class,
- .owner = THIS_MODULE,
-};
-
-static int __init init_rsvp(void)
-{
- return register_tcf_proto_ops(&RSVP_OPS);
-}
-
-static void __exit exit_rsvp(void)
-{
- unregister_tcf_proto_ops(&RSVP_OPS);
-}
-
-module_init(init_rsvp)
-module_exit(exit_rsvp)
diff --git a/net/sched/cls_rsvp6.c b/net/sched/cls_rsvp6.c
deleted file mode 100644
index dd08aea2aee5..000000000000
--- a/net/sched/cls_rsvp6.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * net/sched/cls_rsvp6.c Special RSVP packet classifier for IPv6.
- *
- * 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.
- *
- * Authors: Alexey Kuznetsov, <kuznet(a)ms2.inr.ac.ru>
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ipv6.h>
-#include <linux/skbuff.h>
-#include <net/act_api.h>
-#include <net/pkt_cls.h>
-#include <net/netlink.h>
-
-#define RSVP_DST_LEN 4
-#define RSVP_ID "rsvp6"
-#define RSVP_OPS cls_rsvp6_ops
-
-#include "cls_rsvp.h"
-MODULE_LICENSE("GPL");
--
2.25.1
2
1

06 Oct '23
The hikptool ub_dfx command is submitted for the first time.
This command can be used to dump register information of the LRB, PFA, and PM modules.
Signed-off-by: Jianqiang Li <lijianqiang16(a)huawei.com>
---
libhikptdev/include/hikptdev_plug.h | 1 +
net/hikp_net_lib.h | 9 +
net/ub/ub_dfx/hikp_ub_dfx.c | 319 ++++++++++++++++++++++++++++
net/ub/ub_dfx/hikp_ub_dfx.h | 100 +++++++++
tool_lib/tool_lib.h | 2 +-
5 files changed, 430 insertions(+), 1 deletion(-)
create mode 100644 net/ub/ub_dfx/hikp_ub_dfx.c
create mode 100644 net/ub/ub_dfx/hikp_ub_dfx.h
diff --git a/libhikptdev/include/hikptdev_plug.h b/libhikptdev/include/hikptdev_plug.h
index 42bea6b..56cea78 100644
--- a/libhikptdev/include/hikptdev_plug.h
+++ b/libhikptdev/include/hikptdev_plug.h
@@ -43,6 +43,7 @@ enum cmd_module_type {
MAC_MOD = 8,
DPDK_MOD = 9,
CXL_MOD = 10,
+ UB_MOD = 11,
};
void hikp_unlock(void);
diff --git a/net/hikp_net_lib.h b/net/hikp_net_lib.h
index cc99d0c..af0a51d 100644
--- a/net/hikp_net_lib.h
+++ b/net/hikp_net_lib.h
@@ -98,6 +98,15 @@ enum roce_cmd_type {
GET_ROCEE_TSP_CMD,
};
+enum ub_cmd_type {
+ GET_UNIC_PPP_CMD = 0x1,
+ GET_UB_DFX_INFO_CMD,
+ GET_UB_LINK_INFO_CMD,
+ GET_UB_BP_INFO_CMD,
+ GET_UB_CRD_INFO_CMD,
+ GET_UB_BASIC_INFO_CMD,
+};
+
#define HIKP_MAX_PF_NUM 8
#define HIKP_NIC_MAX_FUNC_NUM 256
diff --git a/net/ub/ub_dfx/hikp_ub_dfx.c b/net/ub/ub_dfx/hikp_ub_dfx.c
new file mode 100644
index 0000000..c50f555
--- /dev/null
+++ b/net/ub/ub_dfx/hikp_ub_dfx.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2023 Hisilicon Technologies Co., Ltd.
+ * Hikptool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ *
+ * See the Mulan PSL v2 for more details.
+ */
+
+#include "tool_cmd.h"
+#include "hikp_net_lib.h"
+#include "hikp_ub_dfx.h"
+
+struct ub_dfx_param g_ub_dfx_param = { 0 };
+
+static const struct dfx_module_cmd g_ub_dfx_module_parse[] = {
+ {"LRB", LRB_DFX_REG_DUMP},
+ {"PFA", PFA_DFX_REG_DUMP},
+ {"PM", PM_DFX_REG_DUMP}
+};
+
+static const struct dfx_type_parse g_dfx_type_parse[] = {
+ {INCORRECT_REG_TYPE, WIDTH_32_BIT, "INCORRECT TYPE"},
+ {TYPE_32_STATS, WIDTH_32_BIT, "32 bit statistics"},
+ {TYPE_32_RUNNING_STATUS, WIDTH_32_BIT, "32 bit running status"},
+ {TYPE_64_STATS, WIDTH_64_BIT, "64 bit statistics"},
+};
+
+static void dfx_help_info(const struct major_cmd_ctrl *self)
+{
+ printf("\n Usage: %s %s\n", self->cmd_ptr->name, "-i <interface>\n");
+ printf("\n %s\n", self->cmd_ptr->help_info);
+ printf(" Options:\n\n");
+ printf(" %s, %-25s %s\n", "-h", "--help", "display this help and exit");
+ printf(" %s, %-25s %s\n", "-i", "--interface=<interface>",
+ "device target or bdf id, e.g. ubn0 or 0000:35:00.0");
+ printf(" %s\n", " [-m/--module LRB/PFA/PM] : this is necessary param\n");
+}
+
+static int hikp_ub_dfx_help(struct major_cmd_ctrl *self, const char *argv)
+{
+ dfx_help_info(self);
+ return 0;
+}
+
+static int hikp_ub_dfx_target(struct major_cmd_ctrl *self, const char *argv)
+{
+ self->err_no = tool_check_and_get_valid_bdf_id(argv, &(g_ub_dfx_param.target));
+ if (self->err_no != 0) {
+ snprintf(self->err_str, sizeof(self->err_str), "Unknown device %s.", argv);
+ return self->err_no;
+ }
+
+ return 0;
+}
+
+static int hikp_ub_dfx_module_select(struct major_cmd_ctrl *self, const char *argv)
+{
+ size_t arr_size = HIKP_ARRAY_SIZE(g_ub_dfx_module_parse);
+ bool is_found;
+ size_t i;
+
+ for (i = 0; i < arr_size; i++) {
+ is_found = strncmp(argv, (const char *)g_ub_dfx_module_parse[i].module_name,
+ sizeof(g_ub_dfx_module_parse[i].module_name)) == 0;
+ if (is_found) {
+ g_ub_dfx_param.sub_cmd_code = g_ub_dfx_module_parse[i].sub_cmd_code;
+ g_ub_dfx_param.module_idx = i;
+ g_ub_dfx_param.flag |= MODULE_SET_FLAG;
+ return 0;
+ }
+ }
+ dfx_help_info(self);
+ snprintf(self->err_str, sizeof(self->err_str), "-m/--module param error!!!");
+ self->err_no = -EINVAL;
+
+ return -EINVAL;
+}
+
+static int hikp_ub_dfx_get_blk_data(struct hikp_cmd_ret **cmd_ret,
+ uint32_t blk_id, uint32_t sub_cmd_code)
+{
+ struct hikp_cmd_header req_header = { 0 };
+ struct ub_dfx_req_para req_data = { 0 };
+
+ req_data.bdf = g_ub_dfx_param.target.bdf;
+ req_data.block_id = blk_id;
+ hikp_cmd_init(&req_header, UB_MOD, GET_UB_DFX_INFO_CMD, sub_cmd_code);
+ *cmd_ret = hikp_cmd_alloc(&req_header, &req_data, sizeof(req_data));
+
+ return hikp_rsp_normal_check(*cmd_ret);
+}
+
+static int hikp_ub_get_first_blk_dfx(struct ub_dfx_rsp_head *rsp_head, uint32_t **reg_data,
+ uint32_t *max_dfx_size, uint32_t *version)
+{
+ struct ub_dfx_rsp *dfx_rsp = NULL;
+ struct hikp_cmd_ret *cmd_ret;
+ int ret;
+
+ ret = hikp_ub_dfx_get_blk_data(&cmd_ret, 0, g_ub_dfx_param.sub_cmd_code);
+ if (ret < 0)
+ goto err_out;
+
+ dfx_rsp = (struct ub_dfx_rsp *)(cmd_ret->rsp_data);
+ *version = cmd_ret->version;
+ *rsp_head = dfx_rsp->rsp_head;
+ if (rsp_head->total_blk_num == 0) {
+ /* if total block number is zero, set total type number to zero anyway */
+ rsp_head->total_type_num = 0;
+ goto err_out;
+ }
+ *max_dfx_size = (uint32_t)(rsp_head->total_blk_num * MAX_DFX_DATA_NUM * sizeof(uint32_t));
+ *reg_data = (uint32_t *)calloc(1, *max_dfx_size);
+ if (*reg_data == NULL) {
+ HIKP_ERROR_PRINT("malloc log memory 0x%x failed.\n", *max_dfx_size);
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ if (rsp_head->cur_blk_size > *max_dfx_size) {
+ free(*reg_data);
+ *reg_data = NULL;
+ HIKP_ERROR_PRINT("blk0 reg_data copy size error, data size: 0x%x, max size: 0x%x\n",
+ rsp_head->cur_blk_size, *max_dfx_size);
+ ret = -EINVAL;
+ goto err_out;
+ }
+ memcpy(*reg_data, dfx_rsp->reg_data, rsp_head->cur_blk_size);
+
+ *max_dfx_size -= (uint32_t)rsp_head->cur_blk_size;
+err_out:
+ free(cmd_ret);
+ cmd_ret = NULL;
+
+ return ret;
+}
+
+static int hikp_ub_get_blk_dfx(struct ub_dfx_rsp_head *rsp_head, uint32_t blk_id,
+ uint32_t *reg_data, uint32_t *max_dfx_size)
+{
+ struct ub_dfx_rsp *dfx_rsp = NULL;
+ struct hikp_cmd_ret *cmd_ret;
+ int ret;
+
+ ret = hikp_ub_dfx_get_blk_data(&cmd_ret, blk_id, g_ub_dfx_param.sub_cmd_code);
+ if (ret < 0)
+ goto err_out;
+
+ dfx_rsp = (struct ub_dfx_rsp *)(cmd_ret->rsp_data);
+ *rsp_head = dfx_rsp->rsp_head;
+ if (rsp_head->cur_blk_size > *max_dfx_size) {
+ HIKP_ERROR_PRINT("blk%u reg_data copy size error, "
+ "data size: 0x%x, max size: 0x%x\n",
+ blk_id, rsp_head->cur_blk_size, *max_dfx_size);
+ ret = -EINVAL;
+ goto err_out;
+ }
+ memcpy(reg_data, dfx_rsp->reg_data, rsp_head->cur_blk_size);
+ *max_dfx_size -= (uint32_t)rsp_head->cur_blk_size;
+
+err_out:
+ free(cmd_ret);
+ cmd_ret = NULL;
+
+ return ret;
+}
+
+static bool is_type_found(uint16_t type_id, uint32_t *index)
+{
+ size_t arr_size = HIKP_ARRAY_SIZE(g_dfx_type_parse);
+ size_t i;
+
+ for (i = 0; i < arr_size; i++) {
+ if (g_dfx_type_parse[i].type_id == type_id) {
+ *index = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void hikp_ub_dfx_print_type_head(uint8_t type_id, uint8_t *last_type_id)
+{
+ uint32_t index = 0;
+
+ if (type_id != *last_type_id) {
+ printf("-----------------------------------------------------\n");
+ if (is_type_found(type_id, &index))
+ printf("type name: %s\n\n", g_dfx_type_parse[index].type_name);
+ else
+ HIKP_WARN_PRINT("type name: unknown type, type id is %u\n\n", type_id);
+
+ *last_type_id = type_id;
+ }
+}
+
+static void hikp_ub_dfx_print_b32(uint32_t num, uint32_t *reg_data)
+{
+ uint32_t word_num = num * WORD_NUM_PER_REG;
+ uint16_t offset;
+ uint32_t value;
+ uint32_t index;
+ uint32_t i;
+
+ for (i = 0, index = 1; i < word_num; i = i + WORD_NUM_PER_REG, index++) {
+ offset = (uint16_t)HI_GET_BITFIELD(reg_data[i], 0, DFX_REG_ADDR_MASK);
+ value = reg_data[i + 1];
+ printf("%03u: 0x%04x\t0x%08x\n", index, offset, value);
+ }
+}
+
+static void hikp_ub_dfx_print_b64(uint32_t num, uint32_t *reg_data)
+{
+ uint32_t word_num = num * WORD_NUM_PER_REG;
+ uint16_t offset;
+ uint64_t value;
+ uint32_t index;
+ uint32_t i;
+
+ for (i = 0, index = 1; i < word_num; i = i + WORD_NUM_PER_REG, index++) {
+ offset = (uint16_t)HI_GET_BITFIELD(reg_data[i], 0, DFX_REG_ADDR_MASK);
+ value = (uint64_t)reg_data[i + 1] |
+ (HI_GET_BITFIELD((uint64_t)reg_data[i], DFX_REG_VALUE_OFF,
+ DFX_REG_VALUE_MASK) << BIT_NUM_OF_WORD);
+ printf("%03u: 0x%04x\t0x%016lx\n", index, offset, value);
+ }
+}
+
+static void hikp_ub_dfx_print(const struct ub_dfx_rsp_head *rsp_head, uint32_t *reg_data)
+{
+ struct ub_dfx_type_head *type_head;
+ uint8_t last_type_id = 0;
+ uint32_t *ptr = reg_data;
+ uint8_t i;
+
+ printf("****************** module %s reg dump start ********************\n",
+ g_ub_dfx_module_parse[g_ub_dfx_param.module_idx].module_name);
+ for (i = 0; i < rsp_head->total_type_num; i++) {
+ type_head = (struct ub_dfx_type_head *)ptr;
+ if (type_head->type_id == INCORRECT_REG_TYPE) {
+ HIKP_ERROR_PRINT("No.%u type is incorrect reg type\n", i + 1u);
+ break;
+ }
+ hikp_ub_dfx_print_type_head(type_head->type_id, &last_type_id);
+ ptr++;
+ if (type_head->bit_width == WIDTH_32_BIT) {
+ hikp_ub_dfx_print_b32((uint32_t)type_head->reg_num, ptr);
+ } else if (type_head->bit_width == WIDTH_64_BIT) {
+ hikp_ub_dfx_print_b64((uint32_t)type_head->reg_num, ptr);
+ } else {
+ HIKP_ERROR_PRINT("type%u's bit width error.\n", type_head->type_id);
+ break;
+ }
+ ptr += (uint32_t)type_head->reg_num * WORD_NUM_PER_REG;
+ }
+ printf("################### ====== dump end ====== ######################\n");
+}
+
+static void hikp_ub_dfx_execute(struct major_cmd_ctrl *self)
+{
+ struct ub_dfx_rsp_head rsp_head = { 0 };
+ struct ub_dfx_rsp_head tmp_head = { 0 };
+ uint32_t *reg_data = NULL;
+ uint32_t max_dfx_size = 0;
+ uint32_t real_reg_size;
+ uint32_t version;
+ uint32_t i;
+
+ if (!(g_ub_dfx_param.flag & MODULE_SET_FLAG)) {
+ self->err_no = -EINVAL;
+ snprintf(self->err_str, sizeof(self->err_str), "Please specify a module.");
+ dfx_help_info(self);
+ return;
+ }
+
+ self->err_no = hikp_ub_get_first_blk_dfx(&rsp_head, ®_data, &max_dfx_size, &version);
+ if (self->err_no != 0) {
+ snprintf(self->err_str, sizeof(self->err_str), "get the first block dfx fail.");
+ return;
+ }
+ real_reg_size = (uint32_t)rsp_head.cur_blk_size;
+ for (i = 1; i < rsp_head.total_blk_num; i++) {
+ self->err_no = hikp_ub_get_blk_dfx(&tmp_head, i,
+ reg_data + (real_reg_size / sizeof(uint32_t)),
+ &max_dfx_size);
+ if (self->err_no != 0) {
+ snprintf(self->err_str, sizeof(self->err_str),
+ "getting block%u reg fail.", i);
+ free(reg_data);
+ return;
+ }
+ real_reg_size += (uint32_t)tmp_head.cur_blk_size;
+ memset(&tmp_head, 0, sizeof(struct ub_dfx_rsp_head));
+ }
+
+ printf("DFX cmd version: 0x%x\n\n", version);
+ hikp_ub_dfx_print((const struct ub_dfx_rsp_head *)&rsp_head, reg_data);
+ free(reg_data);
+}
+
+static void cmd_ub_dfx_init(void)
+{
+ struct major_cmd_ctrl *major_cmd = get_major_cmd();
+
+ major_cmd->option_count = 0;
+ major_cmd->execute = hikp_ub_dfx_execute;
+
+ cmd_option_register("-h", "--help", false, hikp_ub_dfx_help);
+ cmd_option_register("-i", "--interface", true, hikp_ub_dfx_target);
+ cmd_option_register("-m", "--module", true, hikp_ub_dfx_module_select);
+}
+
+HIKP_CMD_DECLARE("ub_dfx", "dump ub dfx info of hardware", cmd_ub_dfx_init);
diff --git a/net/ub/ub_dfx/hikp_ub_dfx.h b/net/ub/ub_dfx/hikp_ub_dfx.h
new file mode 100644
index 0000000..4ba37a1
--- /dev/null
+++ b/net/ub/ub_dfx/hikp_ub_dfx.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2023 Hisilicon Technologies Co., Ltd.
+ * Hikptool is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ * http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ *
+ * See the Mulan PSL v2 for more details.
+ */
+
+#ifndef HIKP_UB_DFX_H
+#define HIKP_UB_DFX_H
+
+#include "hikp_net_lib.h"
+
+#define MAX_DFX_DATA_NUM 59
+#define MODULE_SET_FLAG 0x1
+
+enum ub_dfx_cmd_type {
+ LRB_DFX_REG_DUMP = 0,
+ PFA_DFX_REG_DUMP = 1,
+ PM_DFX_REG_DUMP = 2,
+ INVALID_MODULE = 0xFFFFFFFF,
+};
+
+enum ub_dfx_reg_type {
+ INCORRECT_REG_TYPE = 0,
+ TYPE_32_STATS = 1,
+ TYPE_32_RUNNING_STATUS = 2,
+ TYPE_64_STATS = 3,
+ TYPE_INVALID = 255,
+};
+
+#define MAX_TYPE_NAME_LEN 40
+
+enum ub_dfx_reg_width {
+ WIDTH_32_BIT = 32,
+ WIDTH_64_BIT = 64,
+};
+
+struct dfx_type_parse {
+ uint8_t type_id;
+ uint8_t bit_width;
+ uint8_t type_name[MAX_TYPE_NAME_LEN];
+};
+
+struct ub_dfx_param {
+ struct tool_target target;
+ uint32_t sub_cmd_code;
+ uint8_t module_idx;
+ uint8_t flag;
+};
+
+#define MAX_MODULE_NAME_LEN 20
+struct dfx_module_cmd {
+ uint8_t module_name[MAX_MODULE_NAME_LEN];
+ uint32_t sub_cmd_code;
+};
+
+struct ub_dfx_req_para {
+ struct bdf_t bdf;
+ uint8_t block_id;
+};
+
+struct ub_dfx_type_head {
+ uint8_t type_id;
+ uint8_t bit_width;
+ uint8_t reg_num;
+ uint8_t flag;
+};
+
+struct ub_dfx_rsp_head {
+ uint8_t total_blk_num;
+ uint8_t total_type_num;
+ uint8_t cur_blk_size;
+ uint8_t rsvd;
+};
+
+/*********************************************************
+ * All registers are returned as key-value pairs, and divided
+ * into three groups of data.
+ * 1. 32bit regs: R0 bit0~bit15: offset, R1 bit0~bit31: value
+ * 2. 64bit regs: R0 bit0~bit15: offset, R0 bit16~bit31 high16 value, R1 bit0~bit31: low32 value
+ *********************************************************/
+#define DFX_REG_VALUE_OFF 16
+#define DFX_REG_VALUE_MASK 0xFFFF
+#define DFX_REG_ADDR_MASK 0xFFFF
+
+#define WORD_NUM_PER_REG 2
+#define BIT_NUM_OF_WORD 32
+
+struct ub_dfx_rsp {
+ struct ub_dfx_rsp_head rsp_head;
+ uint32_t reg_data[MAX_DFX_DATA_NUM];
+};
+
+#endif /* HIKP_UB_DFX_H */
diff --git a/tool_lib/tool_lib.h b/tool_lib/tool_lib.h
index b211175..bf37465 100644
--- a/tool_lib/tool_lib.h
+++ b/tool_lib/tool_lib.h
@@ -18,7 +18,7 @@
#define TOOL_NAME "hikptool"
-#define TOOL_VER "1.0.14"
+#define TOOL_VER "1.0.15"
#define HI_GET_BITFIELD(value, start, mask) (((value) >> (start)) & (mask))
#define HI_SET_FIELD(origin, shift, val) ((origin) |= (val) << (shift))
--
2.36.1.windows.1
2
21
Baokun Li (1):
xfs: propagate the return value of xfs_log_force() to avoid soft
lockup
Colin Ian King (2):
xfs: remove redundant initializations of pointers drop_leaf and
save_leaf
xfs: remove redundant pointer lip
Darrick J. Wong (9):
xfs: use setattr_copy to set vfs inode attributes
xfs: remove kmem_zone typedef
xfs: rename _zone variables to _cache
xfs: compact deferred intent item structures
xfs: create slab caches for frequently-used deferred items
xfs: rename xfs_bmap_add_free to xfs_free_extent_later
xfs: reduce the size of struct xfs_extent_free_item
xfs: remove unused parameter from refcount code
xfs: pass xfs_extent_free_item directly through the log intent code
Dave Chinner (19):
xfs: don't assert fail on perag references on teardown
xfs: set prealloc flag in xfs_alloc_file_space()
xfs: validity check agbnos on the AGFL
xfs: validate block number being freed before adding to xefi
xfs: don't reverse order of items in bulk AIL insertion
xfs: use deferred frees for btree block freeing
xfs: pass alloc flags through to xfs_extent_busy_flush()
xfs: allow extent free intents to be retried
xfs: don't block in busy flushing when freeing extents
xfs: journal geometry is not properly bounds checked
xfs: AGF length has never been bounds checked
xfs: fix bounds check in xfs_defer_agfl_block()
xfs: block reservation too large for minleft allocation
xfs: punching delalloc extents on write failure is racy
xfs: use byte ranges for write cleanup ranges
xfs,iomap: move delalloc punching to iomap
iomap: buffered write failure should not truncate the page cache
xfs: xfs_bmap_punch_delalloc_range() should take a byte range
xfs: fix off-by-one-block in xfs_discard_folio()
Gaosheng Cui (1):
xfs: remove xfs_setattr_time() declaration
Guo Xuenan (1):
xfs: set minleft correctly for randomly sparse inode allocations
Jiapeng Chong (1):
xfs: Remove redundant assignment to busy
Long Li (6):
xfs: fix dir3 block read verify fail during log recover
Revert "xfs: propagate the return value of xfs_log_force() to avoid
soft lockup"
xfs: xfs_trans_cancel() path must check for log shutdown
xfs: don't verify agf length when log recovery
xfs: shutdown to ensure submits buffers on LSN boundaries
xfs: update the last_sync_lsn with ctx start lsn
yangerkun (4):
xfs: keep growfs sb log item active until ail flush success
xfs: fix xfs shutdown since we reserve more blocks in agfl fixup
xfs: longest free extent no need consider postalloc
xfs: shutdown xfs once inode double free
fs/xfs/kmem.h | 4 -
fs/xfs/libxfs/xfs_alloc.c | 390 +++++++++++++++++++++--------
fs/xfs/libxfs/xfs_alloc.h | 51 +++-
fs/xfs/libxfs/xfs_alloc_btree.c | 2 +-
fs/xfs/libxfs/xfs_attr_leaf.c | 2 -
fs/xfs/libxfs/xfs_bmap.c | 90 +++----
fs/xfs/libxfs/xfs_bmap.h | 37 +--
fs/xfs/libxfs/xfs_bmap_btree.c | 27 +-
fs/xfs/libxfs/xfs_btree.c | 4 +-
fs/xfs/libxfs/xfs_btree.h | 2 +-
fs/xfs/libxfs/xfs_da_btree.c | 6 +-
fs/xfs/libxfs/xfs_da_btree.h | 3 +-
fs/xfs/libxfs/xfs_defer.c | 70 +++++-
fs/xfs/libxfs/xfs_defer.h | 3 +
fs/xfs/libxfs/xfs_ialloc.c | 32 ++-
fs/xfs/libxfs/xfs_ialloc_btree.c | 8 +-
fs/xfs/libxfs/xfs_inode_fork.c | 4 +-
fs/xfs/libxfs/xfs_inode_fork.h | 2 +-
fs/xfs/libxfs/xfs_refcount.c | 56 +++--
fs/xfs/libxfs/xfs_refcount.h | 7 +-
fs/xfs/libxfs/xfs_refcount_btree.c | 11 +-
fs/xfs/libxfs/xfs_rmap.c | 21 +-
fs/xfs/libxfs/xfs_rmap.h | 7 +-
fs/xfs/libxfs/xfs_rmap_btree.c | 2 +-
fs/xfs/libxfs/xfs_sb.c | 56 ++++-
fs/xfs/libxfs/xfs_types.c | 23 ++
fs/xfs/libxfs/xfs_types.h | 2 +
fs/xfs/xfs_aops.c | 32 +--
fs/xfs/xfs_bmap_item.c | 16 +-
fs/xfs/xfs_bmap_item.h | 6 +-
fs/xfs/xfs_bmap_util.c | 19 +-
fs/xfs/xfs_bmap_util.h | 2 +-
fs/xfs/xfs_buf.c | 16 +-
fs/xfs/xfs_buf_item.c | 10 +-
fs/xfs/xfs_buf_item.h | 11 +-
fs/xfs/xfs_buf_item_recover.c | 9 +-
fs/xfs/xfs_dquot.c | 26 +-
fs/xfs/xfs_extent_busy.c | 36 ++-
fs/xfs/xfs_extent_busy.h | 6 +-
fs/xfs/xfs_extfree_item.c | 137 +++++++---
fs/xfs/xfs_extfree_item.h | 6 +-
fs/xfs/xfs_file.c | 8 -
fs/xfs/xfs_icache.c | 8 +-
fs/xfs/xfs_icreate_item.c | 6 +-
fs/xfs/xfs_icreate_item.h | 2 +-
fs/xfs/xfs_inode.c | 2 +-
fs/xfs/xfs_inode.h | 2 +-
fs/xfs/xfs_inode_item.c | 6 +-
fs/xfs/xfs_inode_item.h | 2 +-
fs/xfs/xfs_iomap.c | 292 ++++++++++++++++++---
fs/xfs/xfs_iops.c | 56 +----
fs/xfs/xfs_iops.h | 1 -
fs/xfs/xfs_log.c | 72 +++---
fs/xfs/xfs_log_priv.h | 2 +-
fs/xfs/xfs_log_recover.c | 6 +-
fs/xfs/xfs_mount.c | 12 +-
fs/xfs/xfs_mru_cache.c | 2 +-
fs/xfs/xfs_pnfs.c | 3 +-
fs/xfs/xfs_qm.h | 2 +-
fs/xfs/xfs_refcount_item.c | 16 +-
fs/xfs/xfs_refcount_item.h | 6 +-
fs/xfs/xfs_reflink.c | 7 +-
fs/xfs/xfs_rmap_item.c | 16 +-
fs/xfs/xfs_rmap_item.h | 6 +-
fs/xfs/xfs_super.c | 233 ++++++++---------
fs/xfs/xfs_trans.c | 24 +-
fs/xfs/xfs_trans.h | 2 +-
fs/xfs/xfs_trans_ail.c | 5 +-
fs/xfs/xfs_trans_dquot.c | 4 +-
mm/filemap.c | 1 +
70 files changed, 1358 insertions(+), 700 deletions(-)
--
2.31.1
2
45

28 Sep '23
Hongchen Zhang (13):
LoongArch: fix ls2k500 bmc not work when installing iso
LS7A2000 : Add quirk for OHCI device rev 0x02
PCI: Check if entry->offset already exist for mem resource
PCI: Check if the pci controller can use both CFG0 and CFG1 mode to
access configuration space
PCI: PM: Fix pcie mrrs restoring
pci: fix kabi error caused by pm_suspend_target_state
LoongArch: Fixed some pcie card not scanning properly
pci/quirks: ls7a2000: fix pm transition of devices under pcie port
LS7A2000: PCIE: Fixup GPU card error
pci: fix X server auto probe fail when both ast and etnaviv drm
present
LoongArch: pci root bridige set acpi companion only when not
acpi_disabled.
LoongArch: Fix secondary bridge routing errors
pci: irq: Add early_param pci_irq_limit to limit pci irq numbers
arch/loongarch/pci/acpi.c | 23 ++--
drivers/gpu/drm/loongson/loongson_module.c | 15 +++
drivers/irqchip/irq-loongson-pch-pic.c | 6 +-
drivers/pci/controller/pci-loongson.c | 147 ++++++++++++++++++++-
drivers/pci/msi/msi.c | 25 ++++
drivers/pci/pci.c | 20 ++-
6 files changed, 217 insertions(+), 19 deletions(-)
--
2.33.0
2
14

[PATCH openEuler-1.0-LTS] can: raw: add missing refcount for memory leak fix
by Ziyang Xuan 28 Sep '23
by Ziyang Xuan 28 Sep '23
28 Sep '23
From: Oliver Hartkopp <socketcan(a)hartkopp.net>
mainline inclusion
from mainline-v6.5
commit c275a176e4b69868576e543409927ae75e3a3288
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I852NG
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?…
----------------------------------------
Commit ee8b94c8510c ("can: raw: fix receiver memory leak") introduced
a new reference to the CAN netdevice that has assigned CAN filters.
But this new ro->dev reference did not maintain its own refcount which
lead to another KASAN use-after-free splat found by Eric Dumazet.
This patch ensures a proper refcount for the CAN nedevice.
Fixes: ee8b94c8510c ("can: raw: fix receiver memory leak")
Reported-by: Eric Dumazet <edumazet(a)google.com>
Cc: Ziyang Xuan <william.xuanziyang(a)huawei.com>
Signed-off-by: Oliver Hartkopp <socketcan(a)hartkopp.net>
Link: https://lore.kernel.org/r/20230821144547.6658-3-socketcan@hartkopp.net
Signed-off-by: Jakub Kicinski <kuba(a)kernel.org>
Conflicts:
net/can/raw.c
Signed-off-by: Ziyang Xuan <william.xuanziyang(a)huawei.com>
---
net/can/raw.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/net/can/raw.c b/net/can/raw.c
index 26934174cd42..951d568470c5 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -287,8 +287,10 @@ static void raw_notify(struct raw_sock *ro, unsigned long msg,
case NETDEV_UNREGISTER:
lock_sock(sk);
/* remove current filters & unregister */
- if (ro->bound)
+ if (ro->bound) {
raw_disable_allfilters(dev_net(dev), dev, sk);
+ dev_put(dev);
+ }
if (ro->count > 1)
kfree(ro->filter);
@@ -392,10 +394,12 @@ static int raw_release(struct socket *sock)
/* remove current filters & unregister */
if (ro->bound) {
- if (ro->dev)
+ if (ro->dev) {
raw_disable_allfilters(dev_net(ro->dev), ro->dev, sk);
- else
+ dev_put(ro->dev);
+ } else {
raw_disable_allfilters(sock_net(sk), NULL, sk);
+ }
}
if (ro->count > 1)
@@ -446,10 +450,10 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
goto out;
}
if (dev->type != ARPHRD_CAN) {
- dev_put(dev);
err = -ENODEV;
- goto out;
+ goto out_put_dev;
}
+
if (!(dev->flags & IFF_UP))
notify_enetdown = 1;
@@ -457,7 +461,9 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
/* filters set by default/setsockopt */
err = raw_enable_allfilters(sock_net(sk), dev, sk);
- dev_put(dev);
+ if (err)
+ goto out_put_dev;
+
} else {
ifindex = 0;
@@ -468,18 +474,28 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
if (!err) {
if (ro->bound) {
/* unregister old filters */
- if (ro->dev)
+ if (ro->dev) {
raw_disable_allfilters(dev_net(ro->dev),
ro->dev, sk);
- else
+ /* drop reference to old ro->dev */
+ dev_put(ro->dev);
+ } else {
raw_disable_allfilters(sock_net(sk), NULL, sk);
+ }
}
ro->ifindex = ifindex;
ro->bound = 1;
+ /* bind() ok -> hold a reference for new ro->dev */
ro->dev = dev;
+ if (ro->dev)
+ dev_hold(ro->dev);
}
- out:
+out_put_dev:
+ /* remove potential reference from dev_get_by_index() */
+ if (dev)
+ dev_put(dev);
+out:
release_sock(sk);
rtnl_unlock();
--
2.25.1
2
1
From: veega2022 <zhuweijia(a)huawei.com>
Return -EINVAL when the parameter check fails
Signed-off-by: fangjian <f.fangjian(a)huawei.com>
---
socip/hikp_socip_dumpreg.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/socip/hikp_socip_dumpreg.c b/socip/hikp_socip_dumpreg.c
index 088f5dd..b74dac8 100644
--- a/socip/hikp_socip_dumpreg.c
+++ b/socip/hikp_socip_dumpreg.c
@@ -131,6 +131,7 @@ static void hikp_socip_dumpreg_execute(struct major_cmd_ctrl *self)
struct hikp_cmd_ret *cmd_ret;
if (!check_socip_dumpreg_param()) {
+ self->err_no = -EINVAL;
cmd_socip_dump_help(self, NULL);
return;
}
@@ -141,6 +142,7 @@ static void hikp_socip_dumpreg_execute(struct major_cmd_ctrl *self)
hikp_cmd_init(&req_header, SOCIP_MOD, HIKP_SOCIP_CMD_DUMPREG, param[MODULE_ID_INDEX].val);
cmd_ret = hikp_cmd_alloc(&req_header, &req_data, sizeof(req_data));
if (!cmd_ret) {
+ self->err_no=-EINVAL;
HIKP_ERROR_PRINT("hikp_cmd_alloc\n");
return;
}
--
2.33.0
1
36

28 Sep '23
Hongchen Zhang (13):
LoongArch: fix ls2k500 bmc not work when installing iso
LS7A2000 : Add quirk for OHCI device rev 0x02
PCI: Check if entry->offset already exist for mem resource
PCI: Check if the pci controller can use both CFG0 and CFG1 mode to
access configuration space
PCI: PM: Fix pcie mrrs restoring
pci: fix kabi error caused by pm_suspend_target_state
LoongArch: Fixed some pcie card not scanning properly
pci/quirks: ls7a2000: fix pm transition of devices under pcie port
LS7A2000: PCIE: Fixup GPU card error
pci: fix X server auto probe fail when both ast and etnaviv drm
present
LoongArch: pci root bridige set acpi companion only when not
acpi_disabled.
LoongArch: Fix secondary bridge routing errors
pci: irq: Add early_param pci_irq_limit to limit pci irq numbers
arch/loongarch/pci/acpi.c | 23 ++--
drivers/gpu/drm/loongson/loongson_module.c | 15 +++
drivers/irqchip/irq-loongson-pch-pic.c | 6 +-
drivers/pci/controller/pci-loongson.c | 147 ++++++++++++++++++++-
drivers/pci/msi/msi.c | 25 ++++
drivers/pci/pci.c | 20 ++-
6 files changed, 217 insertions(+), 19 deletions(-)
--
2.33.0
1
13

28 Sep '23
Hongchen Zhang (14):
LoongArch: fix ls2k500 bmc not work when installing iso
LS7A2000 : Add quirk for OHCI device rev 0x02
PCI: Check if entry->offset already exist for mem resource
PCI: Check if the pci controller can use both CFG0 and CFG1 mode to
access configuration space
PCI: PM: Fix pcie mrrs restoring
pci: fix kabi error caused by pm_suspend_target_state
LoongArch: Fixed some pcie card not scanning properly
pci/quirks: ls7a2000: fix pm transition of devices under pcie port
LS7A2000: PCIE: Fixup GPU card error
pci: fix X server auto probe fail when both ast and etnaviv drm
present
LoongArch: pci root bridige set acpi companion only when not
acpi_disabled.
LoongArch: Fix secondary bridge routing errors
pci: irq: Add early_param pci_irq_limit to limit pci irq numbers
irqchip/loongson-pch-pic: 7a1000 int_clear reg must use 64bit write.
arch/loongarch/pci/acpi.c | 23 ++--
drivers/gpu/drm/loongson/loongson_module.c | 15 +++
drivers/irqchip/irq-loongson-pch-pic.c | 22 +--
drivers/pci/controller/pci-loongson.c | 147 ++++++++++++++++++++-
drivers/pci/msi/msi.c | 25 ++++
drivers/pci/pci.c | 20 ++-
6 files changed, 228 insertions(+), 24 deletions(-)
--
2.33.0
1
5

28 Sep '23
Hongchen Zhang (11):
LoongArch: Adapted SECTION_SIZE_BITS with page size
LoongArch: Remove redudant csr save/restore
LoongArch: use 40 bits address space for user
LoongArch: refresh usage of sync
LoongArch: fix SECCOMP test error
LoongArch: Change definition of cpu_relax() for Loongson-3
usb: xhci: add XHCI_NO_SOFT_RETRY quirk for EJ188
net: stmmac: fix potential double free of dma descriptor resources
LoongArch: Remove generic irq migration
irqchip/loongson-pch-pic: 7a1000 int_clear reg must use 64bit write.
LoongArch: defconfig: Enable a large number of configurations
arch/loongarch/Kconfig | 8 +-
arch/loongarch/configs/loongson3_defconfig | 1586 +++++++++++++++--
arch/loongarch/include/asm/atomic.h | 8 +
arch/loongarch/include/asm/cmpxchg.h | 2 +
arch/loongarch/include/asm/futex.h | 2 +
arch/loongarch/include/asm/irq.h | 1 +
arch/loongarch/include/asm/pgtable.h | 7 +-
arch/loongarch/include/asm/sparsemem.h | 2 +-
arch/loongarch/include/asm/stackframe.h | 9 -
arch/loongarch/include/asm/thread_info.h | 1 +
arch/loongarch/include/asm/vdso/processor.h | 11 +-
arch/loongarch/kernel/entry.S | 8 -
arch/loongarch/kernel/irq.c | 36 +
arch/loongarch/kernel/smp.c | 3 +-
arch/loongarch/kernel/switch.S | 6 -
arch/loongarch/mm/pgtable.c | 2 -
drivers/irqchip/irq-loongson-pch-pic.c | 16 +-
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 11 +
drivers/usb/host/xhci-pci.c | 6 +
kernel/irq/Kconfig | 4 +-
20 files changed, 1546 insertions(+), 183 deletions(-)
--
2.33.0
2
12

28 Sep '23
Hongchen Zhang (14):
LoongArch: fix ls2k500 bmc not work when installing iso
LS7A2000 : Add quirk for OHCI device rev 0x02
PCI: Check if entry->offset already exist for mem resource
PCI: Check if the pci controller can use both CFG0 and CFG1 mode to
access configuration space
PCI: PM: Fix pcie mrrs restoring
pci: fix kabi error caused by pm_suspend_target_state
LoongArch: Fixed some pcie card not scanning properly
pci/quirks: ls7a2000: fix pm transition of devices under pcie port
LS7A2000: PCIE: Fixup GPU card error
pci: fix X server auto probe fail when both ast and etnaviv drm
present
LoongArch: pci root bridige set acpi companion only when not
acpi_disabled.
LoongArch: Fix secondary bridge routing errors
pci: irq: Add early_param pci_irq_limit to limit pci irq numbers
irqchip/loongson-pch-pic: 7a1000 int_clear reg must use 64bit write.
arch/loongarch/pci/acpi.c | 23 ++--
drivers/gpu/drm/loongson/loongson_module.c | 15 +++
drivers/irqchip/irq-loongson-pch-pic.c | 22 +--
drivers/pci/controller/pci-loongson.c | 147 ++++++++++++++++++++-
drivers/pci/msi/msi.c | 25 ++++
drivers/pci/pci.c | 20 ++-
6 files changed, 228 insertions(+), 24 deletions(-)
--
2.33.0
2
15

[PATCH openEuler-23.09 00/24] LoongArch: some fix and optimization for LoongArch machine
by Hongchen Zhang 28 Sep '23
by Hongchen Zhang 28 Sep '23
28 Sep '23
Hongchen Zhang (24):
LoongArch: fix ls2k500 bmc not work when installing iso
LoongArch: Adapted SECTION_SIZE_BITS with page size
LoongArch: Remove redudant csr save/restore
LoongArch: use 40 bits address space for user
LoongArch: refresh usage of sync
LoongArch: fix SECCOMP test error
LoongArch: Change definition of cpu_relax() for Loongson-3
LS7A2000 : Add quirk for OHCI device rev 0x02
PCI: Check if entry->offset already exist for mem resource
PCI: Check if the pci controller can use both CFG0 and CFG1 mode to
access configuration space
PCI: PM: Fix pcie mrrs restoring
pci: fix kabi error caused by pm_suspend_target_state
LoongArch: Fixed some pcie card not scanning properly
pci/quirks: ls7a2000: fix pm transition of devices under pcie port
LS7A2000: PCIE: Fixup GPU card error
pci: fix X server auto probe fail when both ast and etnaviv drm
present
LoongArch: pci root bridige set acpi companion only when not
acpi_disabled.
usb: xhci: add XHCI_NO_SOFT_RETRY quirk for EJ188
net: stmmac: fix potential double free of dma descriptor resources
LoongArch: Fix secondary bridge routing errors
LoongArch: Remove generic irq migration
pci: irq: Add early_param pci_irq_limit to limit pci irq numbers
irqchip/loongson-pch-pic: 7a1000 int_clear reg must use 64bit write.
LoongArch: defconfig: Enable a large number of configurations
arch/loongarch/Kconfig | 8 +-
arch/loongarch/configs/loongson3_defconfig | 1586 +++++++++++++++--
arch/loongarch/include/asm/atomic.h | 8 +
arch/loongarch/include/asm/cmpxchg.h | 2 +
arch/loongarch/include/asm/futex.h | 2 +
arch/loongarch/include/asm/irq.h | 1 +
arch/loongarch/include/asm/pgtable.h | 7 +-
arch/loongarch/include/asm/sparsemem.h | 2 +-
arch/loongarch/include/asm/stackframe.h | 9 -
arch/loongarch/include/asm/thread_info.h | 1 +
arch/loongarch/include/asm/vdso/processor.h | 11 +-
arch/loongarch/kernel/entry.S | 8 -
arch/loongarch/kernel/irq.c | 36 +
arch/loongarch/kernel/smp.c | 3 +-
arch/loongarch/kernel/switch.S | 6 -
arch/loongarch/mm/pgtable.c | 2 -
arch/loongarch/pci/acpi.c | 23 +-
drivers/gpu/drm/loongson/loongson_module.c | 15 +
drivers/irqchip/irq-loongson-pch-pic.c | 22 +-
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 11 +
drivers/pci/controller/pci-loongson.c | 147 +-
drivers/pci/msi/msi.c | 25 +
drivers/pci/pci.c | 20 +-
drivers/usb/host/xhci-pci.c | 6 +
kernel/irq/Kconfig | 4 +-
25 files changed, 1763 insertions(+), 202 deletions(-)
--
2.33.0
1
15

[src-openeuler/rdma-core v3 0/5] Support reporting wc as software mode
by Chengchang Tang 26 Sep '23
by Chengchang Tang 26 Sep '23
26 Sep '23
From: Juan Zhou <zhoujuan51(a)h-partners.com>
Chengchang Tang (5):
libhns: Support reporting wc as software mode
libhns: return error when post send in reset state
libhns: separate the initialization steps of lock
libhns: assign doorbell to zero when allocate it
libhns: Fix missing reset notification.
providers/hns/hns_roce_u.c | 4 +
providers/hns/hns_roce_u.h | 14 ++
providers/hns/hns_roce_u_db.c | 2 +
providers/hns/hns_roce_u_hw_v2.c | 272 +++++++++++++++++++++++++++----
providers/hns/hns_roce_u_hw_v2.h | 2 +
providers/hns/hns_roce_u_verbs.c | 147 ++++++++++++++---
6 files changed, 387 insertions(+), 54 deletions(-)
--
2.30.0
1
5
driver inclusion
category: bugfix
bugzilla: https://gitee.com/src-openeuler/rdma-core/issues/I83L7U
----------------------------------------------------------
Currently, the mapping relationship of reset page between kernel mode
and user mode is maintained by driver. If the driver is hot-plugged
(e.g. reset), the memory of the reset page is released by kernel driver,
but the reset page in user mode still points to this released address
which would lead to a UAF.
This patch use the helper rdma_user_mmap_io() to maintain the vma mapping,
rather than driver itself, which remmaps the userspace reset page to an
safe zero page if driver was hot-plugged.
Fixes: e8b1fec497a0 ("RDMA/hns: Kernel notify usr space to stop ring db")
Signed-off-by: Chengchang Tang <tangchengchang(a)huawei.com>
---
drivers/infiniband/hw/hns/hns_roce_main.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c
index 4a16200ab950..2f0a5b2bbc50 100644
--- a/drivers/infiniband/hw/hns/hns_roce_main.c
+++ b/drivers/infiniband/hw/hns/hns_roce_main.c
@@ -675,10 +675,9 @@ static int hns_roce_mmap(struct ib_ucontext *uctx, struct vm_area_struct *vma)
goto out;
}
- ret = remap_pfn_range(vma, vma->vm_start,
- page_to_pfn(hr_dev->reset_page),
- PAGE_SIZE, vma->vm_page_prot);
- goto out;
+ prot = vma->vm_page_prot;
+ pfn = page_to_pfn(hr_dev->reset_page);
+ break;
default:
ret = -EINVAL;
goto out;
--
2.30.0
1
1
LoongArch inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I84IWW
------------------------------------------
Fix the following warning of inspur BMC drm driver:
drivers/gpu/drm/inspur/inspur_drm_drv.c: In function ‘inspur_pci_probe’:
drivers/gpu/drm/inspur/inspur_drm_drv.c:379:29: warning: unused variable ‘priv’ [-Wunused-variable]
379 | struct inspur_drm_private *priv;
| ^~~~
Fixes: b9d65551a3ad ("drm: add inspur drm driver support")
Signed-off-by: Hongchen Zhang <zhanghongchen(a)loongson.cn>
---
drivers/gpu/drm/inspur/inspur_drm_drv.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/inspur/inspur_drm_drv.c b/drivers/gpu/drm/inspur/inspur_drm_drv.c
index d7026e1df167..f615d6d89109 100644
--- a/drivers/gpu/drm/inspur/inspur_drm_drv.c
+++ b/drivers/gpu/drm/inspur/inspur_drm_drv.c
@@ -376,7 +376,7 @@ static int inspur_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int ret = 0;
- struct inspur_drm_private *priv;
+ struct inspur_drm_private __maybe_unused *priv;
struct drm_device *dev;
inspur_remove_framebuffers(pdev);
--
2.33.0
2
1

[PATCH openEuler-1.0-LTS] sched/rt: Fix RT utilization tracking during policy change
by Xia Fukun 26 Sep '23
by Xia Fukun 26 Sep '23
26 Sep '23
From: Vincent Donnefort <vincent.donnefort(a)arm.com>
stable inclusion
from stable-5.10.50
commit c576472a051a9975e2433de6c80ed27acea2d6f9
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I84IKS
CVE: NA
--------------------------------
[ Upstream commit fecfcbc288e9f4923f40fd23ca78a6acdc7fdf6c ]
RT keeps track of the utilization on a per-rq basis with the structure
avg_rt. This utilization is updated during task_tick_rt(),
put_prev_task_rt() and set_next_task_rt(). However, when the current
running task changes its policy, set_next_task_rt() which would usually
take care of updating the utilization when the rq starts running RT tasks,
will not see a such change, leaving the avg_rt structure outdated. When
that very same task will be dequeued later, put_prev_task_rt() will then
update the utilization, based on a wrong last_update_time, leading to a
huge spike in the RT utilization signal.
The signal would eventually recover from this issue after few ms. Even if
no RT tasks are run, avg_rt is also updated in __update_blocked_others().
But as the CPU capacity depends partly on the avg_rt, this issue has
nonetheless a significant impact on the scheduler.
Fix this issue by ensuring a load update when a running task changes
its policy to RT.
Fixes: 371bf427 ("sched/rt: Add rt_rq utilization tracking")
Signed-off-by: Vincent Donnefort <vincent.donnefort(a)arm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz(a)infradead.org>
Reviewed-by: Vincent Guittot <vincent.guittot(a)linaro.org>
Link: https://lore.kernel.org/r/1624271872-211872-2-git-send-email-vincent.donnef…
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
Signed-off-by: Xia Fukun <xiafukun(a)huawei.com>
---
kernel/sched/rt.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 5dff9a6fe2cf..28fc1e341e17 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2242,13 +2242,20 @@ void __init init_sched_rt_class(void)
static void switched_to_rt(struct rq *rq, struct task_struct *p)
{
/*
- * If we are already running, then there's nothing
- * that needs to be done. But if we are not running
- * we may need to preempt the current running task.
- * If that current running task is also an RT task
+ * If we are running, update the avg_rt tracking, as the running time
+ * will now on be accounted into the latter.
+ */
+ if (task_current(rq, p)) {
+ update_rt_rq_load_avg(rq_clock_pelt(rq), rq, 0);
+ return;
+ }
+
+ /*
+ * If we are not running we may need to preempt the current
+ * running task. If that current running task is also an RT task
* then see if we can move to another run queue.
*/
- if (task_on_rq_queued(p) && rq->curr != p) {
+ if (task_on_rq_queued(p)) {
#ifdef CONFIG_SMP
if (p->nr_cpus_allowed > 1 && rq->rt.overloaded)
rt_queue_push_tasks(rq);
--
2.34.1
2
1

26 Sep '23
From: Russell Harmon via samba-technical <samba-technical(a)lists.samba.org>
stable inclusion
from stable-v4.19.293
commit 5a87735675147f848445f05fd1f06168188f91af
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I84IKB
CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id…
--------------------------------
commit 69513dd669e243928f7450893190915a88f84a2b upstream.
Under the current code, when cifs_readpage_worker is called, the call
contract is that the callee should unlock the page. This is documented
in the read_folio section of Documentation/filesystems/vfs.rst as:
> The filesystem should unlock the folio once the read has completed,
> whether it was successful or not.
Without this change, when fscache is in use and cache hit occurs during
a read, the page lock is leaked, producing the following stack on
subsequent reads (via mmap) to the page:
$ cat /proc/3890/task/12864/stack
[<0>] folio_wait_bit_common+0x124/0x350
[<0>] filemap_read_folio+0xad/0xf0
[<0>] filemap_fault+0x8b1/0xab0
[<0>] __do_fault+0x39/0x150
[<0>] do_fault+0x25c/0x3e0
[<0>] __handle_mm_fault+0x6ca/0xc70
[<0>] handle_mm_fault+0xe9/0x350
[<0>] do_user_addr_fault+0x225/0x6c0
[<0>] exc_page_fault+0x84/0x1b0
[<0>] asm_exc_page_fault+0x27/0x30
This requires a reboot to resolve; it is a deadlock.
Note however that the call to cifs_readpage_from_fscache does mark the
page clean, but does not free the folio lock. This happens in
__cifs_readpage_from_fscache on success. Releasing the lock at that
point however is not appropriate as cifs_readahead also calls
cifs_readpage_from_fscache and *does* unconditionally release the lock
after its return. This change therefore effectively makes
cifs_readpage_worker work like cifs_readahead.
Signed-off-by: Russell Harmon <russ(a)har.mn>
Acked-by: Paulo Alcantara (SUSE) <pc(a)manguebit.com>
Reviewed-by: David Howells <dhowells(a)redhat.com>
Cc: stable(a)vger.kernel.org
Signed-off-by: Steve French <stfrench(a)microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Signed-off-by: ZhaoLong Wang <wangzhaolong1(a)huawei.com>
---
fs/cifs/file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index bc3d0d76c2c4..875cb44ba573 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -4005,9 +4005,9 @@ static int cifs_readpage_worker(struct file *file, struct page *page,
io_error:
kunmap(page);
- unlock_page(page);
read_complete:
+ unlock_page(page);
return rc;
}
--
2.34.3
2
1

26 Sep '23
From: Lu Wei <luwei32(a)huawei.com>
maillist inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I84HSL
Reference: https://lore.kernel.org/netdev/CADvbK_euiOKytyFd6KYgNoM5SbDcyz92Li=K7P48H35…
--------------------------------
There are race conditions that may lead to inet6_dev refcount underflow
in xfrm6_dst_destroy() and rt6_uncached_list_flush_dev().
One of the refcount underflow bugs is shown below:
(cpu 1) | (cpu 2)
xfrm6_dst_destroy() |
... |
in6_dev_put() |
| rt6_uncached_list_flush_dev()
... | ...
| in6_dev_put()
rt6_uncached_list_del() | ...
... |
xfrm6_dst_destroy() calls rt6_uncached_list_del() after in6_dev_put(),
so rt6_uncached_list_flush_dev() has a chance to call in6_dev_put()
again for the same inet6_dev.
Fix it by moving in6_dev_put() after rt6_uncached_list_del() in
xfrm6_dst_destroy().
Fixes: 510c321b5571 ("xfrm: reuse uncached_list to track xdsts")
Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com>
Reviewed-by: Xin Long <lucien.xin(a)gmail.com>
Signed-off-by: Lu Wei <luwei32(a)huawei.com>
Signed-off-by: Zhengchao Shao <shaozhengchao(a)huawei.com>
---
net/ipv6/xfrm6_policy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 247296e3294b..4c3aa97f23fa 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -120,11 +120,11 @@ static void xfrm6_dst_destroy(struct dst_entry *dst)
{
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
- if (likely(xdst->u.rt6.rt6i_idev))
- in6_dev_put(xdst->u.rt6.rt6i_idev);
dst_destroy_metrics_generic(dst);
if (xdst->u.rt6.rt6i_uncached_list)
rt6_uncached_list_del(&xdst->u.rt6);
+ if (likely(xdst->u.rt6.rt6i_idev))
+ in6_dev_put(xdst->u.rt6.rt6i_idev);
xfrm_dst_destroy(xdst);
}
--
2.34.1
2
1

[PATCH openEuler-1.0-LTS] xfrm6: fix inet6_dev refcount underflow problem
by Zhengchao Shao 26 Sep '23
by Zhengchao Shao 26 Sep '23
26 Sep '23
From: Lu Wei <luwei32(a)huawei.com>
maillist inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I84HSL
Reference: https://lore.kernel.org/netdev/CADvbK_euiOKytyFd6KYgNoM5SbDcyz92Li=K7P48H35…
--------------------------------
There are race conditions that may lead to inet6_dev refcount underflow
in xfrm6_dst_destroy() and rt6_uncached_list_flush_dev().
One of the refcount underflow bugs is shown below:
(cpu 1) | (cpu 2)
xfrm6_dst_destroy() |
... |
in6_dev_put() |
| rt6_uncached_list_flush_dev()
... | ...
| in6_dev_put()
rt6_uncached_list_del() | ...
... |
xfrm6_dst_destroy() calls rt6_uncached_list_del() after in6_dev_put(),
so rt6_uncached_list_flush_dev() has a chance to call in6_dev_put()
again for the same inet6_dev.
Fix it by moving in6_dev_put() after rt6_uncached_list_del() in
xfrm6_dst_destroy().
Fixes: 510c321b5571 ("xfrm: reuse uncached_list to track xdsts")
Signed-off-by: Zhang Changzhong <zhangchangzhong(a)huawei.com>
Reviewed-by: Xin Long <lucien.xin(a)gmail.com>
Signed-off-by: Lu Wei <luwei32(a)huawei.com>
Signed-off-by: Zhengchao Shao <shaozhengchao(a)huawei.com>
---
net/ipv6/xfrm6_policy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 30232591cf2b..cfe650cddeb6 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -243,11 +243,11 @@ static void xfrm6_dst_destroy(struct dst_entry *dst)
{
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
- if (likely(xdst->u.rt6.rt6i_idev))
- in6_dev_put(xdst->u.rt6.rt6i_idev);
dst_destroy_metrics_generic(dst);
if (xdst->u.rt6.rt6i_uncached_list)
rt6_uncached_list_del(&xdst->u.rt6);
+ if (likely(xdst->u.rt6.rt6i_idev))
+ in6_dev_put(xdst->u.rt6.rt6i_idev);
xfrm_dst_destroy(xdst);
}
--
2.34.1
2
1
Baokun Li (1):
xfs: propagate the return value of xfs_log_force() to avoid soft
lockup
Colin Ian King (2):
xfs: remove redundant initializations of pointers drop_leaf and
save_leaf
xfs: remove redundant pointer lip
Darrick J. Wong (9):
xfs: use setattr_copy to set vfs inode attributes
xfs: remove kmem_zone typedef
xfs: rename _zone variables to _cache
xfs: compact deferred intent item structures
xfs: create slab caches for frequently-used deferred items
xfs: rename xfs_bmap_add_free to xfs_free_extent_later
xfs: reduce the size of struct xfs_extent_free_item
xfs: remove unused parameter from refcount code
xfs: pass xfs_extent_free_item directly through the log intent code
Dave Chinner (19):
xfs: don't assert fail on perag references on teardown
xfs: set prealloc flag in xfs_alloc_file_space()
xfs: validity check agbnos on the AGFL
xfs: validate block number being freed before adding to xefi
xfs: don't reverse order of items in bulk AIL insertion
xfs: use deferred frees for btree block freeing
xfs: pass alloc flags through to xfs_extent_busy_flush()
xfs: allow extent free intents to be retried
xfs: don't block in busy flushing when freeing extents
xfs: journal geometry is not properly bounds checked
xfs: AGF length has never been bounds checked
xfs: fix bounds check in xfs_defer_agfl_block()
xfs: block reservation too large for minleft allocation
xfs: punching delalloc extents on write failure is racy
xfs: use byte ranges for write cleanup ranges
xfs,iomap: move delalloc punching to iomap
iomap: buffered write failure should not truncate the page cache
xfs: xfs_bmap_punch_delalloc_range() should take a byte range
xfs: fix off-by-one-block in xfs_discard_folio()
Gaosheng Cui (1):
xfs: remove xfs_setattr_time() declaration
Guo Xuenan (1):
xfs: set minleft correctly for randomly sparse inode allocations
Jiapeng Chong (1):
xfs: Remove redundant assignment to busy
Long Li (6):
xfs: fix dir3 block read verify fail during log recover
Revert "[Huawei] xfs: propagate the return value of xfs_log_force() to
avoid soft lockup"
xfs: xfs_trans_cancel() path must check for log shutdown
xfs: don't verify agf length when log recovery
xfs: shutdown to ensure submits buffers on LSN boundaries
xfs: update the last_sync_lsn with ctx start lsn
yangerkun (4):
xfs: keep growfs sb log item active until ail flush success
xfs: fix xfs shutdown since we reserve more blocks in agfl fixup
xfs: longest free extent no need consider postalloc
xfs: shutdown xfs once inode double free
fs/xfs/kmem.h | 4 -
fs/xfs/libxfs/xfs_alloc.c | 390 +++++++++++++++++++++--------
fs/xfs/libxfs/xfs_alloc.h | 51 +++-
fs/xfs/libxfs/xfs_alloc_btree.c | 2 +-
fs/xfs/libxfs/xfs_attr_leaf.c | 2 -
fs/xfs/libxfs/xfs_bmap.c | 90 +++----
fs/xfs/libxfs/xfs_bmap.h | 37 +--
fs/xfs/libxfs/xfs_bmap_btree.c | 27 +-
fs/xfs/libxfs/xfs_btree.c | 4 +-
fs/xfs/libxfs/xfs_btree.h | 2 +-
fs/xfs/libxfs/xfs_da_btree.c | 6 +-
fs/xfs/libxfs/xfs_da_btree.h | 3 +-
fs/xfs/libxfs/xfs_defer.c | 70 +++++-
fs/xfs/libxfs/xfs_defer.h | 3 +
fs/xfs/libxfs/xfs_ialloc.c | 32 ++-
fs/xfs/libxfs/xfs_ialloc_btree.c | 8 +-
fs/xfs/libxfs/xfs_inode_fork.c | 4 +-
fs/xfs/libxfs/xfs_inode_fork.h | 2 +-
fs/xfs/libxfs/xfs_refcount.c | 56 +++--
fs/xfs/libxfs/xfs_refcount.h | 7 +-
fs/xfs/libxfs/xfs_refcount_btree.c | 11 +-
fs/xfs/libxfs/xfs_rmap.c | 21 +-
fs/xfs/libxfs/xfs_rmap.h | 7 +-
fs/xfs/libxfs/xfs_rmap_btree.c | 2 +-
fs/xfs/libxfs/xfs_sb.c | 56 ++++-
fs/xfs/libxfs/xfs_types.c | 23 ++
fs/xfs/libxfs/xfs_types.h | 2 +
fs/xfs/xfs_aops.c | 32 +--
fs/xfs/xfs_bmap_item.c | 16 +-
fs/xfs/xfs_bmap_item.h | 6 +-
fs/xfs/xfs_bmap_util.c | 19 +-
fs/xfs/xfs_bmap_util.h | 2 +-
fs/xfs/xfs_buf.c | 16 +-
fs/xfs/xfs_buf_item.c | 10 +-
fs/xfs/xfs_buf_item.h | 11 +-
fs/xfs/xfs_buf_item_recover.c | 9 +-
fs/xfs/xfs_dquot.c | 26 +-
fs/xfs/xfs_extent_busy.c | 36 ++-
fs/xfs/xfs_extent_busy.h | 6 +-
fs/xfs/xfs_extfree_item.c | 137 +++++++---
fs/xfs/xfs_extfree_item.h | 6 +-
fs/xfs/xfs_file.c | 8 -
fs/xfs/xfs_icache.c | 8 +-
fs/xfs/xfs_icreate_item.c | 6 +-
fs/xfs/xfs_icreate_item.h | 2 +-
fs/xfs/xfs_inode.c | 2 +-
fs/xfs/xfs_inode.h | 2 +-
fs/xfs/xfs_inode_item.c | 6 +-
fs/xfs/xfs_inode_item.h | 2 +-
fs/xfs/xfs_iomap.c | 292 ++++++++++++++++++---
fs/xfs/xfs_iops.c | 56 +----
fs/xfs/xfs_iops.h | 1 -
fs/xfs/xfs_log.c | 72 +++---
fs/xfs/xfs_log_priv.h | 2 +-
fs/xfs/xfs_log_recover.c | 6 +-
fs/xfs/xfs_mount.c | 12 +-
fs/xfs/xfs_mru_cache.c | 2 +-
fs/xfs/xfs_pnfs.c | 3 +-
fs/xfs/xfs_qm.h | 2 +-
fs/xfs/xfs_refcount_item.c | 16 +-
fs/xfs/xfs_refcount_item.h | 6 +-
fs/xfs/xfs_reflink.c | 7 +-
fs/xfs/xfs_rmap_item.c | 16 +-
fs/xfs/xfs_rmap_item.h | 6 +-
fs/xfs/xfs_super.c | 233 ++++++++---------
fs/xfs/xfs_trans.c | 24 +-
fs/xfs/xfs_trans.h | 2 +-
fs/xfs/xfs_trans_ail.c | 5 +-
fs/xfs/xfs_trans_dquot.c | 4 +-
69 files changed, 1357 insertions(+), 700 deletions(-)
--
2.31.1
2
45

[PATCH openEuler-22.03-LTS-SP2] scsi: lpfc: Fix ioremap issues in lpfc_sli4_pci_mem_setup()
by Yong Hu 26 Sep '23
by Yong Hu 26 Sep '23
26 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 openEuler-22.03-LTS] scsi: lpfc: Fix ioremap issues in lpfc_sli4_pci_mem_setup()
by Yong Hu 26 Sep '23
by Yong Hu 26 Sep '23
26 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 134e4ee5dc48..2f7a17e96e25 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -10474,7 +10474,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;
}
}
@@ -10492,7 +10492,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;
}
@@ -10517,9 +10517,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 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: 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

[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