]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nfs: cache all open LOCALIO nfsd_file(s) in client
authorMike Snitzer <snitzer@kernel.org>
Sat, 16 Nov 2024 01:40:57 +0000 (20:40 -0500)
committerAnna Schumaker <anna.schumaker@oracle.com>
Tue, 14 Jan 2025 22:04:05 +0000 (17:04 -0500)
This commit switches from leaning heavily on NFSD's filecache (in
terms of GC'd nfsd_files) back to caching nfsd_files in the
client. A later commit will add the callback mechanism needed to
allow NFSD to force the NFS client to cleanup all cached nfsd_files.

Add nfs_fh_localio_init() and 'struct nfs_fh_localio' to cache opened
nfsd_file(s) (both a RO and RW nfsd_file is able to be opened and
cached for a given nfs_fh).

Update nfs_local_open_fh() to cache the nfsd_file once it is opened
using __nfs_local_open_fh().

Introduce nfs_close_local_fh() to clear the cached open nfsd_files and
call nfs_to_nfsd_file_put_local().

Refcounting is such that:
- nfs_local_open_fh() is paired with nfs_close_local_fh().
- __nfs_local_open_fh() is paired with nfs_to_nfsd_file_put_local().
- nfs_local_file_get() is paired with nfs_local_file_put().

Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
fs/nfs/flexfilelayout/flexfilelayout.c
fs/nfs/flexfilelayout/flexfilelayout.h
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/localio.c
fs/nfs/pagelist.c
fs/nfs/write.c
fs/nfs_common/nfslocalio.c
include/linux/nfs_fs.h
include/linux/nfslocalio.h

index f78115c6c2c12a9b6ebd4fb2072f0e1281fa5523..ce61bf1ada6c397566a7e6d597df3e27f530e0a5 100644 (file)
@@ -164,18 +164,17 @@ decode_name(struct xdr_stream *xdr, u32 *id)
 }
 
 static struct nfsd_file *
-ff_local_open_fh(struct nfs_client *clp, const struct cred *cred,
+ff_local_open_fh(struct pnfs_layout_segment *lseg, u32 ds_idx,
+                struct nfs_client *clp, const struct cred *cred,
                 struct nfs_fh *fh, fmode_t mode)
 {
-       if (mode & FMODE_WRITE) {
-               /*
-                * Always request read and write access since this corresponds
-                * to a rw layout.
-                */
-               mode |= FMODE_READ;
-       }
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+       struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
 
-       return nfs_local_open_fh(clp, cred, fh, mode);
+       return nfs_local_open_fh(clp, cred, fh, &mirror->nfl, mode);
+#else
+       return NULL;
+#endif
 }
 
 static bool ff_mirror_match_fh(const struct nfs4_ff_layout_mirror *m1,
@@ -247,6 +246,7 @@ static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags)
                spin_lock_init(&mirror->lock);
                refcount_set(&mirror->ref, 1);
                INIT_LIST_HEAD(&mirror->mirrors);
+               nfs_localio_file_init(&mirror->nfl);
        }
        return mirror;
 }
@@ -257,6 +257,7 @@ static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror)
 
        ff_layout_remove_mirror(mirror);
        kfree(mirror->fh_versions);
+       nfs_close_local_fh(&mirror->nfl);
        cred = rcu_access_pointer(mirror->ro_cred);
        put_cred(cred);
        cred = rcu_access_pointer(mirror->rw_cred);
@@ -1820,7 +1821,7 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
        hdr->mds_offset = offset;
 
        /* Start IO accounting for local read */
