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