]> Git Repo - linux.git/commitdiff
nfsd: add LOCALIO support
authorWeston Andros Adamson <[email protected]>
Thu, 5 Sep 2024 19:09:50 +0000 (15:09 -0400)
committerAnna Schumaker <[email protected]>
Mon, 23 Sep 2024 19:03:30 +0000 (15:03 -0400)
Add server support for bypassing NFS for localhost reads, writes, and
commits. This is only useful when both the client and server are
running on the same host.

If nfsd_open_local_fh() fails then the NFS client will both retry and
fallback to normal network-based read, write and commit operations if
localio is no longer supported.

Care is taken to ensure the same NFS security mechanisms are used
(authentication, etc) regardless of whether localio or regular NFS
access is used.  The auth_domain established as part of the traditional
NFS client access to the NFS server is also used for localio.  Store
auth_domain for localio in nfsd_uuid_t and transfer it to the client
if it is local to the server.

Relative to containers, localio gives the client access to the network
namespace the server has.  This is required to allow the client to
access the server's per-namespace nfsd_net struct.

This commit also introduces the use of NFSD's percpu_ref to interlock
nfsd_destroy_serv and nfsd_open_local_fh, to ensure nn->nfsd_serv is
not destroyed while in use by nfsd_open_local_fh and other LOCALIO
client code.

CONFIG_NFS_LOCALIO enables NFS server support for LOCALIO.

Signed-off-by: Weston Andros Adamson <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
Co-developed-by: Mike Snitzer <[email protected]>
Signed-off-by: Mike Snitzer <[email protected]>
Co-developed-by: NeilBrown <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
Reviewed-by: Jeff Layton <[email protected]>
Acked-by: Chuck Lever <[email protected]>
Signed-off-by: Anna Schumaker <[email protected]>
fs/nfsd/Makefile
fs/nfsd/filecache.c
fs/nfsd/localio.c [new file with mode: 0644]
fs/nfsd/netns.h
fs/nfsd/nfsctl.c
fs/nfsd/trace.h
fs/nfsd/vfs.h
include/linux/nfslocalio.h

index b8736a82e57c4777b06745cac6d1d7f658ba1888..18cbd3fa7691a27b39a3b1264637b9422aeb42ba 100644 (file)
@@ -23,3 +23,4 @@ nfsd-$(CONFIG_NFSD_PNFS) += nfs4layouts.o
 nfsd-$(CONFIG_NFSD_BLOCKLAYOUT) += blocklayout.o blocklayoutxdr.o
 nfsd-$(CONFIG_NFSD_SCSILAYOUT) += blocklayout.o blocklayoutxdr.o
 nfsd-$(CONFIG_NFSD_FLEXFILELAYOUT) += flexfilelayout.o flexfilelayoutxdr.o
+nfsd-$(CONFIG_NFS_LOCALIO) += localio.o
index 414b5bf738df9dc106124689225adacccf25f124..19bb88c7eebd9b7621398d46ff9a971b55037781 100644 (file)
@@ -52,7 +52,7 @@
 #define NFSD_FILE_CACHE_UP                  (0)
 
 /* We only care about NFSD_MAY_READ/WRITE for this cache */
-#define NFSD_FILE_MAY_MASK     (NFSD_MAY_READ|NFSD_MAY_WRITE)
+#define NFSD_FILE_MAY_MASK     (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
 
 static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
 static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