-       localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh, FMODE_READ);
+       localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh, FMODE_READ);
        if (localio) {
                hdr->task.tk_start = ktime_get();
                ff_layout_read_record_layoutstats_start(&hdr->task, hdr);
@@ -1896,7 +1897,7 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
        hdr->args.offset = offset;
 
        /* Start IO accounting for local write */
-       localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
+       localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh,
                                   FMODE_READ|FMODE_WRITE);
        if (localio) {
                hdr->task.tk_start = ktime_get();
@@ -1981,7 +1982,7 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
                data->args.fh = fh;
 
        /* Start IO accounting for local commit */
-       localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
+       localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh,
                                   FMODE_READ|FMODE_WRITE);
        if (localio) {
                data->task.tk_start = ktime_get();
index f84b3fb0dddd80decadf43a9f4dce8f99fe6e513..095df09017a575960a3baad25ceab3f4728a9be7 100644 (file)
@@ -83,6 +83,7 @@ struct nfs4_ff_layout_mirror {
        nfs4_stateid                    stateid;
        const struct cred __rcu         *ro_cred;
        const struct cred __rcu         *rw_cred;
+       struct nfs_file_localio         nfl;
        refcount_t                      ref;
        spinlock_t                      lock;
        unsigned long                   flags;
index 596f3517013728a3db818798f3f313b8cc842ebb..1aa67fca69b2fbd8afb1c51be78198220b1e13c7 100644 (file)
@@ -1137,6 +1137,8 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
        ctx->lock_context.open_context = ctx;
        INIT_LIST_HEAD(&ctx->list);
        ctx->mdsthreshold = NULL;
+       nfs_localio_file_init(&ctx->nfl);
+
        return ctx;
 }
 EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
@@ -1168,6 +1170,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
        nfs_sb_deactive(sb);
        put_rpccred(rcu_dereference_protected(ctx->ll_cred, 1));
        kfree(ctx->mdsthreshold);
+       nfs_close_local_fh(&ctx->nfl);
        kfree_rcu(ctx, rcu_head);
 }
 
index e564bd11ba607c5b954d91506bca230a619c0647..febf289a9e4c4889c8da2ccc0bf270848e599da7 100644 (file)
@@ -460,6 +460,7 @@ extern void nfs_local_probe(struct nfs_client *);
 extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
                                           const struct cred *,
                                           struct nfs_fh *,
+                                          struct nfs_file_localio *,
                                           const fmode_t);
 extern int nfs_local_doio(struct nfs_client *,
                          struct nfsd_file *,
@@ -475,7 +476,8 @@ static inline void nfs_local_disable(struct nfs_client *clp) {}
 static inline void nfs_local_probe(struct nfs_client *clp) {}
 static inline struct nfsd_file *
 nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
-                 struct nfs_fh *fh, const fmode_t mode)
+                 struct nfs_fh *fh, struct nfs_file_localio *nfl,
+                 const fmode_t mode)
 {
        return NULL;
 }
index 7191135b47a429efd30f4496347efd3d52aed7b6..7e432057c3a1f530c2dc832ba32ecd4a96c169c5 100644 (file)
@@ -211,27 +211,33 @@ void nfs_local_probe(struct nfs_client *clp)
 }
 EXPORT_SYMBOL_GPL(nfs_local_probe);
 
+static inline struct nfsd_file *nfs_local_file_get(struct nfsd_file *nf)
+{
+       return nfs_to->nfsd_file_get(nf);
+}
+
+static inline void nfs_local_file_put(struct nfsd_file *nf)
+{
+       nfs_to->nfsd_file_put(nf);
+}
+
 /*
- * nfs_local_open_fh - open a local filehandle in terms of nfsd_file
+ * __nfs_local_open_fh - open a local filehandle in terms of nfsd_file.
  *
- * Returns a pointer to a struct nfsd_file or NULL
+ * Returns a pointer to a struct nfsd_file or ERR_PTR.
+ * Caller must release returned nfsd_file with nfs_to_nfsd_file_put_local().
  */
