kernel/0004-add_enfs_module_for_sunrpc_multipatch.patch

1861 lines
46 KiB
Diff

diff --git a/fs/nfs/enfs/enfs_multipath.c b/fs/nfs/enfs/enfs_multipath.c
new file mode 100644
index 000000000..d49dc91e1
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#include "enfs_multipath.h"
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/kallsyms.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/atomic.h>
+#include <linux/sunrpc/addr.h>
+#include <linux/sunrpc/bc_xprt.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/xprtmultipath.h>
+#include <linux/types.h>
+#include <linux/un.h>
+#include <linux/utsname.h>
+#include <linux/workqueue.h>
+#include <trace/events/sunrpc.h>
+#include <linux/sunrpc/sunrpc_enfs_adapter.h>
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_multipath_parse.h"
+#include "enfs_path.h"
+#include "enfs_proc.h"
+#include "enfs_remount.h"
+#include "enfs_roundrobin.h"
+#include "failover_path.h"
+#include "failover_time.h"
+#include "pm_ping.h"
+#include "pm_state.h"
+
+struct xprt_attach_callback_data {
+ atomic_t *conditon;
+ wait_queue_head_t *waitq;
+};
+
+struct xprt_attach_info {
+ struct sockaddr_storage *localAddress;
+ struct sockaddr_storage *remoteAddress;
+ struct rpc_xprt *xprt;
+ struct xprt_attach_callback_data *data;
+};
+
+static DECLARE_WAIT_QUEUE_HEAD(path_attach_wait_queue);
+
+/**
+ * set socket port.
+ * @ns: need transform to network byte order
+ */
+static void sockaddr_set_port(struct sockaddr *addr, __be16 port, bool ns)
+{
+ switch (addr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)addr)->sin_port =
+ ns ? htons(port) : port;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)addr)->sin6_port =
+ ns ? htons(port) : port;
+ break;
+ }
+}
+
+static __be16 get_rpc_clnt_port(struct rpc_clnt *clnt)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr *addr = (struct sockaddr *)&ss;
+
+ rpc_peeraddr(clnt, (struct sockaddr *)&ss, sizeof(ss));
+ switch (addr->sa_family) {
+ case AF_INET:
+ return ((struct sockaddr_in *)addr)->sin_port;
+
+ case AF_INET6:
+ return ((struct sockaddr_in6 *)addr)->sin6_port;
+
+ default:
+ enfs_log_error("not support family:%d.\n", addr->sa_family);
+ return -1;
+ }
+}
+
+static int sockaddr_ip_to_str(struct sockaddr *addr, char *buf, int len)
+{
+ switch (addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+
+ snprintf(buf, len, "%pI4", &sin->sin_addr);
+ return 0;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+
+ snprintf(buf, len, "%pI6", &sin6->sin6_addr);
+ return 0;
+ }
+ default:
+ break;
+ }
+ return 1;
+}
+
+void print_enfs_multipath_addr(struct sockaddr *local, struct sockaddr *remote)
+{
+ char buf1[128];
+ char buf2[128];
+
+ sockaddr_ip_to_str(local, buf1, sizeof(buf1));
+ sockaddr_ip_to_str(remote, buf2, sizeof(buf2));
+
+ pr_info("local:%s remote:%s\n", buf1, buf2);
+}
+
+static int enfs_servername(char *servername, unsigned long long len,
+ struct rpc_create_args *args)
+{
+ struct sockaddr_un *sun = (struct sockaddr_un *)args->address;
+ struct sockaddr_in *sin = (struct sockaddr_in *)args->address;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)args->address;
+
+ servername[0] = '\0';
+ switch (args->address->sa_family) {
+ case AF_LOCAL:
+ snprintf(servername, len, "%s", sun->sun_path);
+ break;
+ case AF_INET:
+ snprintf(servername, len, "%pI4", &sin->sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ snprintf(servername, len, "%pI6", &sin6->sin6_addr);
+ break;
+ default:
+ pr_info("invalid family:%d\n",
+ args->address->sa_family);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void pm_xprt_ping_callback(void *data)
+{
+ struct xprt_attach_callback_data *attach_callback_data =
+ (struct xprt_attach_callback_data *)data;
+
+ atomic_dec(attach_callback_data->conditon);
+ wake_up(attach_callback_data->waitq);
+}
+
+static
+int enfs_add_xprt_setup(struct rpc_clnt *clnt, struct rpc_xprt_switch *xps,
+ struct rpc_xprt *xprt, void *data)
+{
+ int ret;
+ struct enfs_xprt_context *ctx;
+ struct xprt_attach_info *attach_info = data;
+ struct sockaddr_storage *srcaddr = attach_info->localAddress;
+
+ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+ ctx->stats = rpc_alloc_iostats(clnt);
+ ctx->main = false;
+ ctx->srcaddr = *srcaddr;
+ pm_set_path_state(xprt, PM_STATE_INIT);
+ pm_ping_set_path_check_state(xprt, PM_CHECK_INIT);
+
+ attach_info->xprt = xprt;
+ xprt_get(xprt);
+
+ ret = pm_ping_rpc_test_xprt_with_callback(clnt, xprt,
+ pm_xprt_ping_callback, attach_info->data);
+ if (ret != 1) {
+ enfs_log_error("Failed to add ping task.\n");
+ atomic_dec(attach_info->data->conditon);
+ }
+
+ // so that rpc_clnt_add_xprt
+ // does not call rpc_xprt_switch_add_xprt
+ return 1;
+}
+
+int enfs_configure_xprt_to_clnt(struct xprt_create *xprtargs,
+ struct rpc_clnt *clnt,
+ struct xprt_attach_info *attach_info)
+{
+ int err = 0;
+ __be16 port;
+
+ xprtargs->srcaddr = (struct sockaddr *)attach_info->localAddress;
+ xprtargs->dstaddr = (struct sockaddr *)attach_info->remoteAddress;
+
+ port = get_rpc_clnt_port(clnt);
+ sockaddr_set_port((struct sockaddr *)attach_info->remoteAddress, port,
+ false);
+ print_enfs_multipath_addr((struct sockaddr *)attach_info->localAddress,
+ (struct sockaddr *)attach_info->remoteAddress);
+
+ err = rpc_clnt_add_xprt(clnt, xprtargs,
+ enfs_add_xprt_setup,
+ attach_info);
+ if (err != 1) {
+ enfs_log_error("clnt add xprt err:%d\n", err);
+ return err;
+ }
+ return 0;
+}
+
+// Calculate the greatest common divisor of two numbers
+static int enfs_cal_gcd(int num1, int num2)
+{
+ if (num2 == 0)
+ return num1;
+ return enfs_cal_gcd(num2, num1 % num2);
+}
+
+bool enfs_cmp_addrs(struct sockaddr_storage *srcaddr,
+ struct sockaddr_storage *dstaddr,
+ struct sockaddr_storage *localAddress,
+ struct sockaddr_storage *remoteAddress)
+{
+ if (rpc_cmp_addr((struct sockaddr *)srcaddr,
+ (struct sockaddr *)localAddress)) {
+
+ if (rpc_cmp_addr((struct sockaddr *)dstaddr,
+ (struct sockaddr *)remoteAddress))
+ return true;
+
+ }
+
+ return false;
+}
+
+bool enfs_xprt_addrs_is_same(struct rpc_xprt *xprt,
+ struct sockaddr_storage *localAddress,
+ struct sockaddr_storage *remoteAddress)
+{
+ struct enfs_xprt_context *xprt_local_info = NULL;
+ struct sockaddr_storage *srcaddr = NULL;
+ struct sockaddr_storage *dstaddr = NULL;
+
+ if (xprt == NULL)
+ return true;
+ xprt_local_info = (struct enfs_xprt_context *)xprt->multipath_context;
+ srcaddr = &xprt_local_info->srcaddr;
+ dstaddr = &xprt->addr;
+
+ return enfs_cmp_addrs(srcaddr, dstaddr, localAddress, remoteAddress);
+}
+
+bool enfs_already_have_xprt(struct rpc_clnt *clnt,
+ struct sockaddr_storage *localAddress,
+ struct sockaddr_storage *remoteAddress)
+{
+ struct rpc_xprt *pos = NULL;
+ struct rpc_xprt_switch *xps = NULL;
+
+ rcu_read_lock();
+ xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
+ if (xps == NULL) {
+ rcu_read_unlock();
+ return false;
+ }
+ list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+ if (enfs_xprt_addrs_is_same(pos, localAddress, remoteAddress)) {
+ xprt_switch_put(xps);
+ rcu_read_unlock();
+ return true;
+ }
+ }
+ xprt_switch_put(xps);
+ rcu_read_unlock();
+ return false;
+}
+
+static void enfs_xprt_switch_add_xprt(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt)
+{
+ struct rpc_xprt_switch *xps;
+
+ rcu_read_lock();
+ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+ spin_lock(&xps->xps_lock);
+ if (xps->xps_net == xprt->xprt_net || xps->xps_net == NULL)
+ xprt_switch_add_xprt_locked(xps, xprt);
+ spin_unlock(&xps->xps_lock);
+ rcu_read_unlock();
+}
+
+static void enfs_add_xprts_to_clnt(struct rpc_clnt *clnt,
+ struct xprt_attach_info *attach_infos,
+ int total_combinations)
+{
+ struct rpc_xprt *xprt;
+ enum pm_path_state state;
+ int i;
+ int link_count = 0;
+
+ for (i = 0; i < total_combinations; i++) {
+ xprt = attach_infos[i].xprt;
+
+ if (xprt == NULL)
+ continue;
+
+ state = pm_get_path_state(xprt);
+
+ if (link_count < MAX_XPRT_NUM_PER_CLIENT &&
+ state == PM_STATE_NORMAL) {
+
+ enfs_xprt_switch_add_xprt(clnt, xprt);
+ link_count++;
+ continue;
+
+ }
+
+ xprt_put(xprt);
+ }
+}
+
+static void enfs_combine_addr(struct xprt_create *xprtargs,
+ struct rpc_clnt *clnt,
+ struct nfs_ip_list *local,
+ struct nfs_ip_list *remote)
+{
+ int i;
+ int err;
+ int local_index;
+ int remote_index;
+ int link_count = 0;
+ int local_total = local->count;
+ int remote_total = remote->count;
+ int local_remote_total_lcm;
+ int total_combinations = local_total * remote_total;
+ struct xprt_attach_info *attach_infos;
+ atomic_t wait_queue_condition;
+
+ struct xprt_attach_callback_data attach_callback_data = {
+ &wait_queue_condition, &path_attach_wait_queue};
+
+ atomic_set(&wait_queue_condition, total_combinations);
+
+ pr_info("local count:%d remote count:%d\n", local_total, remote_total);
+ if (local_total == 0 || remote_total == 0) {
+ pr_err("no ip list is present.\n");
+ return;
+ }
+
+ attach_infos = kzalloc(
+ (total_combinations) * sizeof(struct xprt_attach_info),
+ GFP_KERNEL);
+ if (attach_infos == NULL) {
+ enfs_log_error("Failed to kzalloc memory\n");
+ return;
+ }
+ // Calculate the least common multiple of local_total and remote_total
+ local_remote_total_lcm = total_combinations /
+ enfs_cal_gcd(local_total, remote_total);
+
+ // It needs to be offset one for lcm times of
+ // cycle so that all possible link setup method would be traversed
+ for (i = 0; i < total_combinations; i++) {
+ local_index = i % local_total;
+ remote_index = (i + link_count / local_remote_total_lcm) %
+ remote_total;
+
+ if (enfs_already_have_xprt(clnt,
+ &local->address[local_index],
+ &remote->address[remote_index])) {
+
+ atomic_dec(&wait_queue_condition);
+ link_count++;
+ continue;
+
+ }
+
+ attach_infos[i].localAddress = &local->address[local_index];
+ attach_infos[i].remoteAddress = &remote->address[remote_index];
+ attach_infos[i].data = &attach_callback_data;
+
+ err = enfs_configure_xprt_to_clnt(xprtargs,
+ clnt, &attach_infos[i]);
+ if (err) {
+ pr_err("add xprt ippair err:%d\n", err);
+ atomic_dec(&wait_queue_condition);
+ }
+ link_count++;
+ }
+
+ wait_event(path_attach_wait_queue,
+ atomic_read(&wait_queue_condition) == 0);
+
+ enfs_add_xprts_to_clnt(clnt, attach_infos, total_combinations);
+
+ kfree(attach_infos);
+}
+
+void enfs_xprt_ippair_create(struct xprt_create *xprtargs,
+ struct rpc_clnt *clnt,
+ void *data)
+{
+ struct multipath_mount_options *mopt =
+ (struct multipath_mount_options *)data;
+
+ if (mopt == NULL) {
+ pr_err("ip list is NULL.\n");
+ return;
+ }
+
+ enfs_combine_addr(xprtargs, clnt,
+ mopt->local_ip_list,
+ mopt->remote_ip_list);
+ enfs_lb_set_policy(clnt);
+}
+
+struct xprts_options_and_clnt {
+ struct rpc_create_args *args;
+ struct rpc_clnt *clnt;
+ void *data;
+};
+
+static void set_clnt_enfs_flag(struct rpc_clnt *clnt)
+{
+ clnt->cl_enfs = true;
+}
+
+int enfs_config_xprt_create_args(struct xprt_create *xprtargs,
+ struct rpc_create_args *args,
+ char *servername, size_t length)
+{
+ int errno = 0;
+
+ xprtargs->ident = args->protocol;
+ xprtargs->net = args->net;
+ xprtargs->addrlen = args->addrsize;
+ xprtargs->servername = args->servername;
+
+ if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS)
+ xprtargs->flags |= XPRT_CREATE_INFINITE_SLOTS;
+ if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT)
+ xprtargs->flags |= XPRT_CREATE_NO_IDLE_TIMEOUT;
+
+ if (xprtargs->servername == NULL) {
+ errno = enfs_servername(servername, length, args);
+ if (errno)
+ return errno;
+ xprtargs->servername = servername;
+ }
+
+ return 0;
+}
+
+int enfs_multipath_create_thread(void *data)
+{
+ int errno;
+ char servername[48];
+ struct xprts_options_and_clnt *create_args =
+ (struct xprts_options_and_clnt *)data;
+ struct multipath_mount_options *mount_options =
+ (struct multipath_mount_options *)
+ create_args->args->multipath_option;
+ struct xprt_create xprtargs;
+
+ memset(&xprtargs, 0, sizeof(struct xprt_create));
+
+ if (mount_options == NULL) {
+ enfs_log_error("enfs: local and remot addr empty\n");
+ return -EINVAL;
+ }
+
+ errno = enfs_config_xprt_create_args(&xprtargs,
+ create_args->args, servername,
+ sizeof(servername));
+ if (errno) {
+ enfs_log_error("enfs: create xprt failed! errno:%d\n",
+ errno);
+ return errno;
+ }
+
+ //mount : localaddrs or remoteaddrs is empty
+ if (mount_options->local_ip_list->count == 0) {
+ errno = rpc_localaddr(create_args->clnt,
+ (struct sockaddr *)
+ &mount_options->local_ip_list->address[0],
+ sizeof(struct sockaddr_storage));
+ if (errno) {
+ enfs_log_error("enfs: get clnt srcaddr errno:%d\n",
+ errno);
+ return errno;
+ }
+ mount_options->local_ip_list->count = 1;
+ }
+
+ if (mount_options->remote_ip_list->count == 0) {
+ errno = rpc_peeraddr(create_args->clnt,
+ (struct sockaddr *)
+ &mount_options->remote_ip_list->address[0],
+ sizeof(struct sockaddr_storage));
+ if (errno == 0) {
+ enfs_log_error("enfs: get clnt dstaddr errno:%d\n",
+ errno);
+ return errno;
+ }
+ mount_options->remote_ip_list->count = 1;
+ }
+
+ errno = enfs_proc_create_clnt(create_args->clnt);
+ if (errno != 0)
+ pr_err("create clnt proc failed.\n");
+
+ set_clnt_enfs_flag(create_args->clnt);
+ enfs_xprt_ippair_create(&xprtargs, create_args->clnt, mount_options);
+
+ kfree(create_args->args);
+ kfree(data);
+ return 0;
+}
+
+static int set_main_xprt_ctx(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+ struct sockaddr_storage *srcaddr)
+{
+ struct enfs_xprt_context *ctx = xprt->multipath_context;
+
+ if (!ctx) {
+ enfs_log_error("main xprt not multipath ctx.\n");
+ return -1;
+ }
+
+ ctx->main = true;
+ ctx->stats = rpc_alloc_iostats(clnt);
+ ctx->srcaddr = *srcaddr;
+ pm_set_path_state(xprt, PM_STATE_NORMAL);
+ pm_ping_set_path_check_state(xprt, PM_CHECK_INIT);
+
+ return 0;
+}
+
+static int alloc_main_xprt_multicontext(struct rpc_create_args *args,
+ struct rpc_clnt *clnt)
+{
+ int err;
+ struct sockaddr_storage srcaddr;
+
+ // avoid main xprt multicontex local addr is empty.
+ err = rpc_localaddr(clnt, (struct sockaddr *)&srcaddr, sizeof(srcaddr));
+ if (err) {
+ enfs_log_error("get clnt localaddr err:%d\n", err);
+ return err;
+ }
+
+ err = set_main_xprt_ctx(clnt, clnt->cl_xprt, &srcaddr);
+ if (err)
+ enfs_log_error("alloc main xprt failed.\n");
+
+ return err;
+}
+
+void enfs_create_multi_xprt(struct rpc_create_args *args, struct rpc_clnt *clnt)
+{
+ // struct task_struct *th;
+ struct xprts_options_and_clnt *thargs;
+ struct rpc_create_args *cargs;
+ int err;
+
+ enfs_log_info("%s %p\n", __func__, clnt);
+
+ cargs = kmalloc(sizeof(struct rpc_create_args), GFP_KERNEL);
+ if (cargs == NULL)
+ return;
+
+ *cargs = *args;
+
+ thargs = kmalloc(sizeof(struct xprts_options_and_clnt), GFP_KERNEL);
+ if (thargs == NULL) {
+ kfree(cargs);
+ return;
+ }
+
+ alloc_main_xprt_multicontext(args, clnt);
+
+ thargs->args = cargs;
+ thargs->clnt = clnt;
+ thargs->data = args->multipath_option;
+
+ err = enfs_multipath_create_thread(thargs);
+
+ if (err != 0) {
+ kfree(cargs);
+ kfree(thargs);
+ }
+}
+
+static void enfs_create_xprt_ctx(struct rpc_xprt *xprt)
+{
+ int err;
+
+ err = enfs_alloc_xprt_ctx(xprt);
+ if (err)
+ enfs_log_error("alloc xprt failed.\n");
+}
+
+static struct rpc_multipath_ops ops = {
+ .owner = THIS_MODULE,
+ .create_clnt = enfs_create_multi_xprt,
+ .releas_clnt = enfs_proc_delete_clnt,
+ .create_xprt = enfs_create_xprt_ctx,
+ .destroy_xprt = enfs_free_xprt_ctx,
+ .xprt_iostat = enfs_count_iostat,
+ .failover_handle = failover_handle,
+ .task_need_call_start_again = failover_task_need_call_start_again,
+ .adjust_task_timeout = failover_adjust_task_timeout,
+ .init_task_req = failover_init_task_req,
+ .prepare_transmit = failover_prepare_transmit,
+};
+
+int enfs_multipath_init(void)
+{
+ int err;
+
+ enfs_log_info("multipath init.\n");
+
+ err = pm_ping_init();
+ if (err != 0) {
+ enfs_log_error("pm ping init err:%d\n", err);
+ return err;
+ }
+
+ err = enfs_proc_init();
+ if (err != 0) {
+ enfs_log_error("enfs proc init err:%d\n", err);
+ pm_ping_fini();
+ return err;
+ }
+
+ rpc_multipath_ops_register(&ops);
+
+ return 0;
+}
+
+void enfs_multipath_exit(void)
+{
+ enfs_log_info("multipath exit.\n");
+ rpc_multipath_ops_unregister(&ops);
+ enfs_proc_exit();
+ pm_ping_fini();
+}
diff --git a/fs/nfs/enfs/enfs_multipath.h b/fs/nfs/enfs/enfs_multipath.h
new file mode 100644
index 000000000..3c58b0c4c
--- /dev/null
+++ b/fs/nfs/enfs/enfs_multipath.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Description: enfs multipath
+ * Create: 2023-07-31
+ */
+
+#ifndef ENFS_MULTIPATH_H
+#define ENFS_MULTIPATH_H
+#include <linux/sunrpc/clnt.h>
+
+#define MAX_XPRT_NUM_PER_CLIENT 32
+
+int enfs_multipath_init(void);
+void enfs_multipath_exit(void);
+void enfs_xprt_ippair_create(struct xprt_create *xprtargs,
+ struct rpc_clnt *clnt, void *data);
+int enfs_config_xprt_create_args(struct xprt_create *xprtargs,
+ struct rpc_create_args *args,
+ char *servername, size_t length);
+void print_enfs_multipath_addr(struct sockaddr *local, struct sockaddr *remote);
+
+#endif // ENFS_MULTIPATH_H
diff --git a/fs/nfs/enfs/enfs_path.c b/fs/nfs/enfs/enfs_path.c
new file mode 100644
index 000000000..7355f8c2f
--- /dev/null
+++ b/fs/nfs/enfs/enfs_path.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/xprt.h>
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_path.h"
+
+// only create ctx in this function
+// alloc iostat memory in create_clnt
+int enfs_alloc_xprt_ctx(struct rpc_xprt *xprt)
+{
+ struct enfs_xprt_context *ctx;
+
+ if (!xprt) {
+ enfs_log_error("invalid xprt pointer.\n");
+ return -EINVAL;
+ }
+
+ ctx = kzalloc(sizeof(struct enfs_xprt_context), GFP_KERNEL);
+ if (!ctx) {
+ enfs_log_error("add xprt test failed.\n");
+ return -ENOMEM;
+ }
+
+ xprt->multipath_context = (void *)ctx;
+ return 0;
+}
+
+// free multi_context and iostat memory
+void enfs_free_xprt_ctx(struct rpc_xprt *xprt)
+{
+ struct enfs_xprt_context *ctx = xprt->multipath_context;
+
+ if (ctx) {
+ if (ctx->stats) {
+ rpc_free_iostats(ctx->stats);
+ ctx->stats = NULL;
+ }
+ kfree(xprt->multipath_context);
+ xprt->multipath_context = NULL;
+ }
+}
diff --git a/fs/nfs/enfs/enfs_path.h b/fs/nfs/enfs/enfs_path.h
new file mode 100644
index 000000000..97b1ef373
--- /dev/null
+++ b/fs/nfs/enfs/enfs_path.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+
+#ifndef ENFS_PATH_H
+#define ENFS_PATH_H
+
+int enfs_alloc_xprt_ctx(struct rpc_xprt *xprt);
+void enfs_free_xprt_ctx(struct rpc_xprt *xprt);
+
+#endif // ENFS_PATH_H
diff --git a/fs/nfs/enfs/enfs_proc.c b/fs/nfs/enfs/enfs_proc.c
new file mode 100644
index 000000000..53fa1a076
--- /dev/null
+++ b/fs/nfs/enfs/enfs_proc.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/xprtsock.h>
+#include <net/netns/generic.h>
+
+#include "../../../net/sunrpc/netns.h"
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_proc.h"
+#include "enfs_multipath.h"
+#include "pm_state.h"
+
+#define ENFS_PROC_DIR "enfs"
+#define ENFS_PROC_PATH_STATUS_LEN 256
+
+static struct proc_dir_entry *enfs_proc_parent;
+
+void
+enfs_iterate_each_rpc_clnt(int (*fn)(struct rpc_clnt *clnt, void *data),
+ void *data)
+{
+ struct net *net;
+ struct sunrpc_net *sn;
+ struct rpc_clnt *clnt;
+
+ rcu_read_lock();
+ for_each_net_rcu(net) {
+ sn = net_generic(net, sunrpc_net_id);
+ if (sn == NULL)
+ continue;
+ spin_lock(&sn->rpc_client_lock);
+ list_for_each_entry(clnt, &sn->all_clients, cl_clients) {
+ fn(clnt, data);
+ }
+ spin_unlock(&sn->rpc_client_lock);
+ }
+ rcu_read_unlock();
+}
+
+struct proc_dir_entry *enfs_get_proc_parent(void)
+{
+ return enfs_proc_parent;
+}
+
+static int sockaddr_ip_to_str(struct sockaddr *addr, char *buf, int len)
+{
+ switch (addr->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+
+ snprintf(buf, len, "%pI4", &sin->sin_addr);
+ return 0;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
+
+ snprintf(buf, len, "%pI6", &sin6->sin6_addr);
+ return 0;
+ }
+ default:
+ break;
+ }
+ return 1;
+}
+
+static bool should_print(const char *name)
+{
+ int i;
+ static const char * const proc_names[] = {
+ "READ",
+ "WRITE",
+ };
+
+ if (name == NULL)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(proc_names); i++) {
+ if (strcmp(name, proc_names[i]) == 0)
+ return true;
+ }
+ return false;
+}
+
+struct enfs_xprt_iter {
+ unsigned int id;
+ struct seq_file *seq;
+ unsigned int max_addrs_length;
+};
+
+static int debug_show_xprt(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt,
+ void *data)
+{
+ struct enfs_xprt_context *ctx = NULL;
+
+ if (xprt->multipath_context)
+ ctx = xprt->multipath_context;
+
+ pr_info(" xprt:%p ctx:%p main:%d queue_len:%lu.\n", xprt,
+ xprt->multipath_context,
+ ctx ? ctx->main : false,
+ atomic_long_read(&xprt->queuelen));
+ return 0;
+}
+
+static int debug_show_clnt(struct rpc_clnt *clnt, void *data)
+{
+ pr_info(" clnt %d addr:%p enfs:%d\n",
+ clnt->cl_clid, clnt,
+ clnt->cl_enfs);
+ rpc_clnt_iterate_for_each_xprt(clnt, debug_show_xprt, NULL);
+ return 0;
+}
+
+static void debug_print_all_xprt(void)
+{
+ enfs_iterate_each_rpc_clnt(debug_show_clnt, NULL);
+}
+
+static
+void enfs_proc_format_xprt_addr_display(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt,
+ char *local_name_buf,
+ int local_name_buf_len,
+ char *remote_name_buf,
+ int remote_name_buf_len)
+{
+ int err;
+ struct sockaddr_storage srcaddr;
+ struct enfs_xprt_context *ctx;
+
+ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+ sockaddr_ip_to_str((struct sockaddr *)&xprt->addr,
+ remote_name_buf, remote_name_buf_len);
+
+ // get local address depend one main or not
+ if (enfs_is_main_xprt(xprt)) {
+ err = rpc_localaddr(clnt, (struct sockaddr *)&srcaddr,
+ sizeof(srcaddr));
+ if (err != 0)
+ (void)snprintf(local_name_buf,
+ local_name_buf_len, "Unknown");
+ else
+ sockaddr_ip_to_str((struct sockaddr *)&srcaddr,
+ local_name_buf,
+ local_name_buf_len);
+ } else {
+ sockaddr_ip_to_str((struct sockaddr *)&ctx->srcaddr,
+ local_name_buf,
+ local_name_buf_len);
+ }
+}
+
+static int enfs_show_xprt_stats(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt,
+ void *data)
+{
+ unsigned int op;
+ unsigned int maxproc = clnt->cl_maxproc;
+ struct enfs_xprt_iter *iter = (struct enfs_xprt_iter *)data;
+ struct enfs_xprt_context *ctx;
+ char local_name[INET6_ADDRSTRLEN];
+ char remote_name[INET6_ADDRSTRLEN];
+
+ if (!xprt->multipath_context)
+ return 0;
+
+ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+ enfs_proc_format_xprt_addr_display(clnt, xprt, local_name,
+ sizeof(local_name),
+ remote_name, sizeof(remote_name));
+
+ seq_printf(iter->seq, "%-6u%-*s%-*s", iter->id,
+ iter->max_addrs_length + 4,
+ local_name,
+ iter->max_addrs_length + 4,
+ remote_name);
+
+ iter->id++;
+
+ for (op = 0; op < maxproc; op++) {
+ if (!should_print(clnt->cl_procinfo[op].p_name))
+ continue;
+
+ seq_printf(iter->seq, "%-22lu%-22Lu%-22Lu",
+ ctx->stats[op].om_ops,
+ ctx->stats[op].om_ops == 0 ? 0 :
+ ktime_to_ms(ctx->stats[op].om_rtt) /
+ ctx->stats[op].om_ops,
+ ctx->stats[op].om_ops == 0 ? 0 :
+ ktime_to_ms(ctx->stats[op].om_execute) /
+ ctx->stats[op].om_ops);
+ }
+ seq_puts(iter->seq, "\n");
+ return 0;
+}
+
+static int rpc_proc_show_path_status(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt,
+ void *data)
+{
+ struct enfs_xprt_iter *iter = (struct enfs_xprt_iter *)data;
+ struct enfs_xprt_context *ctx = NULL;
+ char local_name[INET6_ADDRSTRLEN] = {0};
+ char remote_name[INET6_ADDRSTRLEN] = {0};
+ char multiapth_status[ENFS_PROC_PATH_STATUS_LEN] = {0};
+ char xprt_status[ENFS_PROC_PATH_STATUS_LEN] = {0};
+
+ if (!xprt->multipath_context) {
+ enfs_log_debug("multipath_context is null.\n");
+ return 0;
+ }
+ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+ enfs_proc_format_xprt_addr_display(clnt, xprt,
+ local_name,
+ sizeof(local_name),
+ remote_name, sizeof(remote_name));
+
+ pm_get_path_state_desc(xprt,
+ multiapth_status,
+ ENFS_PROC_PATH_STATUS_LEN);
+
+ pm_get_xprt_state_desc(xprt,
+ xprt_status,
+ ENFS_PROC_PATH_STATUS_LEN);
+
+ seq_printf(iter->seq, "%-6u%-*s%-*s%-12s%-12s\n",
+ iter->id, iter->max_addrs_length + 4,
+ local_name, iter->max_addrs_length + 4,
+ remote_name, multiapth_status,
+ xprt_status);
+ iter->id++;
+ return 0;
+}
+
+static int enfs_get_max_addrs_length(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt,
+ void *data)
+{
+ struct enfs_xprt_iter *iter = (struct enfs_xprt_iter *)data;
+ char local_name[INET6_ADDRSTRLEN];
+ char remote_name[INET6_ADDRSTRLEN];
+
+ enfs_proc_format_xprt_addr_display(clnt, xprt,
+ local_name, sizeof(local_name),
+ remote_name, sizeof(remote_name));
+
+ if (iter->max_addrs_length < strlen(local_name))
+ iter->max_addrs_length = strlen(local_name);
+
+ if (iter->max_addrs_length < strlen(remote_name))
+ iter->max_addrs_length = strlen(remote_name);
+
+ return 0;
+}
+
+static int rpc_proc_clnt_showpath(struct seq_file *seq, void *v)
+{
+ struct rpc_clnt *clnt = seq->private;
+ struct enfs_xprt_iter iter;
+
+ iter.seq = seq;
+ iter.id = 0;
+ iter.max_addrs_length = 0;
+
+ rpc_clnt_iterate_for_each_xprt(clnt,
+ enfs_get_max_addrs_length,
+ (void *)&iter);
+
+ seq_printf(seq, "%-6s%-*s%-*s%-12s%-12s\n", "id",
+ iter.max_addrs_length + 4,
+ "local_addr",
+ iter.max_addrs_length + 4,
+ "remote_addr",
+ "path_state",
+ "xprt_state");
+
+ rpc_clnt_iterate_for_each_xprt(clnt,
+ rpc_proc_show_path_status,
+ (void *)&iter);
+ return 0;
+}
+
+static int enfs_rpc_proc_show(struct seq_file *seq, void *v)
+{
+ struct rpc_clnt *clnt = seq->private;
+ struct enfs_xprt_iter iter;
+
+ iter.seq = seq;
+ iter.id = 0;
+ iter.max_addrs_length = 0;
+
+ debug_print_all_xprt();
+ pr_info("enfs proc clnt:%p\n", clnt);
+
+ rpc_clnt_iterate_for_each_xprt(clnt,
+ enfs_get_max_addrs_length,
+ (void *)&iter);
+
+ seq_printf(seq, "%-6s%-*s%-*s%-22s%-22s%-22s%-22s%-22s%-22s\n", "id",
+ iter.max_addrs_length + 4, "local_addr",
+ iter.max_addrs_length + 4,
+ "remote_addr", "r_count",
+ "r_rtt", "r_exec", "w_count", "w_rtt", "w_exec");
+
+ // rpc_clnt_show_stats(seq, clnt);
+ rpc_clnt_iterate_for_each_xprt(clnt,
+ enfs_show_xprt_stats,
+ (void *)&iter);
+ return 0;
+}
+
+static int rpc_proc_open(struct inode *inode, struct file *file)
+{
+ struct rpc_clnt *clnt = PDE_DATA(inode);
+
+ pr_info("%s %p\n", __func__, clnt);
+ return single_open(file, enfs_rpc_proc_show, clnt);
+}
+
+static int enfs_reset_xprt_stats(struct rpc_clnt *clnt,
+ struct rpc_xprt *xprt,
+ void *data)
+{
+ unsigned int op;
+ struct enfs_xprt_context *ctx;
+ unsigned int maxproc = clnt->cl_maxproc;
+ struct rpc_iostats stats = {0};
+
+ if (!xprt->multipath_context)
+ return 0;
+ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+
+ for (op = 0; op < maxproc; op++) {
+ spin_lock(&ctx->stats[op].om_lock);
+ ctx->stats[op] = stats;
+ spin_unlock(&ctx->stats[op].om_lock);
+ }
+ return 0;
+}
+
+static void trim_newline_ch(char *str, int len)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0' && i < len; i++) {
+ if (str[i] == '\n')
+ str[i] = '\0';
+ }
+}
+
+static ssize_t enfs_proc_write(struct file *file,
+ const char __user *user_buf,
+ size_t len,
+ loff_t *offset)
+{
+ char buffer[128];
+ struct rpc_clnt *clnt =
+ ((struct seq_file *)file->private_data)->private;
+
+ if (len >= sizeof(buffer))
+ return -E2BIG;
+
+ if (copy_from_user(buffer, user_buf, len) != 0)
+ return -EFAULT;
+
+ buffer[len] = '\0';
+ trim_newline_ch(buffer, len);
+ if (strcmp(buffer, "reset") != 0)
+ return -EINVAL;
+
+ rpc_clnt_iterate_for_each_xprt(clnt, enfs_reset_xprt_stats, NULL);
+ return len;
+}
+
+static int rpc_proc_show_path(struct inode *inode, struct file *file)
+{
+ struct rpc_clnt *clnt = PDE_DATA(inode);
+
+ return single_open(file, rpc_proc_clnt_showpath, clnt);
+}
+
+static const struct file_operations rpc_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = rpc_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = enfs_proc_write,
+};
+
+static const struct file_operations rpc_show_path_fops = {
+ .owner = THIS_MODULE,
+ .open = rpc_proc_show_path,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int clnt_proc_name(struct rpc_clnt *clnt, char *buf, int len)
+{
+ int ret;
+
+ ret = snprintf(buf, len, "%s_%u",
+ rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR),
+ clnt->cl_clid);
+ if (ret > len)
+ return -E2BIG;
+ return 0;
+}
+
+static int enfs_proc_create_file(struct rpc_clnt *clnt)
+{
+ int err;
+ char buf[128];
+
+ struct proc_dir_entry *clnt_entry;
+ struct proc_dir_entry *stat_entry;
+
+ err = clnt_proc_name(clnt, buf, sizeof(buf));
+ if (err)
+ return err;
+
+ clnt_entry = proc_mkdir(buf, enfs_proc_parent);
+ if (clnt_entry == NULL)
+ return -EINVAL;
+
+ stat_entry = proc_create_data("stat",
+ 0, clnt_entry,
+ &rpc_proc_fops, clnt);
+
+ if (stat_entry == NULL)
+ return -EINVAL;
+
+ stat_entry = proc_create_data("path",
+ 0, clnt_entry,
+ &rpc_show_path_fops, clnt);
+
+ if (stat_entry == NULL)
+ return -EINVAL;
+
+ return 0;
+}
+
+void enfs_count_iostat(struct rpc_task *task)
+{
+ struct enfs_xprt_context *ctx = task->tk_xprt->multipath_context;
+
+ if (!ctx || !ctx->stats)
+ return;
+ rpc_count_iostats(task, ctx->stats);
+}
+
+static void enfs_proc_delete_file(struct rpc_clnt *clnt)
+{
+ int err;
+ char buf[128];
+
+ err = clnt_proc_name(clnt, buf, sizeof(buf));
+ if (err) {
+ pr_err("gen clnt name failed.\n");
+ return;
+ }
+ remove_proc_subtree(buf, enfs_proc_parent);
+}
+
+// create proc file "/porc/enfs/[mount_ip]_[id]/stat"
+int enfs_proc_create_clnt(struct rpc_clnt *clnt)
+{
+ int err;
+
+ err = enfs_proc_create_file(clnt);
+ if (err) {
+ pr_err("create client %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+void enfs_proc_delete_clnt(struct rpc_clnt *clnt)
+{
+ if (clnt->cl_enfs)
+ enfs_proc_delete_file(clnt);
+}
+
+static int enfs_proc_create_parent(void)
+{
+ enfs_proc_parent = proc_mkdir(ENFS_PROC_DIR, NULL);
+
+ if (enfs_proc_parent == NULL) {
+ pr_err("Enfs create proc dir err\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void enfs_proc_delete_parent(void)
+{
+ remove_proc_entry(ENFS_PROC_DIR, NULL);
+}
+
+static int enfs_proc_init_create_clnt(struct rpc_clnt *clnt, void *data)
+{
+ if (clnt->cl_enfs)
+ enfs_proc_create_file(clnt);
+ return 0;
+}
+
+static int enfs_proc_destroy_clnt(struct rpc_clnt *clnt, void *data)
+{
+ if (clnt->cl_enfs)
+ enfs_proc_delete_file(clnt);
+ return 0;
+}
+
+int enfs_proc_init(void)
+{
+ int err;
+
+ err = enfs_proc_create_parent();
+ if (err)
+ return err;
+
+ enfs_iterate_each_rpc_clnt(enfs_proc_init_create_clnt, NULL);
+ return 0;
+}
+
+void enfs_proc_exit(void)
+{
+ enfs_iterate_each_rpc_clnt(enfs_proc_destroy_clnt, NULL);
+ enfs_proc_delete_parent();
+}
diff --git a/fs/nfs/enfs/enfs_proc.h b/fs/nfs/enfs/enfs_proc.h
new file mode 100644
index 000000000..321951031
--- /dev/null
+++ b/fs/nfs/enfs/enfs_proc.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Client-side ENFS PROC.
+ *
+ * Copyright (c) 2023. Huawei Technologies Co., Ltd. All rights reserved.
+ */
+#ifndef ENFS_PROC_H
+#define ENFS_PROC_H
+
+struct rpc_clnt;
+struct rpc_task;
+struct proc_dir_entry;
+
+int enfs_proc_init(void);
+void enfs_proc_exit(void);
+struct proc_dir_entry *enfs_get_proc_parent(void);
+int enfs_proc_create_clnt(struct rpc_clnt *clnt);
+void enfs_proc_delete_clnt(struct rpc_clnt *clnt);
+void enfs_count_iostat(struct rpc_task *task);
+
+#endif
diff --git a/fs/nfs/enfs/enfs_remount.c b/fs/nfs/enfs/enfs_remount.c
new file mode 100644
index 000000000..3b7d6a03d
--- /dev/null
+++ b/fs/nfs/enfs/enfs_remount.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Description: remount ip source file
+ * Create: 2023-08-12
+ */
+#include "enfs_remount.h"
+
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/spinlock.h>
+#include <linux/sunrpc/addr.h>
+#include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/xprtmultipath.h>
+#include <linux/sunrpc/xprtsock.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+
+#include "enfs.h"
+#include "enfs_log.h"
+#include "enfs_multipath.h"
+#include "enfs_multipath_parse.h"
+#include "enfs_path.h"
+#include "enfs_proc.h"
+#include "enfs_multipath_client.h"
+
+static bool enfs_rpc_xprt_switch_need_delete_addr(
+ struct multipath_mount_options *enfs_option,
+ struct sockaddr *dstaddr, struct sockaddr *srcaddr)
+{
+ int i;
+ bool find_same_ip = false;
+ int32_t local_total;
+ int32_t remote_total;
+
+ local_total = enfs_option->local_ip_list->count;
+ remote_total = enfs_option->remote_ip_list->count;
+ if (local_total == 0 || remote_total == 0) {
+ pr_err("no ip list is present.\n");
+ return false;
+ }
+
+ for (i = 0; i < local_total; i++) {
+ find_same_ip =
+ rpc_cmp_addr((struct sockaddr *)
+ &enfs_option->local_ip_list->address[i],
+ srcaddr);
+ if (find_same_ip)
+ break;
+ }
+
+ if (find_same_ip == false)
+ return true;
+
+ find_same_ip = false;
+ for (i = 0; i < remote_total; i++) {
+ find_same_ip =
+ rpc_cmp_addr((struct sockaddr *)
+ &enfs_option->remote_ip_list->address[i],
+ dstaddr);
+ if (find_same_ip)
+ break;
+ }
+
+ if (find_same_ip == false)
+ return true;
+
+ return false;
+}
+
+// Used in rcu_lock
+static bool enfs_delete_xprt_from_switch(struct rpc_xprt *xprt,
+ void *enfs_option,
+ struct rpc_xprt_switch *xps)
+{
+ struct enfs_xprt_context *ctx = NULL;
+ struct multipath_mount_options *mopt =
+ (struct multipath_mount_options *)enfs_option;
+
+ if (enfs_is_main_xprt(xprt))
+ return true;
+
+ ctx = (struct enfs_xprt_context *)xprt->multipath_context;
+ if (enfs_rpc_xprt_switch_need_delete_addr(mopt,
+ (struct sockaddr *)&xprt->addr,
+ (struct sockaddr *)&ctx->srcaddr)) {
+
+ print_enfs_multipath_addr((struct sockaddr *)&ctx->srcaddr,
+ (struct sockaddr *)&xprt->addr);
+ rpc_xprt_switch_remove_xprt(xps, xprt);
+ return true;
+ }
+
+ return false;
+}
+
+void enfs_clnt_delete_obsolete_xprts(struct nfs_client *nfs_client,
+ void *enfs_option)
+{
+ int xprt_count = 0;
+ struct rpc_xprt *pos = NULL;
+ struct rpc_xprt_switch *xps = NULL;
+
+ rcu_read_lock();
+ xps = xprt_switch_get(
+ rcu_dereference(
+ nfs_client->cl_rpcclient->cl_xpi.xpi_xpswitch));
+ if (xps == NULL) {
+ rcu_read_unlock();
+ xprt_switch_put(xps);
+ return;
+ }
+ list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+ if (xprt_count < MAX_XPRT_NUM_PER_CLIENT) {
+ if (enfs_delete_xprt_from_switch(
+ pos, enfs_option, xps) == false)
+ xprt_count++;
+ } else
+ rpc_xprt_switch_remove_xprt(xps, pos);
+ }
+ rcu_read_unlock();
+ xprt_switch_put(xps);
+}
+
+int enfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option)
+{
+ int errno = 0;
+ char servername[48];
+ struct multipath_mount_options *remount_lists =
+ (struct multipath_mount_options *)enfs_option;
+ struct multipath_client_info *client_info =
+ (struct multipath_client_info *)nfs_client->cl_multipath_data;
+ struct xprt_create xprtargs;
+ struct rpc_create_args args = {
+ .protocol = nfs_client->cl_proto,
+ .net = nfs_client->cl_net,
+ .addrsize = nfs_client->cl_addrlen,
+ .servername = nfs_client->cl_hostname,
+ };
+
+ memset(&xprtargs, 0, sizeof(struct xprt_create));
+
+ //mount is not use multipath
+ if (client_info == NULL || enfs_option == NULL) {
+ enfs_log_error(
+ "mount information or remount information is empty.\n");
+ return -EINVAL;
+ }
+
+ //remount : localaddrs and remoteaddrs are empty
+ if (remount_lists->local_ip_list->count == 0 &&
+ remount_lists->remote_ip_list->count == 0) {
+ enfs_log_info("remount local_ip_list and remote_ip_list are NULL\n");
+ return 0;
+ }
+
+ errno = enfs_config_xprt_create_args(&xprtargs,
+ &args, servername, sizeof(servername));
+
+ if (errno) {
+ enfs_log_error("config_xprt_create failed! errno:%d\n", errno);
+ return errno;
+ }
+
+ if (remount_lists->local_ip_list->count == 0) {
+ if (client_info->local_ip_list->count == 0) {
+ errno = rpc_localaddr(nfs_client->cl_rpcclient,
+ (struct sockaddr *)
+ &remount_lists->local_ip_list->address[0],
+ sizeof(struct sockaddr_storage));
+ if (errno) {
+ enfs_log_error("get clnt srcaddr errno:%d\n",
+ errno);
+ return errno;
+ }
+ remount_lists->local_ip_list->count = 1;
+ } else
+ memcpy(remount_lists->local_ip_list,
+ client_info->local_ip_list,
+ sizeof(struct nfs_ip_list));
+ }
+
+ if (remount_lists->remote_ip_list->count == 0) {
+ if (client_info->remote_ip_list->count == 0) {
+ errno = rpc_peeraddr(nfs_client->cl_rpcclient,
+ (struct sockaddr *)
+ &remount_lists->remote_ip_list->address[0],
+ sizeof(struct sockaddr_storage));
+ if (errno == 0) {
+ enfs_log_error("get clnt dstaddr errno:%d\n",
+ errno);
+ return errno;
+ }
+ remount_lists->remote_ip_list->count = 1;
+ } else
+ memcpy(remount_lists->remote_ip_list,
+ client_info->remote_ip_list,
+ sizeof(struct nfs_ip_list));
+ }
+
+ enfs_log_info("Remount creating new links...\n");
+ enfs_xprt_ippair_create(&xprtargs,
+ nfs_client->cl_rpcclient,
+ remount_lists);
+
+ enfs_log_info("Remount deleting obsolete links...\n");
+ enfs_clnt_delete_obsolete_xprts(nfs_client, remount_lists);
+
+ memcpy(client_info->local_ip_list,
+ remount_lists->local_ip_list,
+ sizeof(struct nfs_ip_list));
+ memcpy(client_info->remote_ip_list,
+ remount_lists->remote_ip_list,
+ sizeof(struct nfs_ip_list));
+
+ return 0;
+}
diff --git a/fs/nfs/enfs/enfs_remount.h b/fs/nfs/enfs/enfs_remount.h
new file mode 100644
index 000000000..c7a1dcbb6
--- /dev/null
+++ b/fs/nfs/enfs/enfs_remount.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ * Description: remount ip header file
+ * Create: 2023-08-12
+ */
+#ifndef _ENFS_REMOUNT_
+#define _ENFS_REMOUNT_
+#include <linux/string.h>
+#include "enfs.h"
+
+int enfs_remount_iplist(struct nfs_client *nfs_client, void *enfs_option);
+
+#endif
diff --git a/fs/nfs/enfs/enfs_roundrobin.c b/fs/nfs/enfs/enfs_roundrobin.c
new file mode 100644
index 000000000..4e4eda784
--- /dev/null
+++ b/fs/nfs/enfs/enfs_roundrobin.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/kref.h>
+#include <linux/rculist.h>
+#include <linux/types.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprtmultipath.h>
+#include "enfs_roundrobin.h"
+
+#include "enfs.h"
+#include "enfs_config.h"
+#include "pm_state.h"
+
+typedef struct rpc_xprt *(*enfs_xprt_switch_find_xprt_t)(
+ struct rpc_xprt_switch *xps, const struct rpc_xprt *cur);
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_roundrobin;
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_singular;
+
+static bool enfs_xprt_is_active(struct rpc_xprt *xprt)
+{
+ enum pm_path_state state;
+
+ if (kref_read(&xprt->kref) <= 0)
+ return false;
+
+ state = pm_get_path_state(xprt);
+ if (state == PM_STATE_NORMAL)
+ return true;
+
+ return false;
+}
+
+static struct rpc_xprt *enfs_lb_set_cursor_xprt(
+ struct rpc_xprt_switch *xps, struct rpc_xprt **cursor,
+ enfs_xprt_switch_find_xprt_t find_next)
+{
+ struct rpc_xprt *pos;
+ struct rpc_xprt *old;
+
+ old = smp_load_acquire(cursor); /* read latest cursor */
+ pos = find_next(xps, old);
+ smp_store_release(cursor, pos); /* let cursor point to pos */
+ return pos;
+}
+
+static
+struct rpc_xprt *enfs_lb_find_next_entry_roundrobin(
+ struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+ struct rpc_xprt *pos;
+ struct rpc_xprt *prev = NULL;
+ bool found = false;
+ struct rpc_xprt *min_queuelen_xprt = NULL;
+ unsigned long pos_xprt_queuelen;
+ unsigned long min_xprt_queuelen = 0;
+
+ unsigned long xps_queuelen = atomic_long_read(&xps->xps_queuelen);
+ // delete origin xprt
+ unsigned int multipath_nactive = READ_ONCE(xps->xps_nactive) - 1;
+
+ list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+ if (enfs_is_main_xprt(pos) || !enfs_xprt_is_active(pos)) {
+ prev = pos;
+ continue;
+ }
+
+ pos_xprt_queuelen = atomic_long_read(&pos->queuelen);
+ if (min_queuelen_xprt == NULL ||
+ pos_xprt_queuelen < min_xprt_queuelen) {
+
+ min_queuelen_xprt = pos;
+ min_xprt_queuelen = pos_xprt_queuelen;
+ }
+
+ if (cur == prev)
+ found = true;
+
+ if (found && pos_xprt_queuelen *
+ multipath_nactive <= xps_queuelen)
+ return pos;
+ prev = pos;
+ };
+
+ return min_queuelen_xprt;
+}
+
+struct rpc_xprt *enfs_lb_switch_find_first_active_xprt(
+ struct rpc_xprt_switch *xps)
+{
+ struct rpc_xprt *pos;
+
+ list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+ if (enfs_xprt_is_active(pos))
+ return pos;
+ };
+ return NULL;
+}
+
+struct rpc_xprt *enfs_lb_switch_get_main_xprt(struct rpc_xprt_switch *xps)
+{
+ return list_first_or_null_rcu(&xps->xps_xprt_list,
+ struct rpc_xprt, xprt_switch);
+}
+
+static struct rpc_xprt *enfs_lb_switch_get_next_xprt_roundrobin(
+ struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+ struct rpc_xprt *xprt;
+
+ // disable multipath
+ if (enfs_get_config_multipath_state())
+ return enfs_lb_switch_get_main_xprt(xps);
+
+ xprt = enfs_lb_find_next_entry_roundrobin(xps, cur);
+ if (xprt != NULL)
+ return xprt;
+
+ return enfs_lb_switch_get_main_xprt(xps);
+}
+
+static
+struct rpc_xprt *enfs_lb_iter_next_entry_roundrobin(struct rpc_xprt_iter *xpi)
+{
+ struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
+
+ if (xps == NULL)
+ return NULL;
+
+ return enfs_lb_set_cursor_xprt(xps, &xpi->xpi_cursor,
+ enfs_lb_switch_get_next_xprt_roundrobin);
+}
+
+static
+struct rpc_xprt *enfs_lb_switch_find_singular_entry(
+ struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+ struct rpc_xprt *pos;
+ bool found = false;
+
+ list_for_each_entry_rcu(pos, &xps->xps_xprt_list, xprt_switch) {
+ if (cur == pos)
+ found = true;
+
+ if (found && enfs_xprt_is_active(pos))
+ return pos;
+ }
+ return NULL;
+}
+
+struct rpc_xprt *enfs_lb_get_singular_xprt(
+ struct rpc_xprt_switch *xps, const struct rpc_xprt *cur)
+{
+ struct rpc_xprt *xprt;
+
+ if (xps == NULL)
+ return NULL;
+
+ // disable multipath
+ if (enfs_get_config_multipath_state())
+ return enfs_lb_switch_get_main_xprt(xps);
+
+ if (cur == NULL || xps->xps_nxprts < 2)
+ return enfs_lb_switch_find_first_active_xprt(xps);
+
+ xprt = enfs_lb_switch_find_singular_entry(xps, cur);
+ if (!xprt)
+ return enfs_lb_switch_get_main_xprt(xps);
+
+ return xprt;
+}
+
+static
+struct rpc_xprt *enfs_lb_iter_next_entry_sigular(struct rpc_xprt_iter *xpi)
+{
+ struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
+
+ if (xps == NULL)
+ return NULL;
+
+ return enfs_lb_set_cursor_xprt(xps, &xpi->xpi_cursor,
+ enfs_lb_get_singular_xprt);
+}
+
+static void enfs_lb_iter_default_rewind(struct rpc_xprt_iter *xpi)
+{
+ WRITE_ONCE(xpi->xpi_cursor, NULL);
+}
+
+static void enfs_lb_switch_set_roundrobin(struct rpc_clnt *clnt)
+{
+ struct rpc_xprt_switch *xps;
+
+ rcu_read_lock();
+ xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+ rcu_read_unlock();
+ if (clnt->cl_vers == 3) {
+
+ if (READ_ONCE(xps->xps_iter_ops) != &enfs_xprt_iter_roundrobin)
+ WRITE_ONCE(xps->xps_iter_ops,
+ &enfs_xprt_iter_roundrobin);
+
+ return;
+ }
+ if (READ_ONCE(xps->xps_iter_ops) != &enfs_xprt_iter_singular)
+ WRITE_ONCE(xps->xps_iter_ops, &enfs_xprt_iter_singular);
+}
+
+static
+struct rpc_xprt *enfs_lb_switch_find_current(struct list_head *head,
+ const struct rpc_xprt *cur)
+{
+ struct rpc_xprt *pos;
+
+ list_for_each_entry_rcu(pos, head, xprt_switch) {
+ if (cur == pos)
+ return pos;
+ }
+ return NULL;
+}
+
+static struct rpc_xprt *enfs_lb_iter_current_entry(struct rpc_xprt_iter *xpi)
+{
+ struct rpc_xprt_switch *xps = rcu_dereference(xpi->xpi_xpswitch);
+ struct list_head *head;
+
+ if (xps == NULL)
+ return NULL;
+ head = &xps->xps_xprt_list;
+ if (xpi->xpi_cursor == NULL || xps->xps_nxprts < 2)
+ return enfs_lb_switch_get_main_xprt(xps);
+ return enfs_lb_switch_find_current(head, xpi->xpi_cursor);
+}
+
+void enfs_lb_set_policy(struct rpc_clnt *clnt)
+{
+ enfs_lb_switch_set_roundrobin(clnt);
+}
+
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_roundrobin = {
+ .xpi_rewind = enfs_lb_iter_default_rewind,
+ .xpi_xprt = enfs_lb_iter_current_entry,
+ .xpi_next = enfs_lb_iter_next_entry_roundrobin,
+};
+
+static const struct rpc_xprt_iter_ops enfs_xprt_iter_singular = {
+ .xpi_rewind = enfs_lb_iter_default_rewind,
+ .xpi_xprt = enfs_lb_iter_current_entry,
+ .xpi_next = enfs_lb_iter_next_entry_sigular,
+};
diff --git a/fs/nfs/enfs/enfs_roundrobin.h b/fs/nfs/enfs/enfs_roundrobin.h
new file mode 100644
index 000000000..b72b088a6
--- /dev/null
+++ b/fs/nfs/enfs/enfs_roundrobin.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
+ */
+#ifndef ENFS_ROUNDROBIN_H
+#define ENFS_ROUNDROBIN_H
+
+void enfs_lb_set_policy(struct rpc_clnt *clnt);
+#endif