
--- fs/lockd/clntlock.c | 1 + fs/lockd/host.c | 11 +- fs/nfs/Kconfig | 11 ++ fs/nfs/Makefile | 5 +- fs/nfs/client.c | 28 +++- fs/nfs/dir.c | 11 +- fs/nfs/enfs/Makefile | 6 + fs/nfs/enfs_adapter.c | 292 +++++++++++++++++++++++++++++++++++ fs/nfs/enfs_adapter.h | 62 ++++++++ fs/nfs/fs_context.c | 50 +++++- fs/nfs/internal.h | 3 + fs/nfs/nfs3xdr.c | 66 ++++++++ fs/nfs/nfs4client.c | 27 +++- fs/nfs/nfs4proc.c | 14 +- fs/nfs/nfs4state.c | 64 +++++++- fs/nfs/super.c | 17 +- include/linux/lockd/lockd.h | 1 + include/linux/nfs_fs_sb.h | 13 ++ include/linux/nfs_xdr.h | 6 + include/linux/sunrpc/clnt.h | 53 +++++++ include/linux/sunrpc/sched.h | 3 + include/linux/sunrpc/xprt.h | 2 + include/linux/sunrpc/xprtmultipath.h | 5 + net/sunrpc/.kunitconfig | 29 ++++ net/sunrpc/clnt.c | 269 +++++++++++++++++++++++++++++++- net/sunrpc/sched.c | 15 +- net/sunrpc/xprt.c | 126 ++++++++++++++- net/sunrpc/xprtmultipath.c | 14 +- 28 files changed, 1167 insertions(+), 37 deletions(-) create mode 100644 fs/nfs/enfs/Makefile create mode 100644 fs/nfs/enfs_adapter.c create mode 100644 fs/nfs/enfs_adapter.h create mode 100644 net/sunrpc/.kunitconfig diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 5d85715..226eb1e 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -222,6 +222,7 @@ nlmclnt_recovery(struct nlm_host *host) "(%ld)\n", host->h_name, PTR_ERR(task)); } } +EXPORT_SYMBOL_GPL(nlmclnt_recovery); static int reclaimer(void *ptr) diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 127a728..7a16f48 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -31,6 +31,8 @@ #define NLM_HOST_EXPIRE (300 * HZ) #define NLM_HOST_COLLECT (120 * HZ) +#define ENFS_CAPABILITY_LSID_SUPPORT 0x0002 /* lsversion query capability */ + static struct hlist_head nlm_server_hosts[NLM_HOST_NRHASH]; static struct hlist_head nlm_client_hosts[NLM_HOST_NRHASH]; @@ -446,7 +448,7 @@ nlm_bind_host(struct nlm_host *host) .to_initval = increment, .to_increment = increment, .to_maxval = increment * 6UL, - .to_retries = 5U, + .to_retries = 0, }; struct rpc_create_args args = { .net = host->net, @@ -569,10 +571,17 @@ void nlm_host_rebooted(const struct net *net, const struct nlm_reboot *info) * To avoid processing a host several times, we match the nsmstate. */ while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) { + if (host->enfs_flag & ENFS_CAPABILITY_LSID_SUPPORT) { + continue; + } nlmsvc_free_host_resources(host); nlmsvc_release_host(host); } while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) { + if (host->enfs_flag & ENFS_CAPABILITY_LSID_SUPPORT) { + dprintk("lockd: ignore nsm notify. \n"); + continue; + } nlmclnt_recovery(host); nlmclnt_release_host(host); } diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 7df2503..4bf6090 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -212,3 +212,14 @@ config NFS_V4_2_READ_PLUS default y help Choose Y here to enable use of the NFS v4.2 READ_PLUS operation. + +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 5f6db37..ed2786c 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,7 +9,7 @@ CFLAGS_nfstrace.o += -I$(src) nfs-y := client.o dir.o file.o getroot.o inode.o super.o \ io.o direct.o pagelist.o read.o symlink.o unlink.o \ write.o namespace.o mount_clnt.o nfstrace.o \ - export.o sysfs.o fs_context.o + export.o sysfs.o fs_context.o enfs_adapter.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o @@ -35,3 +35,6 @@ nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o nfs42xattr.o obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/ obj-$(CONFIG_PNFS_BLOCK) += blocklayout/ obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/ +CONFIG_ENFS=m +obj-$(CONFIG_ENFS) += enfs/ + diff --git a/fs/nfs/client.c b/fs/nfs/client.c index d9b10c4..0995d34 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -44,7 +44,8 @@ #include "callback.h" #include "delegation.h" #include "iostat.h" -#include "internal.h" +//#include "internal.h" +#include "enfs_adapter.h" #include "fscache.h" #include "pnfs.h" #include "nfs.h" @@ -241,6 +242,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); @@ -315,20 +317,30 @@ again: continue; /* Match the full socket address */ - if (!rpc_cmp_addr_port(sap, clap)) + if (!rpc_cmp_addr_port(sap, clap)) { + if (data->enfs_option != NULL) { + continue; + } else { /* Match all xprt_switch full socket addresses */ if (IS_ERR(clp->cl_rpcclient) || !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient, sap)) continue; - + } + } + if (!nfs_multipath_client_match(clp->cl_multipath_data, data->enfs_option)) { + printk("not match client src %p dst %p.\n", clp->cl_multipath_data, data->enfs_option); + continue; + } /* Match the xprt security policy */ if (clp->cl_xprtsec.policy != data->xprtsec.policy) continue; refcount_inc(&clp->cl_count); + printk("match client %p.\n", clp); return clp; } + printk("not match client .\n"); return NULL; } @@ -516,6 +528,7 @@ int nfs_create_rpc_client(struct nfs_client *clp, .xprtsec = cl_init->xprtsec, .connect_timeout = cl_init->connect_timeout, .reconnect_timeout = cl_init->reconnect_timeout, + .multipath_option = cl_init->enfs_option, }; if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) @@ -650,6 +663,14 @@ struct nfs_client *nfs_init_client(struct nfs_client *clp, if (clp->cl_cons_state == NFS_CS_READY) return clp; + error = nfs_create_multi_path_client(clp, cl_init); + if (error < 0) { + printk("nfs_create_multi_path_client faild.%d\n.", error); + nfs_put_client(clp); + clp = ERR_PTR(error); + return clp; + } + /* * Create a client RPC handle for doing FSSTAT with UNIX auth only * - RFC 2623, sec 2.3.2 @@ -684,6 +705,7 @@ static int nfs_init_server(struct nfs_server *server, .nconnect = ctx->nfs_server.nconnect, .init_flags = (1UL << NFS_CS_REUSEPORT), .xprtsec = ctx->xprtsec, + .enfs_option = ctx->enfs_option }; struct nfs_client *clp; int error; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 39f7549..0ea4d8a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -43,7 +43,7 @@ #include "delegation.h" #include "iostat.h" -#include "internal.h" +#include "enfs_adapter.h" #include "fscache.h" #include "nfstrace.h" @@ -1360,6 +1360,11 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence) return offset; } +bool nfs_check_have_lookup_cache_flag(struct nfs_server *server, int flag) +{ + return enfs_check_have_lookup_cache_flag(server, flag); +} + /* * All directory operations under NFS are synchronous, so fsync() * is a dummy operation. @@ -1506,7 +1511,7 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry, { if (IS_ROOT(dentry)) return 1; - if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE) + if (nfs_check_have_lookup_cache_flag(NFS_SERVER(dir), NFS_MOUNT_LOOKUP_CACHE_NONE)) return 0; if (!nfs_dentry_verify_change(dir, dentry)) return 0; @@ -1610,7 +1615,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, { if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) return 0; - if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) + if (nfs_check_have_lookup_cache_flag(NFS_SERVER(dir), NFS_MOUNT_LOOKUP_CACHE_NONEG)) return 1; /* Case insensitive server? Revalidate negative dentries */ if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE)) diff --git a/fs/nfs/enfs/Makefile b/fs/nfs/enfs/Makefile new file mode 100644 index 0000000..5720a74 --- /dev/null +++ b/fs/nfs/enfs/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the nfs multipath kernel module +# +obj-$(CONFIG_ENFS) += enfs.o +enfs-y := enfs_init.o enfs_multipath_client.o enfs_multipath_parse.o diff --git a/fs/nfs/enfs_adapter.c b/fs/nfs/enfs_adapter.c new file mode 100644 index 0000000..6668d24 --- /dev/null +++ b/fs/nfs/enfs_adapter.c @@ -0,0 +1,292 @@ +#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 <linux/nfs_mount.h> +#include "enfs_adapter.h" +#include "iostat.h" + +struct enfs_adapter_ops __rcu *enfs_adapter; + +static DEFINE_MUTEX(enfs_module_mutex); +static void * enfs_adapter_data; + +void *enfs_adapter_get_data(void) +{ + return enfs_adapter_data; +} +EXPORT_SYMBOL_GPL(enfs_adapter_get_data); + +void enfs_adapter_set_data(void *data) +{ + enfs_adapter_data = data; +} +EXPORT_SYMBOL_GPL(enfs_adapter_set_data); + +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; + printk(KERN_ERR "regist enfs_adapter ops %p fail. old %p\n", 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; + printk(KERN_ERR "unregist enfs_adapter ops %p fail. old %p\n", 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) { + printk(KERN_WARNING "ENFS: invalid option %d\n", option); + return false; + } + + return true; +} + +int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str, struct nfs_fs_context *mnt, struct fs_context *fc) +{ + + //parseMultiPathOptions(getNfsMultiPathOpt(token), string, mnt); + + int rc; + struct enfs_adapter_ops *ops; + + // whether insert enfs.ko or not + ops = nfs_multipath_router_get(); + if (ops == NULL) { + dfprintk(MOUNT, "eNFS: prepare loading eNFS module[%s]\n", __FUNCTION__); + mutex_lock(&enfs_module_mutex); + rc = request_module("enfs"); + mutex_unlock(&enfs_module_mutex); + + if (rc) { + dfprintk(MOUNT, "eNFS: failed loading eNFS module[%s]\n", __FUNCTION__); + return -EOPNOTSUPP; + } + + 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", __FUNCTION__); + return -EOPNOTSUPP; + } + // nfs_multipath_parse_options + dfprintk(MOUNT, "NFS: parsing nfs mount option '%s' type: %d[%s]\n", str, option, __FUNCTION__); + rc = ops->parse_mount_options(option, str, &mnt->enfs_option, fc->net_ns); + nfs_multipath_router_put(ops); + return rc; +} + +void enfs_free_mount_options(struct nfs_fs_context *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(void *src, void *dst) +{ + int ret = true; + struct enfs_adapter_ops *ops; + printk("nfs_multipath_client_match src %p dst %p\n.", src, dst); + if (src == NULL && dst == NULL) + return true; + + if ((src == NULL && dst) || (src && dst == NULL)) + return false; + + ops = nfs_multipath_router_get(); + if (ops != NULL && ops->client_info_match != NULL) + ret = ops->client_info_match(src, dst); + nfs_multipath_router_put(ops); + + return ret; +} + +int nfs4_multipath_client_match(void *src, void *dst) +{ + int ret = true; + struct enfs_adapter_ops *ops; + if (src == NULL && dst == NULL) + return true; + + if (src == NULL || dst == NULL) + return false; + + ops = nfs_multipath_router_get(); + if (ops != NULL && ops->nfs4_client_info_match != NULL) + ret = ops->nfs4_client_info_match(src, dst); + 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); + +void nfs_multipath_set_mount_data(void **opt, const char *hostname) +{ + struct enfs_adapter_ops *ops = nfs_multipath_router_get(); + int rc; + + if (ops == NULL) { + dfprintk(MOUNT, "eNFS: prepare loading eNFS module[%s]\n", __FUNCTION__); + mutex_lock(&enfs_module_mutex); + rc = request_module("enfs"); + mutex_unlock(&enfs_module_mutex); + + if (rc) { + dfprintk(MOUNT, "eNFS: failed loading eNFS module[%s]\n", __FUNCTION__); + } + ops = nfs_multipath_router_get(); + } + + if (ops != NULL && ops->set_mount_data != NULL) + ops->set_mount_data(opt, hostname); + nfs_multipath_router_put(ops); +} +EXPORT_SYMBOL_GPL(nfs_multipath_set_mount_data); + +bool enfs_check_have_lookup_cache_flag(struct nfs_server *server, int flag) +{ + /* + rule: + 1. first check user lookupcache flag match or not + 2. then if user lookupcache option is positive/none, will ignore server lookupcache flag. + if user lookupcache option is all, will check server lookupcache flag. + + we don't use enfs ops to check, because during upgrade ops will be null, it will cause + result will change when upgrade. + */ + if (server->flags & flag) + return true; + + if (server->flags & (NFS_MOUNT_LOOKUP_CACHE_NONE | NFS_MOUNT_LOOKUP_CACHE_NONEG)) + return false; + + return ((server->enfs_flags & flag) ? true : false); +} +EXPORT_SYMBOL_GPL(enfs_check_have_lookup_cache_flag); + +void enfs_trigger_get_server_capability(struct nfs_server *server) +{ + struct enfs_adapter_ops *ops; + + ops = nfs_multipath_router_get(); + if (ops != NULL && ops->trigger_get_capability != NULL) + ops->trigger_get_capability(server); + nfs_multipath_router_put(ops); + + return; +} +EXPORT_SYMBOL_GPL(enfs_trigger_get_server_capability); diff --git a/fs/nfs/enfs_adapter.h b/fs/nfs/enfs_adapter.h new file mode 100644 index 0000000..d27e745 --- /dev/null +++ b/fs/nfs/enfs_adapter.h @@ -0,0 +1,62 @@ +/* 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" + +enum nfsmultipathoptions { + REMOTEADDR, + LOCALADDR, + INVALID_OPTION +}; + +/* enfs_flag in struct nfs_server bitmap define */ +#define ENFS_SERVER_FLAG_GET_CAP_RUNNING 0x1 /* enfs get capability rpc task is running or pendding */ +#define ENFS_SERVER_FLAG_LOOKUP_CACHE_NOREG 0x10000 /* NFS_MOUNT_LOOKUP_CACHE_NONEG, don't change value */ +#define ENFS_SERVER_FLAG_LOOKUP_CACHE_NONE 0x20000 /* NFS_MOUNT_LOOKUP_CACHE_NONE, don't change value */ + +struct enfs_adapter_ops { + const char *name; + struct module *owner; + //int (*alloc_mount_option) + int (*parse_mount_options)(enum nfsmultipathoptions option, char *str, void **enfs_option, struct net *net_ns); + void (*free_mount_options)(void **data); + + //void *(*dup_mount_options)(struct nfs_fs_context *ctx); + 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 (*client_info_clone)(struct nfs_server *src, struct nfs_server *dst, + // rpc_authflavor_t flavor); + // struct rpc_clnt *(*get_best_conn)(struct nfs_client *clp, struct nfs_fh *fh); + // void (*conn_set_unavailable)(struct nfs_client *clp, struct rpc_clnt *clnt); + int (*remount_ip_list)(struct nfs_client *nfs_client, void *enfs_option); + void (*set_mount_data)(void **opt, const char *hostname); + void (*trigger_get_capability)(struct nfs_server *server); +}; + +void *enfs_adapter_get_data(void); +void enfs_adapter_set_data(void *data); + +int enfs_parse_mount_options(enum nfsmultipathoptions option, char *str, struct nfs_fs_context *mnt, struct fs_context *fc); +void enfs_free_mount_options(struct nfs_fs_context *data); +int nfs_create_multi_path_client(struct nfs_client *client, const struct nfs_client_initdata *cl_init); +void nfs_multipath_set_mount_data(void **opt, const char *hostname); +void nfs_free_multi_path_client(struct nfs_client *clp); +int nfs_multipath_client_match(void *src, void *dst); +int nfs4_multipath_client_match(void *src, void *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); +bool enfs_check_have_lookup_cache_flag(struct nfs_server *server, int flag); +void enfs_trigger_get_server_capability(struct nfs_server *server); +#endif \ No newline at end of file diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c index 41126d6..47e8915 100644 --- a/fs/nfs/fs_context.c +++ b/fs/nfs/fs_context.c @@ -22,7 +22,8 @@ #include <net/handshake.h> #include "nfs.h" -#include "internal.h" +// #include "internal.h" +#include "enfs_adapter.h" #include "nfstrace.h" @@ -92,6 +93,11 @@ enum nfs_param { Opt_wsize, Opt_write, Opt_xprtsec, + Opt_remote_addrs, + Opt_local_iplist, + Opt_enfs_info, + Opt_slookupcache, + Opt_alookupcache, }; enum { @@ -199,6 +205,11 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = { fsparam_enum ("write", Opt_write, nfs_param_enums_write), fsparam_u32 ("wsize", Opt_wsize), fsparam_string("xprtsec", Opt_xprtsec), + fsparam_string("localaddrs", Opt_local_iplist), + fsparam_string("remoteaddrs", Opt_remote_addrs), + fsparam_string("enfs_info", Opt_enfs_info), + fsparam_string("slookupcache", Opt_slookupcache), + fsparam_string("alookupcache", Opt_alookupcache), {} }; @@ -356,6 +367,19 @@ out_invalid_xprtsec_policy: return nfs_invalf(fc, "NFS: Transport does not support xprtsec"); } +enum nfsmultipathoptions getNfsMultiPathOpt(int token) +{ + switch (token) { + case Opt_remote_addrs: { + return REMOTEADDR; + } + case Opt_local_iplist: { + return LOCALADDR; + } + } + return INVALID_OPTION; +} + /* * For text based NFSv2/v3 mounts, the mount protocol transport default * settings should depend upon the specified NFS transport. @@ -899,6 +923,17 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, goto out_invalid_value; } break; + case Opt_local_iplist: + case Opt_remote_addrs: + switch (enfs_parse_mount_options(getNfsMultiPathOpt(opt), param->string, ctx, fc)) { + 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_write: trace_nfs_mount_assign(param->key, param->string); switch (result.uint_32) { @@ -925,6 +960,12 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, case Opt_sloppy: ctx->sloppy = true; break; + case Opt_enfs_info: + case Opt_slookupcache: + case Opt_alookupcache: + break; + default: + dfprintk(MOUNT, "NFS: unrecognized mount option"); } return 0; @@ -937,6 +978,10 @@ out_of_bounds: return nfs_invalf(fc, "NFS: Value for '%s' out of range", param->key); out_bad_transport: return nfs_invalf(fc, "NFS: Unrecognized transport protocol"); +out_limit: + return nfs_invalf(fc, "NFS: param is more than supported limit"); +out_nomem: + return nfs_invalf(fc, "NFS: not enough memory to parse option"); } /* @@ -1453,6 +1498,7 @@ static int nfs_fs_context_validate(struct fs_context *fc) ret = nfs_parse_source(fc, max_namelen, max_pathlen); if (ret < 0) return ret; + nfs_multipath_set_mount_data(&ctx->enfs_option, ctx->nfs_server.hostname); /* Load the NFS protocol module if we haven't done so yet */ if (!ctx->nfs_mod) { @@ -1537,6 +1583,7 @@ static int nfs_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc) ctx->nfs_server.hostname = NULL; ctx->fscache_uniq = NULL; ctx->clone_data.fattr = NULL; + ctx->enfs_option = NULL; fc->fs_private = ctx; return 0; } @@ -1555,6 +1602,7 @@ static void nfs_fs_context_free(struct fs_context *fc) kfree(ctx->nfs_server.export_path); kfree(ctx->nfs_server.hostname); kfree(ctx->fscache_uniq); + enfs_free_mount_options(ctx); nfs_free_fhandle(ctx->mntfh); nfs_free_fattr(ctx->clone_data.fattr); kfree(ctx); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index a92b234..2be753a 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -84,6 +84,7 @@ struct nfs_client_initdata { struct xprtsec_parms xprtsec; unsigned long connect_timeout; unsigned long reconnect_timeout; + void *enfs_option; /* struct multipath_mount_options */ }; /* @@ -151,6 +152,8 @@ struct nfs_fs_context { struct nfs_fattr *fattr; unsigned int inherited_bsize; } clone_data; + + void *enfs_option; /* struct multipath_mount_options */ }; #define nfs_errorf(fc, fmt, ...) ((fc)->log.log ? \ diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 60f032b..49b5038 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -23,9 +23,12 @@ #include <linux/nfsacl.h> #include "nfstrace.h" #include "internal.h" +#include "linux/nfs_xdr.h" #define NFSDBG_FACILITY NFSDBG_XDR +#define EXTEND_CMD_MAX_BUF_LEN 819200 /* 800K */ + /* Mapping from NFS error code to "errno" error code. */ #define errno_NFSERR_IO EIO @@ -65,6 +68,7 @@ #define NFS3_readdirargs_sz (NFS3_fh_sz+NFS3_cookieverf_sz+3) #define NFS3_readdirplusargs_sz (NFS3_fh_sz+NFS3_cookieverf_sz+4) #define NFS3_commitargs_sz (NFS3_fh_sz+3) +#define NFS3_extendargs_sz (4 + XDR_QUADLEN(EXTEND_CMD_MAX_BUF_LEN)) #define NFS3_getattrres_sz (1+NFS3_fattr_sz) #define NFS3_setattrres_sz (1+NFS3_wcc_data_sz) @@ -82,6 +86,7 @@ #define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12) #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) +#define NFS3_extendres_sz (1 + 4 + XDR_QUADLEN(EXTEND_CMD_MAX_BUF_LEN)) #define ACL3_getaclargs_sz (NFS3_fh_sz+1) #define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \ @@ -1369,6 +1374,17 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req, #endif /* CONFIG_NFS_V3_ACL */ +static void nfs3_xdr_enc_extend3args(struct rpc_rqst *req, + struct xdr_stream *xdr, const void *data) +{ + const struct nfs_extend_xdr_arg *encArg = data; + __be32 *p; + + WARN_ON_ONCE(encArg->buflen > EXTEND_CMD_MAX_BUF_LEN); + p = xdr_reserve_space(xdr, 4 + encArg->buflen); + xdr_encode_opaque(p, encArg->pBuf, encArg->buflen); +} + /* * NFSv3 XDR decode functions * @@ -2440,6 +2456,52 @@ out_default: #endif /* CONFIG_NFS_V3_ACL */ +static int nfs3_xdr_dec_extend3res(struct rpc_rqst *req, struct xdr_stream *xdr, + void *result) +{ + enum nfs_stat status; + int error; + struct nfs_extend_xdr_arg *decArg = result; + int length; + __be32 *p; + + // check the status + error = decode_nfsstat3(xdr, &status); + if (unlikely(error)) + goto out; + if (status != NFS3_OK) + goto out_default; + + // important for upgrade scenario + memset(decArg->pBuf, '\0', decArg->maxsize); + + // decode legth of opaque data + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) { + return -EIO; + } + length = be32_to_cpup(p++); + if (unlikely(length > decArg->maxsize)) { + dprintk("NFS: response size (%u) too big , max_size is %d\n", length, + decArg->maxsize); + return -E2BIG; + } + // decode length number of bytes + p = xdr_inline_decode(xdr, length); + if (unlikely(!p)) { + return -EIO; + } + + decArg->buflen = length; + memcpy(decArg->pBuf, p, decArg->buflen); + dprintk("NFS: extend response size (%u)\n", length); + +out: + return error; +out_default: + return nfs3_stat_to_errno(status); +} + /* * We need to translate between nfs status return values and @@ -2517,6 +2579,8 @@ static int nfs3_stat_to_errno(enum nfs_stat status) .p_name = #proc, \ } +#define NFS3PROC_EXTEND 22 + const struct rpc_procinfo nfs3_procedures[] = { PROC(GETATTR, getattr, getattr, 1), PROC(SETATTR, setattr, setattr, 0), @@ -2539,7 +2603,9 @@ const struct rpc_procinfo nfs3_procedures[] = { PROC(FSINFO, getattr, fsinfo, 0), PROC(PATHCONF, getattr, pathconf, 0), PROC(COMMIT, commit, commit, 5), + PROC(EXTEND, extend, extend, 0), }; +EXPORT_SYMBOL_GPL(nfs3_procedures); static unsigned int nfs_version3_counts[ARRAY_SIZE(nfs3_procedures)]; const struct rpc_version nfs_version3 = { diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index ac80f87..a56c61c 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -11,7 +11,8 @@ #include <linux/sunrpc/xprt.h> #include <linux/sunrpc/bc_xprt.h> #include <linux/sunrpc/rpc_pipe_fs.h> -#include "internal.h" +//#include "internal.h" +#include "enfs_adapter.h" #include "callback.h" #include "delegation.h" #include "nfs4session.h" @@ -456,6 +457,14 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, if (error < 0) goto error; + error = nfs_create_multi_path_client(clp, cl_init); + if (error < 0) { + printk("nfs4_create_multi_path_client faild.%d\n.", error); + nfs_put_client(clp); + clp = ERR_PTR(error); + return clp; + } + error = nfs4_discover_server_trunking(clp, &old); if (error < 0) goto error; @@ -566,6 +575,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->cl_multipath_data, new->cl_multipath_data)) + return 1; + return 0; } @@ -901,7 +913,8 @@ static int nfs4_set_client(struct nfs_server *server, u32 minorversion, unsigned int nconnect, unsigned int max_connect, struct net *net, - struct xprtsec_parms *xprtsec) + struct xprtsec_parms *xprtsec, + void *enfs_option) { struct nfs_client_initdata cl_init = { .hostname = hostname, @@ -915,6 +928,7 @@ static int nfs4_set_client(struct nfs_server *server, .timeparms = timeparms, .cred = server->cred, .xprtsec = *xprtsec, + .enfs_option = enfs_option, }; struct nfs_client *clp; @@ -1177,7 +1191,8 @@ static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc) ctx->nfs_server.nconnect, ctx->nfs_server.max_connect, fc->net_ns, - &ctx->xprtsec); + &ctx->xprtsec, + ctx->enfs_option); if (error < 0) return error; @@ -1268,7 +1283,7 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc) parent_client->cl_nconnect, parent_client->cl_max_connect, parent_client->cl_net, - &parent_client->cl_xprtsec); + &parent_client->cl_xprtsec, NULL); if (!error) goto init_server; #endif /* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */ @@ -1288,7 +1303,7 @@ struct nfs_server *nfs4_create_referral_server(struct fs_context *fc) parent_client->cl_nconnect, parent_client->cl_max_connect, parent_client->cl_net, - &parent_client->cl_xprtsec); + &parent_client->cl_xprtsec, NULL); if (error < 0) goto error; @@ -1363,7 +1378,7 @@ int nfs4_update_server(struct nfs_server *server, const char *hostname, clp->cl_proto, clnt->cl_timeout, clp->cl_minorversion, clp->cl_nconnect, clp->cl_max_connect, - net, &clp->cl_xprtsec); + net, &clp->cl_xprtsec, NULL); clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status); if (error != 0) { nfs_server_insert_lists(server); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4b12e45..6d7e8c5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -8884,7 +8884,7 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cre if (clp->cl_serverscope != NULL && !nfs41_same_server_scope(clp->cl_serverscope, - resp->server_scope)) { + resp->server_scope) && clp->cl_multipath_data == NULL) { dprintk("%s: server_scope mismatch detected\n", __func__); set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state); @@ -9014,10 +9014,14 @@ static int nfs4_proc_destroy_clientid(struct nfs_client *clp, for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) { ret = _nfs4_proc_destroy_clientid(clp, cred); switch (ret) { - case -NFS4ERR_DELAY: - case -NFS4ERR_CLIENTID_BUSY: - ssleep(1); - break; + case -NFS4ERR_DELAY: { + ssleep(1); + break; + } + case -NFS4ERR_CLIENTID_BUSY: { + msleep(200); + break; + } default: return ret; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8515d3d..955b4e3 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -318,9 +318,70 @@ static void nfs41_finish_session_reset(struct nfs_client *clp) nfs4_setup_state_renewal(clp); } +static DEFINE_MUTEX(g_nfs41_clntid_cachelist_lock); +static LIST_HEAD(g_nfs41_clntid_cachelist); + +typedef struct { + struct list_head list_node; + u64 nfs41_clntid; +} nfs4_clntid_locknode; + +static nfs4_clntid_locknode *_nfs41_get_clntid_locknode(u64 clientid) +{ + nfs4_clntid_locknode *node = NULL; + nfs4_clntid_locknode *ret_node = NULL; + + mutex_lock(&g_nfs41_clntid_cachelist_lock); + list_for_each_entry(node, &g_nfs41_clntid_cachelist, list_node) { + if (node->nfs41_clntid != clientid) + continue; + ret_node = node; + break; + } + + if (ret_node != NULL) { + mutex_unlock(&g_nfs41_clntid_cachelist_lock); + return NULL; + } + + ret_node = (nfs4_clntid_locknode *)kzalloc(sizeof(nfs4_clntid_locknode), GFP_NOFS); + if (ret_node == NULL) { + printk("NFSv41: Failed to alloc clntid lock node %llu.\n", clientid); + mutex_unlock(&g_nfs41_clntid_cachelist_lock); + return NULL; + } + INIT_LIST_HEAD(&ret_node->list_node); + ret_node->nfs41_clntid = clientid; + list_add_tail(&ret_node->list_node, &g_nfs41_clntid_cachelist); + mutex_unlock(&g_nfs41_clntid_cachelist_lock); + + return ret_node; +} + +static void _nfs41_put_clntid_locknode(nfs4_clntid_locknode *lock_node) +{ + if (lock_node == NULL) { + return; + } + + mutex_lock(&g_nfs41_clntid_cachelist_lock); + list_del(&lock_node->list_node); + kfree(lock_node); + mutex_unlock(&g_nfs41_clntid_cachelist_lock); + return; +} + int nfs41_init_clientid(struct nfs_client *clp, const struct cred *cred) { int status; + nfs4_clntid_locknode *node = NULL; + + node = _nfs41_get_clntid_locknode(clp->cl_clientid); + if (node == NULL) { + status = -EAGAIN; // finally goes to nfs4_handle_reclaim_lease_error, will retry 1s later + printk_ratelimited("NFSv41: get clntid %llu lock node failed, retry later.\n", clp->cl_clientid); + goto out; + } if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) goto do_confirm; @@ -337,6 +398,7 @@ do_confirm: nfs41_finish_session_reset(clp); nfs_mark_client_ready(clp, NFS_CS_READY); out: + _nfs41_put_clntid_locknode(node); return status; } @@ -375,7 +437,7 @@ int nfs41_discover_server_trunking(struct nfs_client *clp, * server via Transparent State Migration. */ if (clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R) { - if (!test_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags)) + if ((!test_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags)) && (clp->cl_multipath_data == NULL)) set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); else set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e1bcad5..4a95f8a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -66,7 +66,8 @@ #include "callback.h" #include "delegation.h" #include "iostat.h" -#include "internal.h" +//#include "internal.h" +#include "enfs_adapter.h" #include "fscache.h" #include "nfs4session.h" #include "pnfs.h" @@ -559,6 +560,7 @@ 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; @@ -663,6 +665,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root) seq_puts(m, root->d_sb->s_flags & SB_NOATIME ? ",noatime" : ""); 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); @@ -1013,6 +1016,7 @@ nfs_compare_remount_data(struct nfs_server *nfss, int nfs_reconfigure(struct fs_context *fc) { + int error; struct nfs_fs_context *ctx = nfs_fc2context(fc); struct super_block *sb = fc->root->d_sb; struct nfs_server *nfss = sb->s_fs_info; @@ -1029,6 +1033,14 @@ int nfs_reconfigure(struct fs_context *fc) if (ctx->skip_reconfig_option_check) return 0; + if (ctx->enfs_option) { + error = nfs_remount_iplist(nfss->nfs_client, ctx->enfs_option); + if (error) { + /* release remount option member */ + enfs_free_mount_options(ctx); + return error; + } + } /* * noac is a special case. It implies -o sync, but that's not * necessarily reflected in the mtab options. reconfigure_super @@ -1332,6 +1344,9 @@ int nfs_get_tree_common(struct fs_context *fc) s->s_flags |= SB_ACTIVE; error = 0; + if (server) + enfs_trigger_get_server_capability(server); + out: return error; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 0f016d6..b6d6b09 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -73,6 +73,7 @@ struct nlm_host { const struct cred *h_cred; char nodename[UNX_MAXNODENAME + 1]; const struct nlmclnt_operations *h_nlmclnt_ops; /* Callback ops for NLM users */ + int enfs_flag; /* enfs falgs */ }; /* diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 86d96e0..3959896 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -124,6 +124,7 @@ struct nfs_client { char cl_ipaddr[48]; struct net *cl_net; struct list_head pending_cb_stateids; + void *cl_multipath_data; /* multi path private structure (struct multipath_client_info *) */ }; /* @@ -265,6 +266,7 @@ struct nfs_server { const struct cred *cred; bool has_sec_mnt_opts; struct kobject kobj; + int enfs_flags; /* ENFS_SERVER_FLAG_xx */ }; /* Server capabilities */ @@ -294,4 +296,15 @@ struct nfs_server { #define NFS_CAP_READ_PLUS (1U << 29) #define NFS_CAP_FS_LOCATIONS (1U << 30) #define NFS_CAP_MOVEABLE (1U << 31) + +/* use for kernel's nfsclient structure for refcount_t clp_count */ +static inline void nfsclient_refinc(refcount_t *ref_count) +{ + refcount_inc(ref_count); +} + +static inline void nfsclient_refdec(refcount_t *ref_count) +{ + refcount_dec(ref_count); +} #endif diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 12bbb5c..0dafbbb 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1725,6 +1725,12 @@ struct nfs_renamedata { bool cancelled; }; +struct nfs_extend_xdr_arg { + int maxsize; + int buflen; + char *pBuf; +}; + struct nfs_access_entry; struct nfs_client; struct rpc_timeout; diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 2c52acf..0b0753b 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -38,6 +38,31 @@ struct rpc_sysfs_client { struct rpc_xprt_switch *xprt_switch; }; +struct rpc_clnt_reserve { + atomic_t cl_count; /* Number of references */ + unsigned int cl_clid; /* client id */ + struct list_head cl_clients; /* Global list of clients */ + struct list_head cl_tasks; /* List of tasks */ + spinlock_t cl_lock; /* spinlock */ + struct rpc_xprt __rcu * cl_xprt; /* transport */ + const struct rpc_procinfo *cl_procinfo; /* procedure info */ + u32 cl_prog, /* RPC program number */ + cl_vers, /* RPC version number */ + cl_maxproc; /* max procedure number */ + + struct rpc_auth * cl_auth; /* authenticator */ + struct rpc_stat * cl_stats; /* per-program statistics */ + struct rpc_iostats * cl_metrics; /* per-client statistics */ + + unsigned int cl_softrtry : 1,/* soft timeouts */ + cl_discrtry : 1,/* disconnect before retry */ + cl_noretranstimeo: 1,/* No retransmit timeouts */ + cl_autobind : 1,/* use getport() */ + cl_chatty : 1,/* be verbose */ + cl_reserve : 11,/* reserve bits */ + cl_enfs : 1;/* be enfs */ +}; + /* * The high-level client handle @@ -164,6 +189,7 @@ struct rpc_create_args { KABI_RESERVE(1) KABI_RESERVE(2) + void *multipath_option; }; struct rpc_add_xprt_test { @@ -236,6 +262,7 @@ void rpc_force_rebind(struct rpc_clnt *); size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t); const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); int rpc_localaddr(struct rpc_clnt *, struct sockaddr *, size_t); +int rpc_localalladdr(struct rpc_xprt *xprt, struct sockaddr *buf, size_t buflen); int rpc_clnt_iterate_for_each_xprt(struct rpc_clnt *clnt, int (*fn)(struct rpc_clnt *, struct rpc_xprt *, void *), @@ -273,6 +300,9 @@ bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, void rpc_clnt_xprt_set_online(struct rpc_clnt *clnt, struct rpc_xprt *xprt); void rpc_clnt_disconnect(struct rpc_clnt *clnt); void rpc_cleanup_clids(void); +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_xprt *rpc_task_get_next_xprt(struct rpc_clnt *clnt); static inline int rpc_reply_expected(struct rpc_task *task) { @@ -285,4 +315,27 @@ static inline void rpc_task_close_connection(struct rpc_task *task) if (task->tk_xprt) xprt_force_disconnect(task->tk_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); + 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); + void (*set_transport)(struct rpc_task *task, struct rpc_clnt *clnt); + void (*inc_queuelen)(struct rpc_xprt *xprt); + void (*dec_queuelen)(struct rpc_xprt *xprt); + void (*get_rpc_program)(struct rpc_task *task, u32 *program, u32 *version); + bool (*task_need_call_start_again)(struct rpc_task *task); +}; +extern struct rpc_multipath_ops __rcu *multipath_ops; +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); #endif /* _LINUX_SUNRPC_CLNT_H */ diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index fc35544..0c28734 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -147,6 +147,8 @@ struct rpc_task_setup { #define RPC_TASK_NOCONNECT 0x2000 /* return ENOTCONN if not connected */ #define RPC_TASK_NO_RETRANS_TIMEOUT 0x4000 /* wait forever for a reply */ #define RPC_TASK_CRED_NOREF 0x8000 /* No refcount on the credential */ +#define RPC_TASK_FIXED 0x0004 +#define RPC_TASK_ENFS 0x0008 /* enfs rpc program task */ #define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) @@ -283,6 +285,7 @@ extern struct workqueue_struct *rpciod_workqueue; extern struct workqueue_struct *xprtiod_workqueue; void rpc_prepare_task(struct rpc_task *task); gfp_t rpc_task_gfp_mask(void); +void rpc_init_task_retry_counters(struct rpc_task *task); #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) || IS_ENABLED(CONFIG_TRACEPOINTS) static inline const char * rpc_qname(const struct rpc_wait_queue *q) diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 44ab7f2..d4e35f8 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -417,6 +417,8 @@ void xprt_free(struct rpc_xprt *); void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task); bool xprt_wake_up_backlog(struct rpc_xprt *xprt, struct rpc_rqst *req); void xprt_cleanup_ids(void); +void *xprt_get_reserve_context(struct rpc_xprt *xprt); +void xprt_set_reserve_context(struct rpc_xprt *xprt, void *context); static inline int xprt_enable_swap(struct rpc_xprt *xprt) diff --git a/include/linux/sunrpc/xprtmultipath.h b/include/linux/sunrpc/xprtmultipath.h index c0514c6..502d060 100644 --- a/include/linux/sunrpc/xprtmultipath.h +++ b/include/linux/sunrpc/xprtmultipath.h @@ -83,4 +83,9 @@ extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps, extern void xprt_multipath_cleanup_ids(void); +extern void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps, + struct rpc_xprt *xprt); + +extern void rpc_xprt_switch_set_singular(struct rpc_xprt_switch *xps); + #endif diff --git a/net/sunrpc/.kunitconfig b/net/sunrpc/.kunitconfig new file mode 100644 index 0000000..eb02b90 --- /dev/null +++ b/net/sunrpc/.kunitconfig @@ -0,0 +1,29 @@ +CONFIG_KUNIT=y +CONFIG_UBSAN=y +CONFIG_STACKTRACE=y +CONFIG_NET=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_INET=y +CONFIG_FILE_LOCKING=y +CONFIG_MULTIUSER=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_CAMELLIA=y +CONFIG_NFS_FS=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA1=y +CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_CAMELLIA=y +CONFIG_RPCSEC_GSS_KRB5_ENCTYPES_AES_SHA2=y +CONFIG_RPCSEC_GSS_KRB5_KUNIT_TEST=y diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 142ee65..2fec76d 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -54,6 +54,8 @@ static DECLARE_WAIT_QUEUE_HEAD(destroy_wait); +struct rpc_multipath_ops __rcu *multipath_ops = NULL; + static void call_start(struct rpc_task *task); static void call_reserve(struct rpc_task *task); @@ -78,6 +80,7 @@ static int rpc_decode_header(struct rpc_task *task, static int rpc_ping(struct rpc_clnt *clnt); static int rpc_ping_noreply(struct rpc_clnt *clnt); static void rpc_check_timeout(struct rpc_task *task); +static void rpc_check_timeout_trans(struct rpc_task *task); static void rpc_register_client(struct rpc_clnt *clnt) { @@ -457,11 +460,60 @@ out_no_rpciod: return ERR_PTR(err); } +struct rpc_multipath_ops *rpc_multipath_ops_get(void) +{ + struct rpc_multipath_ops *ops; + + rcu_read_lock(); + ops = rcu_dereference(multipath_ops); + if (ops == NULL) { + 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); + +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 == NULL || old == ops) + return 0; + printk(KERN_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 == NULL || old == ops) + return 0; + printk(KERN_ERR "regist rpc_multipath ops %p fail. old %p\n", ops, old); + return -EPERM; +} +EXPORT_SYMBOL_GPL(rpc_multipath_ops_unregister); + static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args, struct rpc_xprt *xprt) { struct rpc_clnt *clnt = NULL; struct rpc_xprt_switch *xps; + struct rpc_multipath_ops *mops; if (args->bc_xprt && args->bc_xprt->xpt_bc_xps) { WARN_ON_ONCE(!(args->protocol & XPRT_TRANSPORT_BC)); @@ -496,6 +548,14 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args, } } + if (args->multipath_option) { + mops = rpc_multipath_ops_get(); + if (mops && mops->create_clnt) { + mops->create_clnt(args, clnt); + } + rpc_multipath_ops_put(mops); + } + clnt->cl_softrtry = 1; if (args->flags & (RPC_CLNT_CREATE_HARDRTRY|RPC_CLNT_CREATE_SOFTERR)) { clnt->cl_softrtry = 0; @@ -944,6 +1004,8 @@ EXPORT_SYMBOL_GPL(rpc_clnt_disconnect); */ void rpc_shutdown_client(struct rpc_clnt *clnt) { + struct rpc_multipath_ops *mops; + might_sleep(); trace_rpc_clnt_shutdown(clnt); @@ -954,6 +1016,12 @@ void rpc_shutdown_client(struct rpc_clnt *clnt) list_empty(&clnt->cl_tasks), 1*HZ); } + mops = rpc_multipath_ops_get(); + if (mops && mops->releas_clnt) { + mops->releas_clnt(clnt); + } + rpc_multipath_ops_put(mops); + rpc_release_client(clnt); } EXPORT_SYMBOL_GPL(rpc_shutdown_client); @@ -1078,9 +1146,17 @@ struct rpc_xprt * rpc_task_get_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) { struct rpc_xprt_switch *xps; + struct rpc_multipath_ops *mops; if (!xprt) return NULL; + + mops = rpc_multipath_ops_get(); + if (mops && mops->inc_queuelen) { + mops->inc_queuelen(xprt); + } + rpc_multipath_ops_put(mops); + rcu_read_lock(); xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch); atomic_long_inc(&xps->xps_queuelen); @@ -1094,6 +1170,13 @@ static void rpc_task_release_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt) { struct rpc_xprt_switch *xps; + struct rpc_multipath_ops *mops; + + mops = rpc_multipath_ops_get(); + if (mops && mops->dec_queuelen) { + mops->dec_queuelen(xprt); + } + rpc_multipath_ops_put(mops); atomic_long_dec(&xprt->queuelen); rcu_read_lock(); @@ -1145,15 +1228,24 @@ rpc_task_get_first_xprt(struct rpc_clnt *clnt) return rpc_task_get_xprt(clnt, xprt); } -static struct rpc_xprt * +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)); } +EXPORT_SYMBOL_GPL(rpc_task_get_next_xprt); static void rpc_task_set_transport(struct rpc_task *task, struct rpc_clnt *clnt) { + struct rpc_multipath_ops *mops; + + mops = rpc_multipath_ops_get(); + if (mops && mops->set_transport) { + mops->set_transport(task, clnt); + } + rpc_multipath_ops_put(mops); + if (task->tk_xprt) { if (!(test_bit(XPRT_OFFLINE, &task->tk_xprt->state) && (task->tk_flags & RPC_TASK_MOVEABLE))) @@ -1564,6 +1656,33 @@ int rpc_localaddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t buflen) } EXPORT_SYMBOL_GPL(rpc_localaddr); +int rpc_localalladdr(struct rpc_xprt *xprt, struct sockaddr *buf, size_t buflen) +{ + struct sockaddr_storage address; + struct sockaddr *sap = (struct sockaddr *)&address; + struct rpc_xprt *xpr; + struct net *net; + size_t salen; + int err; + + rcu_read_lock(); + xpr = rcu_dereference(xprt); + salen = xpr->addrlen; + memcpy(sap, &xpr->addr, salen); + net = get_net(xpr->xprt_net); + dprintk("NFS:net:%p\n", xpr->xprt_net); + rcu_read_unlock(); + + rpc_set_port(sap, 0); + err = rpc_sockname(net, sap, salen, buf); + put_net(net); + if (err != 0) + /* Couldn't discover local address, return ANYADDR */ + return rpc_anyaddr(sap->sa_family, buf, buflen); + return 0; +} +EXPORT_SYMBOL_GPL(rpc_localalladdr); + void rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize) { @@ -1621,10 +1740,19 @@ size_t rpc_max_bc_payload(struct rpc_clnt *clnt) { struct rpc_xprt *xprt; size_t ret; + struct rpc_clnt_reserve *clnt_reserve; + + clnt_reserve = (struct rpc_clnt_reserve *)clnt; rcu_read_lock(); - xprt = rcu_dereference(clnt->cl_xprt); + if (clnt_reserve->cl_enfs == 0) { + xprt = rcu_dereference(clnt->cl_xprt); + } else { + xprt = rpc_task_get_next_xprt(clnt); + } ret = xprt->ops->bc_maxpayload(xprt); + if (clnt_reserve->cl_enfs == 1 && xprt) + xprt_put(xprt); rcu_read_unlock(); return ret; } @@ -1765,6 +1893,7 @@ static void call_retry_reserve(struct rpc_task *task); static void call_reserveresult(struct rpc_task *task) { + struct rpc_multipath_ops *mpath_ops = NULL; int status = task->tk_status; /* @@ -1789,6 +1918,17 @@ call_reserveresult(struct rpc_task *task) case -EAGAIN: /* woken up; retry */ task->tk_action = call_retry_reserve; return; + case -ETIMEDOUT: /* woken up; restart */ + mpath_ops = rpc_multipath_ops_get(); + if (mpath_ops && mpath_ops->task_need_call_start_again && + mpath_ops->task_need_call_start_again(task)) { + rpc_multipath_ops_put(mpath_ops); + rpc_task_release_transport(task); + task->tk_action = call_start; + return; + } + rpc_multipath_ops_put(mpath_ops); + return; default: rpc_call_rpcerror(task, status); } @@ -2334,7 +2474,7 @@ call_transmit_status(struct rpc_task *task) task->tk_status = 0; break; } - rpc_check_timeout(task); + rpc_check_timeout_trans(task); } #if defined(CONFIG_SUNRPC_BACKCHANNEL) @@ -2500,15 +2640,84 @@ rpc_check_connected(const struct rpc_rqst *req) } static void +rpc_check_timeout_trans(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + + if (RPC_SIGNALLED(task)) { + rpc_call_rpcerror(task, -ERESTARTSYS); + return; + } + + if (xprt_adjust_timeout(task->tk_rqstp) == 0) { + return; + } + + trace_rpc_timeout_status(task); + task->tk_timeouts++; + + if (RPC_IS_SOFTCONN(task) && !rpc_check_connected(task->tk_rqstp)) { + rpc_call_rpcerror(task, -ETIMEDOUT); + return; + } + + if (RPC_IS_SOFT(task)) { + /* + * Once a "no retrans timeout" soft tasks (a.k.a NFSv4) has + * been sent, it should time out only if the transport + * connection gets terminally broken. + */ + if ((task->tk_flags & RPC_TASK_NO_RETRANS_TIMEOUT) && + rpc_check_connected(task->tk_rqstp)) + return; + + if (clnt->cl_chatty) { + pr_notice_ratelimited( + "%s: server %s not responding, timed out\n", + clnt->cl_program->name, + task->tk_xprt->servername); + } + if (task->tk_flags & RPC_TASK_TIMEOUT) + rpc_call_rpcerror(task, -ETIMEDOUT); + else + __rpc_call_rpcerror(task, -EIO, -ETIMEDOUT); + return; + } + + if (!(task->tk_flags & RPC_CALL_MAJORSEEN)) { + task->tk_flags |= RPC_CALL_MAJORSEEN; + if (clnt->cl_chatty) { + pr_notice_ratelimited( + "%s: server %s not responding, still trying\n", + clnt->cl_program->name, + task->tk_xprt->servername); + } + } + rpc_force_rebind(clnt); + /* + * Did our request time out due to an RPCSEC_GSS out-of-sequence + * event? RFC2203 requires the server to drop all such requests. + */ + rpcauth_invalcred(task); +} + +static void rpc_check_timeout(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; + struct rpc_multipath_ops *mpath_ops = NULL; if (RPC_SIGNALLED(task)) return; - if (xprt_adjust_timeout(task->tk_rqstp) == 0) - return; + if (xprt_adjust_timeout(task->tk_rqstp) == 0) { + mpath_ops = rpc_multipath_ops_get(); + if (mpath_ops && mpath_ops->failover_handle) { + mpath_ops->failover_handle(task); + } + rpc_multipath_ops_put(mpath_ops); + return; + } trace_rpc_timeout_status(task); task->tk_timeouts++; @@ -2630,6 +2839,19 @@ out: } } +void update_rpc_program(struct rpc_task *task, u32 *cl_prog, u32 *cl_vers) +{ + struct rpc_multipath_ops *mops; + + if (task->tk_flags & RPC_TASK_ENFS) { + mops = rpc_multipath_ops_get(); + if (mops && mops->get_rpc_program) { + mops->get_rpc_program(task, cl_prog, cl_vers); + } + rpc_multipath_ops_put(mops); + } +} + static int rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr) { @@ -2637,7 +2859,10 @@ rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr) struct rpc_rqst *req = task->tk_rqstp; __be32 *p; int error; + u32 cl_prog = clnt->cl_prog; + u32 cl_vers = clnt->cl_vers; + update_rpc_program(task, &cl_prog, &cl_vers); error = -EMSGSIZE; p = xdr_reserve_space(xdr, RPC_CALLHDRSIZE << 2); if (!p) @@ -2645,8 +2870,8 @@ rpc_encode_header(struct rpc_task *task, struct xdr_stream *xdr) *p++ = req->rq_xid; *p++ = rpc_call; *p++ = cpu_to_be32(RPC_VERSION); - *p++ = cpu_to_be32(clnt->cl_prog); - *p++ = cpu_to_be32(clnt->cl_vers); + *p++ = cpu_to_be32(cl_prog); + *p++ = cpu_to_be32(cl_vers); *p = cpu_to_be32(task->tk_msg.rpc_proc->p_proc); error = rpcauth_marshcred(task, xdr); @@ -2966,6 +3191,30 @@ success: } EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt); +/* + * 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); + static int rpc_clnt_add_xprt_helper(struct rpc_clnt *clnt, struct rpc_xprt *xprt, struct rpc_add_xprt_test *data) @@ -3061,6 +3310,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, { struct rpc_xprt_switch *xps; struct rpc_xprt *xprt; + struct rpc_clnt_reserve *clnt_reserve; unsigned long connect_timeout; unsigned long reconnect_timeout; unsigned char resvport, reuseport; @@ -3101,7 +3351,10 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, connect_timeout, reconnect_timeout); - rpc_xprt_switch_set_roundrobin(xps); + clnt_reserve = (struct rpc_clnt_reserve *)clnt; + if (!clnt_reserve->cl_enfs) { + rpc_xprt_switch_set_roundrobin(xps); + } if (setup) { ret = setup(clnt, xps, xprt, data); if (ret != 0) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index dd3d410..f001f60 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -814,15 +814,22 @@ void rpc_prepare_task(struct rpc_task *task) task->tk_ops->rpc_call_prepare(task, task->tk_calldata); } -static void -rpc_init_task_statistics(struct rpc_task *task) +void rpc_init_task_retry_counters(struct rpc_task *task) { /* Initialize retry counters */ task->tk_garb_retry = 2; task->tk_cred_retry = 2; +} +EXPORT_SYMBOL_GPL(rpc_init_task_retry_counters); + +static void +rpc_init_task_statistics(struct rpc_task *task) +{ + /* Initialize retry counters */ + rpc_init_task_retry_counters(task); - /* starting timestamp */ - task->tk_start = ktime_get(); + /* starting timestamp */ + task->tk_start = ktime_get(); } static void diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index ab453ed..e891301 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -58,6 +58,11 @@ #include "sysfs.h" #include "fail.h" +struct xprt_client_private { + void *reserve_context; + char servername; +}; + /* * Local variables */ @@ -265,6 +270,7 @@ static void xprt_clear_locked(struct rpc_xprt *xprt) int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; + struct rpc_multipath_ops *mops = NULL; if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) { if (task == xprt->snd_task) @@ -282,6 +288,11 @@ out_locked: out_unlock: xprt_clear_locked(xprt); out_sleep: + mops = rpc_multipath_ops_get(); + if (mops && mops->adjust_task_timeout) { + mops->adjust_task_timeout(task, NULL); + } + rpc_multipath_ops_put(mops); task->tk_status = -EAGAIN; if (RPC_IS_SOFT(task)) rpc_sleep_on_timeout(&xprt->sending, task, NULL, @@ -329,6 +340,7 @@ xprt_test_and_clear_congestion_window_wait(struct rpc_xprt *xprt) int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; + struct rpc_multipath_ops *mops = NULL; if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) { if (task == xprt->snd_task) @@ -348,6 +360,11 @@ int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task) out_unlock: xprt_clear_locked(xprt); out_sleep: + mops = rpc_multipath_ops_get(); + if (mops && mops->adjust_task_timeout) { + mops->adjust_task_timeout(task, NULL); + } + rpc_multipath_ops_put(mops); task->tk_status = -EAGAIN; if (RPC_IS_SOFT(task)) rpc_sleep_on_timeout(&xprt->sending, task, NULL, @@ -608,6 +625,15 @@ EXPORT_SYMBOL_GPL(xprt_wake_pending_tasks); */ void xprt_wait_for_buffer_space(struct rpc_xprt *xprt) { + struct rpc_task *task = xprt->snd_task; + 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); + set_bit(XPRT_WRITE_SPACE, &xprt->state); } EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space); @@ -1873,6 +1899,7 @@ xprt_request_init(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_xprt; struct rpc_rqst *req = task->tk_rqstp; + struct rpc_multipath_ops *mops = NULL; req->rq_task = task; req->rq_xprt = xprt; @@ -1888,6 +1915,12 @@ xprt_request_init(struct rpc_task *task) req->rq_release_snd_buf = NULL; xprt_init_majortimeo(task, req); + mops = rpc_multipath_ops_get(); + if (mops && mops->init_task_req) { + mops->init_task_req(task, req); + } + rpc_multipath_ops_put(mops); + trace_xprt_reserve(req); } @@ -1950,6 +1983,7 @@ void xprt_release(struct rpc_task *task) { struct rpc_xprt *xprt; struct rpc_rqst *req = task->tk_rqstp; + struct rpc_multipath_ops *mops; if (req == NULL) { if (task->tk_client) { @@ -1961,6 +1995,13 @@ void xprt_release(struct rpc_task *task) xprt = req->rq_xprt; xprt_request_dequeue_xprt(task); + + mops = rpc_multipath_ops_get(); + if (task->tk_client && mops && mops->xprt_iostat) { + mops->xprt_iostat(task); + } + rpc_multipath_ops_put(mops); + spin_lock(&xprt->transport_lock); xprt->ops->release_xprt(xprt, task); if (xprt->ops->release_request) @@ -1980,6 +2021,7 @@ void xprt_release(struct rpc_task *task) else xprt_free_bc_request(req); } +EXPORT_SYMBOL_GPL(xprt_release); #ifdef CONFIG_SUNRPC_BACKCHANNEL void @@ -2030,6 +2072,74 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net) xprt->xprt_net = get_net_track(net, &xprt->ns_tracker, GFP_KERNEL); } +const char *xprt_set_servername(const char *s, gfp_t gfp) +{ + size_t len; + struct xprt_client_private *buf; + + if (!s) + return NULL; + + len = sizeof(struct xprt_client_private) + strlen(s) + 1; + buf = kmalloc(len, gfp); + if (buf) { + memset(buf, 0, len); + memcpy(&(buf->servername), s, strlen(s) + 1); + return &(buf->servername); + } + return NULL; +} + +void xprt_free_servername(struct rpc_xprt *xprt) +{ + struct xprt_client_private *buf; + struct rpc_multipath_ops *mops; + + if (xprt == NULL || xprt->servername == NULL) { + return; + } + + buf = container_of(xprt->servername, struct xprt_client_private, servername); + if (buf->reserve_context) { + mops = rpc_multipath_ops_get(); + if (mops && mops->destroy_xprt) { + mops->destroy_xprt(xprt); + } + rpc_multipath_ops_put(mops); + } + + kfree((void *)buf); + xprt->servername = NULL; + return; +} + +void *xprt_get_reserve_context(struct rpc_xprt *xprt) +{ + struct xprt_client_private *buf; + + if (xprt == NULL || xprt->servername == NULL) { + return NULL; + } + + buf = container_of(xprt->servername, struct xprt_client_private, servername); + return buf->reserve_context; +} +EXPORT_SYMBOL_GPL(xprt_get_reserve_context); + +void xprt_set_reserve_context(struct rpc_xprt *xprt, void *context) +{ + struct xprt_client_private *buf; + + if (xprt == NULL || xprt->servername == NULL) { + return; + } + + buf = container_of(xprt->servername, struct xprt_client_private, servername); + buf->reserve_context = context; + return; +} +EXPORT_SYMBOL_GPL(xprt_set_reserve_context); + /** * xprt_create_transport - create an RPC transport * @args: rpc transport creation arguments @@ -2039,6 +2149,7 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args) { struct rpc_xprt *xprt; const struct xprt_class *t; + struct rpc_multipath_ops *mops; t = xprt_class_find_by_ident(args->ident); if (!t) { @@ -2063,12 +2174,23 @@ struct rpc_xprt *xprt_create_transport(struct xprt_create *args) xprt_destroy(xprt); return ERR_PTR(-EINVAL); } - xprt->servername = kstrdup(args->servername, GFP_KERNEL); + xprt->servername = xprt_set_servername(args->servername, GFP_KERNEL); if (xprt->servername == NULL) { xprt_destroy(xprt); return ERR_PTR(-ENOMEM); } + mops = rpc_multipath_ops_get(); + if (mops && mops->create_xprt) { + mops->create_xprt(xprt); + if (!xprt_get_reserve_context(xprt)) { + xprt_destroy(xprt); + rpc_multipath_ops_put(mops); + return ERR_PTR(-ENOMEM); + } + } + rpc_multipath_ops_put(mops); + rpc_xprt_debugfs_register(xprt); trace_xprt_create(xprt); @@ -2088,7 +2210,7 @@ static void xprt_destroy_cb(struct work_struct *work) rpc_destroy_wait_queue(&xprt->pending); rpc_destroy_wait_queue(&xprt->sending); rpc_destroy_wait_queue(&xprt->backlog); - kfree(xprt->servername); + xprt_free_servername(xprt); /* * Destroy any existing back channel */ diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c index 720d3ba..8548891 100644 --- a/net/sunrpc/xprtmultipath.c +++ b/net/sunrpc/xprtmultipath.c @@ -29,7 +29,7 @@ static const struct rpc_xprt_iter_ops rpc_xprt_iter_roundrobin; static const struct rpc_xprt_iter_ops rpc_xprt_iter_listall; static const struct rpc_xprt_iter_ops rpc_xprt_iter_listoffline; -static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps, +void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps, struct rpc_xprt *xprt) { if (unlikely(xprt_get(xprt) == NULL)) @@ -41,6 +41,7 @@ static void xprt_switch_add_xprt_locked(struct rpc_xprt_switch *xps, xps->xps_nxprts++; xps->xps_nactive++; } +EXPORT_SYMBOL(xprt_switch_add_xprt_locked); /** * rpc_xprt_switch_add_xprt - Add a new rpc_xprt to an rpc_xprt_switch @@ -91,6 +92,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); static DEFINE_IDA(rpc_xprtswitch_ids); @@ -187,6 +189,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 @@ -199,6 +202,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 @@ -212,6 +216,13 @@ void rpc_xprt_switch_set_roundrobin(struct rpc_xprt_switch *xps) WRITE_ONCE(xps->xps_iter_ops, &rpc_xprt_iter_roundrobin); } +void rpc_xprt_switch_set_singular(struct rpc_xprt_switch *xps) +{ + if (READ_ONCE(xps->xps_iter_ops) != &rpc_xprt_iter_singular) + WRITE_ONCE(xps->xps_iter_ops, &rpc_xprt_iter_singular); +} +EXPORT_SYMBOL(rpc_xprt_switch_set_singular); + static const struct rpc_xprt_iter_ops *xprt_iter_ops(const struct rpc_xprt_iter *xpi) { @@ -618,6 +629,7 @@ struct rpc_xprt *xprt_iter_get_xprt(struct rpc_xprt_iter *xpi) rcu_read_unlock(); return xprt; } +EXPORT_SYMBOL(xprt_iter_get_xprt); /** * xprt_iter_get_next - Returns the next rpc_xprt following the cursor -- 1.8.3.1