-struct nfsd_file *
-nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
-                 struct nfs_fh *fh, const fmode_t mode)
+static struct nfsd_file *
+__nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
+                   struct nfs_fh *fh, struct nfs_file_localio *nfl,
+                   const fmode_t mode)
 {
        struct nfsd_file *localio;
-       int status;
-
-       if (!nfs_server_is_local(clp))
-               return NULL;
-       if (mode & ~(FMODE_READ | FMODE_WRITE))
-               return NULL;
 
        localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient,
-                                   cred, fh, mode);
+                                   cred, fh, nfl, mode);
        if (IS_ERR(localio)) {
-               status = PTR_ERR(localio);
+               int status = PTR_ERR(localio);
                trace_nfs_local_open_fh(fh, mode, status);
                switch (status) {
                case -ENOMEM:
@@ -240,10 +246,59 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
                        /* Revalidate localio, will disable if unsupported */
                        nfs_local_probe(clp);
                }
-               return NULL;
        }
        return localio;
 }
+
+/*
+ * nfs_local_open_fh - open a local filehandle in terms of nfsd_file.
+ * First checking if the open nfsd_file is already cached, otherwise
+ * must __nfs_local_open_fh and insert the nfsd_file in nfs_file_localio.
+ *
+ * Returns a pointer to a struct nfsd_file or NULL.
+ */
+struct nfsd_file *
+nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
+                 struct nfs_fh *fh, struct nfs_file_localio *nfl,
+                 const fmode_t mode)
+{
+       struct nfsd_file *nf, *new, __rcu **pnf;
+
+       if (!nfs_server_is_local(clp))
+               return NULL;
+       if (mode & ~(FMODE_READ | FMODE_WRITE))
+               return NULL;
+
+       if (mode & FMODE_WRITE)
+               pnf = &nfl->rw_file;
+       else
+               pnf = &nfl->ro_file;
+
+       new = NULL;
+       rcu_read_lock();
+       nf = rcu_dereference(*pnf);
+       if (!nf) {
+               rcu_read_unlock();
+               new = __nfs_local_open_fh(clp, cred, fh, nfl, mode);
+               if (IS_ERR(new))
+                       return NULL;
+               /* try to swap in the pointer */
+               spin_lock(&clp->cl_uuid.lock);
+               nf = rcu_dereference_protected(*pnf, 1);
+               if (!nf) {
+                       nf = new;
+                       new = NULL;
+                       rcu_assign_pointer(*pnf, nf);
+               }
+               spin_unlock(&clp->cl_uuid.lock);
+               rcu_read_lock();
+       }
+       nf = nfs_local_file_get(nf);
+       rcu_read_unlock();
+       if (new)
+               nfs_to_nfsd_file_put_local(new);
+       return nf;
+}
 EXPORT_SYMBOL_GPL(nfs_local_open_fh);
 
 static struct bio_vec *
@@ -347,7 +402,7 @@ nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
 {
        struct nfs_pgio_header *hdr = iocb->hdr;
 
-       nfs_to_nfsd_file_put_local(iocb->localio);
+       nfs_local_file_put(iocb->localio);
        nfs_local_iocb_free(iocb);
        nfs_local_hdr_release(hdr, hdr->task.tk_ops);
 }
@@ -694,7 +749,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
        if (status != 0) {
                if (status == -EAGAIN)
                        nfs_local_disable(clp);
-               nfs_to_nfsd_file_put_local(localio);
+               nfs_local_file_put(localio);
                hdr->task.tk_status = status;
                nfs_local_hdr_release(hdr, call_ops);
        }
@@ -745,7 +800,7 @@ nfs_local_release_commit_data(struct nfsd_file *localio,
                struct nfs_commit_data *data,
                const struct rpc_call_ops *call_ops)
 {
-       nfs_to_nfsd_file_put_local(localio);
+       nfs_local_file_put(localio);
        call_ops->rpc_call_done(&data->task, data);
        call_ops->rpc_release(data);
 }