new file mode 100644 (file)
index 0000000..9fa10a8
--- /dev/null
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NFS server support for local clients to bypass network stack
+ *
+ * Copyright (C) 2014 Weston Andros Adamson <[email protected]>
+ * Copyright (C) 2019 Trond Myklebust <[email protected]>
+ * Copyright (C) 2024 Mike Snitzer <[email protected]>
+ * Copyright (C) 2024 NeilBrown <[email protected]>
+ */
+
+#include <linux/exportfs.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs_common.h>
+#include <linux/nfslocalio.h>
+#include <linux/string.h>
+
+#include "nfsd.h"
+#include "vfs.h"
+#include "netns.h"
+#include "filecache.h"
+
+static const struct nfsd_localio_operations nfsd_localio_ops = {
+       .nfsd_serv_try_get  = nfsd_serv_try_get,
+       .nfsd_serv_put  = nfsd_serv_put,
+       .nfsd_open_local_fh = nfsd_open_local_fh,
+       .nfsd_file_put_local = nfsd_file_put_local,
+       .nfsd_file_file = nfsd_file_file,
+};
+
+void nfsd_localio_ops_init(void)
+{
+       nfs_to = &nfsd_localio_ops;
+}
+
+/**
+ * nfsd_open_local_fh - lookup a local filehandle @nfs_fh and map to nfsd_file
+ *
+ * @net: 'struct net' to get the proper nfsd_net required for LOCALIO access
+ * @dom: 'struct auth_domain' required for LOCALIO access
+ * @rpc_clnt: rpc_clnt that the client established
+ * @cred: cred that the client established
+ * @nfs_fh: filehandle to lookup
+ * @fmode: fmode_t to use for open
+ *
+ * This function maps a local fh to a path on a local filesystem.
+ * This is useful when the nfs client has the local server mounted - it can
+ * avoid all the NFS overhead with reads, writes and commits.
+ *
+ * On successful return, returned nfsd_file will have its nf_net member
+ * set. Caller (NFS client) is responsible for calling nfsd_serv_put and
+ * nfsd_file_put (via nfs_to->nfsd_file_put_local).
+ */
+struct nfsd_file *
+nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
+                  struct rpc_clnt *rpc_clnt, const struct cred *cred,
+                  const struct nfs_fh *nfs_fh, const fmode_t fmode)
+{
+       int mayflags = NFSD_MAY_LOCALIO;
+       struct svc_cred rq_cred;
+       struct svc_fh fh;
+       struct nfsd_file *localio;
+       __be32 beres;
+
+       if (nfs_fh->size > NFS4_FHSIZE)
+               return ERR_PTR(-EINVAL);
+
+       /* nfs_fh -> svc_fh */
+       fh_init(&fh, NFS4_FHSIZE);
+       fh.fh_handle.fh_size = nfs_fh->size;
+       memcpy(fh.fh_handle.fh_raw, nfs_fh->data, nfs_fh->size);
+
+       if (fmode & FMODE_READ)
+               mayflags |= NFSD_MAY_READ;
+       if (fmode & FMODE_WRITE)
+               mayflags |= NFSD_MAY_WRITE;
+
+       svcauth_map_clnt_to_svc_cred_local(rpc_clnt, cred, &rq_cred);
+
+       beres = nfsd_file_acquire_local(net, &rq_cred, dom,
+                                       &fh, mayflags, &localio);
+       if (beres)
+               localio = ERR_PTR(nfs_stat_to_errno(be32_to_cpu(beres)));
+
+       fh_put(&fh);
+       if (rq_cred.cr_group_info)
+               put_group_info(rq_cred.cr_group_info);
+
+       return localio;
+}
+EXPORT_SYMBOL_GPL(nfsd_open_local_fh);
index a228b9d6a6e1e79a91f3cf579a6d868605da8502..26f7b34d1a03057df52ffa66ccc83d2d14016f81 100644 (file)
@@ -217,6 +217,10 @@ struct nfsd_net {
        /* last time an admin-revoke happened for NFSv4.0 */
        time64_t                nfs40_last_revoke;
 
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+       /* Local clients to be invalidated when net is shut down */
+       struct list_head        local_clients;
+#endif
 };
 
 /* Simple check to find out if a given net was properly initialized */
index 64c1b4d649bc83c0ddd6c2d911fdb690bcbbf25c..3adbc05ebaac4cd9b7a005ef8ada17566c0f6bbf 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
+#include <linux/nfslocalio.h>
 
 #include "idmap.h"
 #include "nfsd.h"
