On Fri, Aug 30, 2024 at 01:01:55AM -0400, Mike Snitzer wrote: > On Fri, Aug 30, 2024 at 02:36:13PM +1000, NeilBrown wrote: > > On Fri, 30 Aug 2024, Jeff Layton wrote: > > > > > > Have a pointer to a struct nfsd_localio_ops or something in the > > > nfs_common module. That's initially set to NULL. Then, have a static > > > structure of that type in nfsd.ko, and have its __init routine set the > > > pointer in nfs_common to point to the right structure. The __exit > > > routine will later set it to NULL. > > > > > > > I really don't want all calls in NFS client (or nfs_common) to have to > > > > first check if nfs_common's 'nfs_to' ops structure is NULL or not. > > > > > > Neil seems to think that's not necessary: > > > > > > "If nfs/localio holds an auth_domain, then it implicitly holds a > > > reference to the nfsd module and the functions cannot disappear." > > > > On reflection that isn't quite right, but it is the sort of approach > > that I think we need to take. > > There are several things that the NFS client needs to hold one to. > > > > 1/ It needs a reference to the nfsd module (or symbols in the module). > > I think this can be held long term but we need a clear mechanism for > > it to be dropped. > > 2/ It needs a reference to the nfsd_serv which it gets through the > > 'struct net' pointer. I've posted patches to handle that better. > > 3/ It needs a reference to an auth_domain. This can safely be a long > > term reference. It can already be invalidated and the code to free > > it is in sunrpc which nfs already pins. Any delay in freeing it only > > wastes memory (not much), it doesn't impact anything else. > > 4/ It needs a reference to the nfsd_file and/or file. This is currently > > done only while the ref to the nfsd_serv is held, so I think there is > > no problem there. > > > > So possibly we could take a reference to the nfsd module whenever we > > store a net in nfs_uuid. and drop the ref whenever we clear that. > > > > That means we cannot call nfsd_open_local_fh() without first getting a > > ref on the nfsd_serv which my latest code doesn't do. That is easily > > fixed. I'll send a patch for consideration... > > I already implemented 2 different versions today, meant for v15. > > First is a relaxed version of the v14 code (less code, only using > symbol_request on nfsd_open_local_fh. > > Second is much more relaxed, because it leverages your original > assumption that the auth_domain ref sufficient. Corresponding code needed in fs/nfsd/localio.c: static const struct nfsd_localio_operations nfsd_localio_ops = { .nfsd_open_local_fh = nfsd_open_local_fh, .nfsd_file_get = nfsd_file_get, .nfsd_file_put = nfsd_file_put, .nfsd_file_file = nfsd_file_file, .nfsd_serv_put = nfsd_serv_put, }; void nfsd_localio_ops_init(void) { memcpy(&nfs_to, &nfsd_localio_ops, sizeof(nfsd_localio_ops)); } From: Mike Snitzer <snitzer@xxxxxxxxxx> Date: Wed, 28 Aug 2024 17:04:44 -0500 Subject: [PATCH v15.option2] nfs_common: introduce nfs_localio_ctx struct and interfaces Introduce struct nfs_localio_ctx (which has nfsd_file and nfsd_net members) and the interfaces nfs_localio_ctx_alloc() and nfs_localio_ctx_free(). The next commit will introduce nfsd_open_local_fh() which returns a nfs_localio_ctx structure. Also, expose localio's required NFSD symbols to NFS client: - Make nfsd_open_local_fh() symbol and other required NFSD symbols available to NFS in a global 'nfs_to' nfsd_localio_operations struct. The next commit will also introduce nfsd_localio_ops_init() that init_nfsd() will call to initialize 'nfs_to'. - Introduce nfsd_file_file() wrapper that provides access to nfsd_file's backing file. Keeps nfsd_file structure opaque to NFS client (as suggested by Jeff Layton). - The addition of nfsd_file_get, nfsd_file_put and nfsd_file_file symbols prepares for the NFS client to use nfsd_file for localio. Suggested-by: Trond Myklebust <trond.myklebust@xxxxxxxxxxxxxxx> # nfs_to Suggested-by: NeilBrown <neilb@xxxxxxx> # nfsd_localio_operations Suggested-by: Jeff Layton <jlayton@xxxxxxxxxx> # nfsd_file_file Signed-off-by: Mike Snitzer <snitzer@xxxxxxxxxx> --- fs/nfs_common/nfslocalio.c | 62 ++++++++++++++++++++++++++++++++++++++ fs/nfsd/filecache.c | 16 ++++++++++ fs/nfsd/filecache.h | 1 + fs/nfsd/nfssvc.c | 2 ++ include/linux/nfslocalio.h | 43 ++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c index 0b2e17c2068f..175064e37a75 100644 --- a/fs/nfs_common/nfslocalio.c +++ b/fs/nfs_common/nfslocalio.c @@ -72,3 +72,65 @@ bool nfs_uuid_is_local(const uuid_t *uuid, struct net *net, struct auth_domain * return is_local; } EXPORT_SYMBOL_GPL(nfs_uuid_is_local); + +/* + * The NFS LOCALIO code needs to call into NFSD using various symbols, + * but cannot be statically linked, because that will make the NFS + * module always depend on the NFSD module. + * + * 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO, + * its lifetime is tightly coupled to the NFSD module and will always + * be available to NFS LOCALIO because any successful client<->server + * LOCALIO handshake results in a reference taken on an auth_domain, + * so NFS implicitly holds a reference to the NFSD module and its + * functions in the 'nfs_to' nfsd_localio_operations cannot disappear. + * + * If the last NFS client using LOCALIO disconnects (and its reference + * on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to' + * functions being invalid pointers. But if NFSD isn't loaded then NFS + * will not be able to handshake with NFSD and will have no cause to + * try to call 'nfs_to' function pointers. If/when NFSD is reloaded it + * will reinitialize the 'nfs_to' function pointers and make LOCALIO + * possible. + */ +struct nfsd_localio_operations nfs_to; +EXPORT_SYMBOL_GPL(nfs_to); + +/* + * nfs_localio_ctx cache and alloc/free interfaces. + */ +static struct kmem_cache *nfs_localio_ctx_cache; + +struct nfs_localio_ctx *nfs_localio_ctx_alloc(void) +{ + return kmem_cache_alloc(nfs_localio_ctx_cache, + GFP_KERNEL | __GFP_ZERO); +} +EXPORT_SYMBOL_GPL(nfs_localio_ctx_alloc); + +void nfs_localio_ctx_free(struct nfs_localio_ctx *localio) +{ + if (localio->nf) + nfs_to.nfsd_file_put(localio->nf); + if (localio->nn) + nfs_to.nfsd_serv_put(localio->nn); + kmem_cache_free(nfs_localio_ctx_cache, localio); +} +EXPORT_SYMBOL_GPL(nfs_localio_ctx_free); + +static int __init nfslocalio_init(void) +{ + nfs_localio_ctx_cache = KMEM_CACHE(nfs_localio_ctx, 0); + if (!nfs_localio_ctx_cache) + return -ENOMEM; + + return 0; +} + +static void __exit nfslocalio_exit(void) +{ + kmem_cache_destroy(nfs_localio_ctx_cache); +} + +module_init(nfslocalio_init); +module_exit(nfslocalio_exit); diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c index 2dc72de31f61..1a26f5812578 100644 --- a/fs/nfsd/filecache.c +++ b/fs/nfsd/filecache.c @@ -39,6 +39,7 @@ #include <linux/fsnotify.h> #include <linux/seq_file.h> #include <linux/rhashtable.h> +#include <linux/nfslocalio.h> #include "vfs.h" #include "nfsd.h" @@ -345,6 +346,7 @@ nfsd_file_get(struct nfsd_file *nf) return nf; return NULL; } +EXPORT_SYMBOL_GPL(nfsd_file_get); /** * nfsd_file_put - put the reference to a nfsd_file @@ -389,6 +391,20 @@ nfsd_file_put(struct nfsd_file *nf) if (refcount_dec_and_test(&nf->nf_ref)) nfsd_file_free(nf); } +EXPORT_SYMBOL_GPL(nfsd_file_put); + +/** + * nfsd_file_file - get the backing file of an nfsd_file + * @nf: nfsd_file of which to access the backing file. + * + * Return backing file for @nf. + */ +struct file * +nfsd_file_file(struct nfsd_file *nf) +{ + return nf->nf_file; +} +EXPORT_SYMBOL_GPL(nfsd_file_file); static void nfsd_file_dispose_list(struct list_head *dispose) diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h index 26ada78b8c1e..6fbbb2e32e95 100644 --- a/fs/nfsd/filecache.h +++ b/fs/nfsd/filecache.h @@ -56,6 +56,7 @@ int nfsd_file_cache_start_net(struct net *net); void nfsd_file_cache_shutdown_net(struct net *net); void nfsd_file_put(struct nfsd_file *nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); +struct file *nfsd_file_file(struct nfsd_file *nf); void nfsd_file_close_inode_sync(struct inode *inode); void nfsd_file_net_dispose(struct nfsd_net *nn); bool nfsd_file_is_cached(struct inode *inode); diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index c639fbe4d8c2..7b9119b8dd1b 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -19,6 +19,7 @@ #include <linux/sunrpc/svc_xprt.h> #include <linux/lockd/bind.h> #include <linux/nfsacl.h> +#include <linux/nfslocalio.h> #include <linux/seq_file.h> #include <linux/inetdevice.h> #include <net/addrconf.h> @@ -201,6 +202,7 @@ void nfsd_serv_put(struct nfsd_net *nn) { percpu_ref_put(&nn->nfsd_serv_ref); } +EXPORT_SYMBOL_GPL(nfsd_serv_put); static void nfsd_serv_done(struct percpu_ref *ref) { diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h index 9735ae8d3e5e..fdb1f278afb6 100644 --- a/include/linux/nfslocalio.h +++ b/include/linux/nfslocalio.h @@ -7,6 +7,7 @@ #include <linux/list.h> #include <linux/uuid.h> +#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/svcauth.h> #include <linux/nfs.h> #include <net/net_namespace.h> @@ -28,4 +29,46 @@ void nfs_uuid_begin(nfs_uuid_t *); void nfs_uuid_end(nfs_uuid_t *); bool nfs_uuid_is_local(const uuid_t *, struct net *, struct auth_domain *); +struct nfsd_file; +struct nfsd_net; + +struct nfs_localio_ctx { + struct nfsd_file *nf; + struct nfsd_net *nn; +}; + +/* localio needs to map filehandle -> struct nfsd_file */ +typedef struct nfs_localio_ctx * +(*nfs_to_nfsd_open_local_fh_t)(struct net *, struct auth_domain *, + struct rpc_clnt *, const struct cred *, + const struct nfs_fh *, const fmode_t); + +extern struct nfs_localio_ctx * +nfsd_open_local_fh(struct net *, struct auth_domain *, + struct rpc_clnt *, const struct cred *, + const struct nfs_fh *, const fmode_t); + +/* localio needs to acquire an nfsd_file */ +typedef struct nfsd_file * (*nfs_to_nfsd_file_get_t)(struct nfsd_file *); +/* localio needs to release an nfsd_file */ +typedef void (*nfs_to_nfsd_file_put_t)(struct nfsd_file *); +/* localio needs to access the nf->nf_file */ +typedef struct file * (*nfs_to_nfsd_file_file_t)(struct nfsd_file *); +/* localio needs to release nn->nfsd_serv */ +typedef void (*nfs_to_nfsd_serv_put_t)(struct nfsd_net *); + +struct nfsd_localio_operations { + nfs_to_nfsd_open_local_fh_t nfsd_open_local_fh; + nfs_to_nfsd_file_get_t nfsd_file_get; + nfs_to_nfsd_file_put_t nfsd_file_put; + nfs_to_nfsd_file_file_t nfsd_file_file; + nfs_to_nfsd_serv_put_t nfsd_serv_put; +} ____cacheline_aligned; + +extern void nfsd_localio_ops_init(void); +extern struct nfsd_localio_operations nfs_to; + +struct nfs_localio_ctx *nfs_localio_ctx_alloc(void); +void nfs_localio_ctx_free(struct nfs_localio_ctx *); + #endif /* __LINUX_NFSLOCALIO_H */ -- 2.44.0