index e27c07bd89290545339a71f235e040e08ade9fc7..11968dcb7243173132d1da09c7e6ee7eeca8f6a4 100644 (file)
@@ -961,8 +961,9 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
                struct nfs_client *clp = NFS_SERVER(hdr->inode)->nfs_client;
 
                struct nfsd_file *localio =
-                       nfs_local_open_fh(clp, hdr->cred,
-                                         hdr->args.fh, hdr->args.context->mode);
+                       nfs_local_open_fh(clp, hdr->cred, hdr->args.fh,
+                                         &hdr->args.context->nfl,
+                                         hdr->args.context->mode);
 
                if (NFS_SERVER(hdr->inode)->nfs_client->cl_minorversion)
                        task_flags = RPC_TASK_MOVEABLE;
index 50fa539611f5ecbc96a2ae7be90670001457dda4..aa3d8bea3ec06126d31e2a4eb525b43f3070a5bf 100644 (file)
@@ -1826,7 +1826,8 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
                task_flags = RPC_TASK_MOVEABLE;
 
        localio = nfs_local_open_fh(NFS_SERVER(inode)->nfs_client, data->cred,
-                                   data->args.fh, data->context->mode);
+                                   data->args.fh, &data->context->nfl,
+                                   data->context->mode);
        return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
                                   data->mds_ops, how,
                                   RPC_TASK_CRED_NOREF | task_flags, localio);
index abc132166742e7551a85ea70a5b90b5a025c4b23..35a2e48731df68626afb427bbd3f12057c50646d 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/nfslocalio.h>
 #include <linux/nfs3.h>
 #include <linux/nfs4.h>
-#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_fs.h>
 #include <net/netns/generic.h>
 
 MODULE_LICENSE("GPL");
@@ -151,9 +151,18 @@ void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
 }
 EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
 
+static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
+{
+       spin_lock(&nfs_uuid_lock);
+       if (!nfl->nfs_uuid)
+               rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
+       spin_unlock(&nfs_uuid_lock);
+}
+
 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
                   struct rpc_clnt *rpc_clnt, const struct cred *cred,
-                  const struct nfs_fh *nfs_fh, const fmode_t fmode)
+                  const struct nfs_fh *nfs_fh, struct nfs_file_localio *nfl,
+                  const fmode_t fmode)
 {
        struct net *net;
        struct nfsd_file *localio;
@@ -180,11 +189,50 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
                                             cred, nfs_fh, fmode);
        if (IS_ERR(localio))
                nfs_to_nfsd_net_put(net);
+       else
+               nfs_uuid_add_file(uuid, nfl);
 
        return localio;
 }
 EXPORT_SYMBOL_GPL(nfs_open_local_fh);
 
