NFSD: Add an NFSD_FILE_GC flag to enable nfsd_file garbage collection
authorChuck Lever <chuck.lever@oracle.com>
Fri, 28 Oct 2022 14:46:51 +0000 (10:46 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 18 Jan 2023 10:58:25 +0000 (11:58 +0100)
[ Upstream commit 4d1ea8455716ca070e3cd85767e6f6a562a58b1b ]

NFSv4 operations manage the lifetime of nfsd_file items they use by
means of NFSv4 OPEN and CLOSE. Hence there's no need for them to be
garbage collected.

Introduce a mechanism to enable garbage collection for nfsd_file
items used only by NFSv2/3 callers.

Note that the change in nfsd_file_put() ensures that both CLOSE and
DELEGRETURN will actually close out and free an nfsd_file on last
reference of a non-garbage-collected file.

Link: https://bugzilla.linux-nfs.org/show_bug.cgi?id=394
Suggested-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Tested-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: NeilBrown <neilb@suse.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Stable-dep-of: 0b3a551fa58b ("nfsd: fix handling of cached open files in nfsd4_open codepath")
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/nfsd/filecache.c
fs/nfsd/filecache.h
fs/nfsd/nfs3proc.c
fs/nfsd/trace.h
fs/nfsd/vfs.c

index babea79..cee4440 100644 (file)
@@ -63,6 +63,7 @@ struct nfsd_file_lookup_key {
        struct net                      *net;
        const struct cred               *cred;
        unsigned char                   need;
+       bool                            gc;
        enum nfsd_file_lookup_type      type;
 };
 
@@ -162,6 +163,8 @@ static int nfsd_file_obj_cmpfn(struct rhashtable_compare_arg *arg,
                        return 1;
                if (!nfsd_match_cred(nf->nf_cred, key->cred))
                        return 1;
+               if (!!test_bit(NFSD_FILE_GC, &nf->nf_flags) != key->gc)
+                       return 1;
                if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0)
                        return 1;
                break;
@@ -297,6 +300,8 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
                nf->nf_flags = 0;
                __set_bit(NFSD_FILE_HASHED, &nf->nf_flags);
                __set_bit(NFSD_FILE_PENDING, &nf->nf_flags);
+               if (key->gc)
+                       __set_bit(NFSD_FILE_GC, &nf->nf_flags);
                nf->nf_inode = key->inode;
                /* nf_ref is pre-incremented for hash table */
                refcount_set(&nf->nf_ref, 2);
@@ -428,16 +433,27 @@ nfsd_file_put_noref(struct nfsd_file *nf)
        }
 }
 
+static void
+nfsd_file_unhash_and_put(struct nfsd_file *nf)
+{
+       if (nfsd_file_unhash(nf))
+               nfsd_file_put_noref(nf);
+}
+
 void
 nfsd_file_put(struct nfsd_file *nf)
 {
        might_sleep();
 
-       nfsd_file_lru_add(nf);
-       if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) {
+       if (test_bit(NFSD_FILE_GC, &nf->nf_flags))
+               nfsd_file_lru_add(nf);
+       else if (refcount_read(&nf->nf_ref) == 2)
+               nfsd_file_unhash_and_put(nf);
+
+       if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
                nfsd_file_flush(nf);
                nfsd_file_put_noref(nf);
-       } else if (nf->nf_file) {
+       } else if (nf->nf_file && test_bit(NFSD_FILE_GC, &nf->nf_flags)) {
                nfsd_file_put_noref(nf);
                nfsd_file_schedule_laundrette();
        } else
@@ -1016,12 +1032,14 @@ nfsd_file_is_cached(struct inode *inode)
 
 static __be32
 nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
-                    unsigned int may_flags, struct nfsd_file **pnf, bool open)
+                    unsigned int may_flags, struct nfsd_file **pnf,
+                    bool open, bool want_gc)
 {
        struct nfsd_file_lookup_key key = {
                .type   = NFSD_FILE_KEY_FULL,
                .need   = may_flags & NFSD_FILE_MAY_MASK,
                .net    = SVC_NET(rqstp),
+               .gc     = want_gc,
        };
        bool open_retry = true;
        struct nfsd_file *nf;
@@ -1117,8 +1135,7 @@ open_file:
         * then unhash.
         */
        if (status != nfs_ok || key.inode->i_nlink == 0)
-               if (nfsd_file_unhash(nf))
-                       nfsd_file_put_noref(nf);
+               nfsd_file_unhash_and_put(nf);
        clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags);
        smp_mb__after_atomic();
        wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING);