@@ -2257,7 +2258,9 @@ static __net_init int nfsd_net_init(struct net *net)
        get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
        seqlock_init(&nn->writeverf_lock);
        nfsd_proc_stat_init(net);
-
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+       INIT_LIST_HEAD(&nn->local_clients);
+#endif
        return 0;
 
 out_repcache_error:
@@ -2268,6 +2271,22 @@ out_export_error:
        return retval;
 }
 
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+/**
+ * nfsd_net_pre_exit - Disconnect localio clients from net namespace
+ * @net: a network namespace that is about to be destroyed
+ *
+ * This invalidated ->net pointers held by localio clients
+ * while they can still safely access nn->counter.
+ */
+static __net_exit void nfsd_net_pre_exit(struct net *net)
+{
+       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+       nfs_uuid_invalidate_clients(&nn->local_clients);
+}
+#endif
+
 /**
  * nfsd_net_exit - Release the nfsd_net portion of a net namespace
  * @net: a network namespace that is about to be destroyed
@@ -2285,6 +2304,9 @@ static __net_exit void nfsd_net_exit(struct net *net)
 
 static struct pernet_operations nfsd_net_ops = {
        .init = nfsd_net_init,
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+       .pre_exit = nfsd_net_pre_exit,
+#endif
        .exit = nfsd_net_exit,
        .id   = &nfsd_net_id,
        .size = sizeof(struct nfsd_net),
@@ -2322,6 +2344,7 @@ static int __init init_nfsd(void)
        retval = genl_register_family(&nfsd_nl_family);
        if (retval)
                goto out_free_all;
+       nfsd_localio_ops_init();
 
        return 0;
 out_free_all:
index e339d04ff32f510859a22b6fb2cd91ae817c3ee9..c625966cfcf35ebc47248a6826306162b45cded5 100644 (file)
@@ -86,7 +86,8 @@ DEFINE_NFSD_XDR_ERR_EVENT(cant_encode);
                { NFSD_MAY_NOT_BREAK_LEASE,     "NOT_BREAK_LEASE" },    \
                { NFSD_MAY_BYPASS_GSS,          "BYPASS_GSS" },         \
                { NFSD_MAY_READ_IF_EXEC,        "READ_IF_EXEC" },       \
-               { NFSD_MAY_64BIT_COOKIE,        "64BIT_COOKIE" })
+               { NFSD_MAY_64BIT_COOKIE,        "64BIT_COOKIE" },       \
+               { NFSD_MAY_LOCALIO,             "LOCALIO" })
 
 TRACE_EVENT(nfsd_compound,
        TP_PROTO(
index 01947561d3752f15b0af2d10f143e63e2746c19f..3ff1465225569628cf5c18f1fc9ad4a4ca180a08 100644 (file)
@@ -33,6 +33,8 @@
 
 #define NFSD_MAY_64BIT_COOKIE          0x1000 /* 64 bit readdir cookies for >= NFSv3 */
 
+#define NFSD_MAY_LOCALIO               0x2000 /* for tracing, reflects when localio used */
+
 #define NFSD_MAY_CREATE                (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
 #define NFSD_MAY_REMOVE                (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
 
index 1a39d7d727bd483a903354b205f6e12de4e5f9b0..546f46b4e46f0c38617fbb68f8f42644748c8a8b 100644 (file)
@@ -6,6 +6,8 @@
 #ifndef __LINUX_NFSLOCALIO_H
 #define __LINUX_NFSLOCALIO_H
 
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+
 #include <linux/module.h>
 #include <linux/list.h>
 #include <linux/uuid.h>
@@ -58,4 +60,10 @@ struct nfsd_localio_operations {
 extern void nfsd_localio_ops_init(void);
 extern const struct nfsd_localio_operations *nfs_to;
 
+#else   /* CONFIG_NFS_LOCALIO */
+static inline void nfsd_localio_ops_init(void)
+{
+}
+#endif  /* CONFIG_NFS_LOCALIO */
+
 #endif  /* __LINUX_NFSLOCALIO_H */
This page took 0.075413 seconds and 4 git commands to generate.