+void nfs_close_local_fh(struct nfs_file_localio *nfl)
+{
+       struct nfsd_file *ro_nf = NULL;
+       struct nfsd_file *rw_nf = NULL;
+       nfs_uuid_t *nfs_uuid;
+
+       rcu_read_lock();
+       nfs_uuid = rcu_dereference(nfl->nfs_uuid);
+       if (!nfs_uuid) {
+               /* regular (non-LOCALIO) NFS will hammer this */
+               rcu_read_unlock();
+               return;
+       }
+
+       ro_nf = rcu_access_pointer(nfl->ro_file);
+       rw_nf = rcu_access_pointer(nfl->rw_file);
+       if (ro_nf || rw_nf) {
+               spin_lock(&nfs_uuid_lock);
+               if (ro_nf)
+                       ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
+               if (rw_nf)
+                       rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
+
+               rcu_assign_pointer(nfl->nfs_uuid, NULL);
+               spin_unlock(&nfs_uuid_lock);
+               rcu_read_unlock();
+
+               if (ro_nf)
+                       nfs_to_nfsd_file_put_local(ro_nf);
+               if (rw_nf)
+                       nfs_to_nfsd_file_put_local(rw_nf);
+               return;
+       }
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(nfs_close_local_fh);
+
 /*
  * The NFS LOCALIO code needs to call into NFSD using various symbols,
  * but cannot be statically linked, because that will make the NFS
index 039898d70954f9a0ef29d4f4d3ae75bdbd6ce2a1..67ae2c3f41d2023dda496f6d7ba8fcadc7544d4a 100644 (file)
@@ -77,6 +77,23 @@ struct nfs_lock_context {
        struct rcu_head rcu_head;
 };
 
+struct nfs_file_localio {
+       struct nfsd_file __rcu *ro_file;
+       struct nfsd_file __rcu *rw_file;
+       struct list_head list;
+       void __rcu *nfs_uuid; /* opaque pointer to 'nfs_uuid_t' */
+};
+
+static inline void nfs_localio_file_init(struct nfs_file_localio *nfl)
+{
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+       nfl->ro_file = NULL;
+       nfl->rw_file = NULL;
+       INIT_LIST_HEAD(&nfl->list);
+       nfl->nfs_uuid = NULL;
+#endif
+}
+
 struct nfs4_state;
 struct nfs_open_context {
        struct nfs_lock_context lock_context;
@@ -87,15 +104,16 @@ struct nfs_open_context {
        struct nfs4_state *state;
        fmode_t mode;
 
+       int error;
        unsigned long flags;
 #define NFS_CONTEXT_BAD                        (2)
 #define NFS_CONTEXT_UNLOCK     (3)
 #define NFS_CONTEXT_FILE_OPEN          (4)
-       int error;
 
-       struct list_head list;
        struct nfs4_threshold   *mdsthreshold;
+       struct list_head list;
        struct rcu_head rcu_head;
+       struct nfs_file_localio nfl;
 };
 
 struct nfs_open_dir_context {
index 4d5583873f41877089e63d37caa01c1d7c772523..7cfc6720ed26daa2f75db09774c38bdbea1378f3 100644 (file)
@@ -6,10 +6,6 @@
 #ifndef __LINUX_NFSLOCALIO_H
 #define __LINUX_NFSLOCALIO_H
 
-
-/* nfsd_file structure is purposely kept opaque to NFS client */
-struct nfsd_file;
-
 #if IS_ENABLED(CONFIG_NFS_LOCALIO)
 
 #include <linux/module.h>
@@ -21,6 +17,7 @@ struct nfsd_file;
 #include <net/net_namespace.h>
 
 struct nfs_client;
+struct nfs_file_localio;
 
 /*
  * Useful to allow a client to negotiate if localio
@@ -52,6 +49,7 @@ extern struct nfsd_file *
 nfsd_open_local_fh(struct net *, struct auth_domain *, struct rpc_clnt *,
                   const struct cred *, const struct nfs_fh *,
                   const fmode_t) __must_hold(rcu);
+void nfs_close_local_fh(struct nfs_file_localio *);
 
 struct nfsd_localio_operations {
        bool (*nfsd_serv_try_get)(struct net *);
@@ -73,7 +71,8 @@ extern const struct nfsd_localio_operations *nfs_to;
 
 struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
                   struct rpc_clnt *, const struct cred *,
-                  const struct nfs_fh *, const fmode_t);
+                  const struct nfs_fh *, struct nfs_file_localio *,
+                  const fmode_t);
 
 static inline void nfs_to_nfsd_net_put(struct net *net)
 {
@@ -100,12 +99,15 @@ static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
 }
 
 #else   /* CONFIG_NFS_LOCALIO */
-static inline void nfsd_localio_ops_init(void)
+
+struct nfs_file_localio;
+static inline void nfs_close_local_fh(struct nfs_file_localio *nfl)
 {
 }
-static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
+static inline void nfsd_localio_ops_init(void)
 {
 }
+
 #endif  /* CONFIG_NFS_LOCALIO */
 
 #endif  /* __LINUX_NFSLOCALIO_H */