@@ -1126,12 +1143,38 @@ open_file:
 }
 
 /**
+ * nfsd_file_acquire_gc - Get a struct nfsd_file with an open file
+ * @rqstp: the RPC transaction being executed
+ * @fhp: the NFS filehandle of the file to be opened
+ * @may_flags: NFSD_MAY_ settings for the file
+ * @pnf: OUT: new or found "struct nfsd_file" object
+ *
+ * The nfsd_file object returned by this API is reference-counted
+ * and garbage-collected. The object is retained for a few
+ * seconds after the final nfsd_file_put() in case the caller
+ * wants to re-use it.
+ *
+ * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
+ * network byte order is returned.
+ */
+__be32
+nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
+                    unsigned int may_flags, struct nfsd_file **pnf)
+{
+       return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, true);
+}
+
+/**
  * nfsd_file_acquire - Get a struct nfsd_file with an open file
  * @rqstp: the RPC transaction being executed
  * @fhp: the NFS filehandle of the file to be opened
  * @may_flags: NFSD_MAY_ settings for the file
  * @pnf: OUT: new or found "struct nfsd_file" object
  *
+ * The nfsd_file_object returned by this API is reference-counted
+ * but not garbage-collected. The object is unhashed after the
+ * final nfsd_file_put().
+ *
  * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
  * network byte order is returned.
  */
@@ -1139,7 +1182,7 @@ __be32
 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
                  unsigned int may_flags, struct nfsd_file **pnf)
 {
-       return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true);
+       return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, false);
 }
 
 /**
@@ -1149,6 +1192,10 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
  * @may_flags: NFSD_MAY_ settings for the file
  * @pnf: OUT: new or found "struct nfsd_file" object
  *
+ * The nfsd_file_object returned by this API is reference-counted
+ * but not garbage-collected. The object is released immediately
+ * one RCU grace period after the final nfsd_file_put().
+ *
  * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
  * network byte order is returned.
  */
@@ -1156,7 +1203,7 @@ __be32
 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                 unsigned int may_flags, struct nfsd_file **pnf)
 {
-       return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false);
+       return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false, false);
 }
 
 /*
index 6b012ea..b7efb2c 100644 (file)
@@ -38,6 +38,7 @@ struct nfsd_file {
 #define NFSD_FILE_HASHED       (0)
 #define NFSD_FILE_PENDING      (1)
 #define NFSD_FILE_REFERENCED   (2)
+#define NFSD_FILE_GC           (3)
        unsigned long           nf_flags;
        struct inode            *nf_inode;      /* don't deref */
        refcount_t              nf_ref;
@@ -55,6 +56,8 @@ void nfsd_file_put(struct nfsd_file *nf);
 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
 void nfsd_file_close_inode_sync(struct inode *inode);
 bool nfsd_file_is_cached(struct inode *inode);
+__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
+                 unsigned int may_flags, struct nfsd_file **nfp);
 __be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
                  unsigned int may_flags, struct nfsd_file **nfp);
 __be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
index ff29205..d01b29a 100644 (file)
@@ -772,8 +772,8 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
                                (unsigned long long) argp->offset);
 
        fh_copy(&resp->fh, &argp->fh);
-       resp->status = nfsd_file_acquire(rqstp, &resp->fh, NFSD_MAY_WRITE |
-                                        NFSD_MAY_NOT_BREAK_LEASE, &nf);
+       resp->status = nfsd_file_acquire_gc(rqstp, &resp->fh, NFSD_MAY_WRITE |
+                                           NFSD_MAY_NOT_BREAK_LEASE, &nf);
        if (resp->status)
                goto out;
        resp->status = nfsd_commit(rqstp, &resp->fh, nf, argp->offset,
index d4b6839..3fcfeb7 100644 (file)
@@ -817,7 +817,8 @@ DEFINE_CLID_EVENT(confirmed_r);
        __print_flags(val, "|",                                         \
                { 1 << NFSD_FILE_HASHED,        "HASHED" },             \
                { 1 << NFSD_FILE_PENDING,       "PENDING" },            \
-               { 1 << NFSD_FILE_REFERENCED,    "REFERENCED"})
+               { 1 << NFSD_FILE_REFERENCED,    "REFERENCED"},          \
+               { 1 << NFSD_FILE_GC,            "GC"})
 
 DECLARE_EVENT_CLASS(nfsd_file_class,
        TP_PROTO(struct nfsd_file *nf),
index f191983..2934ab1 100644 (file)
@@ -1085,7 +1085,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
        __be32 err;
 
        trace_nfsd_read_start(rqstp, fhp, offset, *count);
-       err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_READ, &nf);
+       err = nfsd_file_acquire_gc(rqstp, fhp, NFSD_MAY_READ, &nf);
        if (err)
                return err;
 
@@ -1117,7 +1117,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 
        trace_nfsd_write_start(rqstp, fhp, offset, *cnt);
 
-       err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_WRITE, &nf);
+       err = nfsd_file_acquire_gc(rqstp, fhp, NFSD_MAY_WRITE, &nf);
        if (err)
                goto out;