]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
NFSv4: Allow FREE_STATEID to clean up delegations
authorBenjamin Coddington <bcodding@redhat.com>
Thu, 1 May 2025 12:29:42 +0000 (08:29 -0400)
committerAnna Schumaker <anna.schumaker@oracle.com>
Wed, 28 May 2025 21:17:13 +0000 (17:17 -0400)
The NFS client's list of delegations can grow quite large (well beyond the
delegation watermark) if the server is revoking or there are repeated
events that expire state.  Once this happens, the revoked delegations can
cause a performance problem for subsequent walks of the
servers->delegations list when the client tries to test and free state.

If we can determine that the FREE_STATEID operation has completed without
error, we can prune the delegation from the list.

Since the NFS client combines TEST_STATEID with FREE_STATEID in its minor
version operations, there isn't an easy way to communicate success of
FREE_STATEID.  Rather than re-arrange quite a number of calling paths to
break out the separate procedures, let's signal the success of FREE_STATEID
by setting the stateid's type.

Set NFS4_FREED_STATEID_TYPE for stateids that have been successfully
discarded from the server, and use that type to signal that the delegation
can be cleaned up.

Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
fs/nfs/delegation.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
include/linux/nfs4.h

index 8bdbc4dca89ca62e5763d6aaf6df3938b20c2128..10ef46e29b2544920c026f023885301d5d8e22cd 100644 (file)
@@ -1021,13 +1021,6 @@ out:
                nfs_inode_find_state_and_recover(inode, stateid);
 }
 
-void nfs_remove_bad_delegation(struct inode *inode,
-               const nfs4_stateid *stateid)
-{
-       nfs_revoke_delegation(inode, stateid);
-}
-EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
-
 void nfs_delegation_mark_returned(struct inode *inode,
                const nfs4_stateid *stateid)
 {
@@ -1069,6 +1062,24 @@ out_rcu_unlock:
        nfs_inode_find_state_and_recover(inode, stateid);
 }
 
+/**
+ * nfs_remove_bad_delegation - handle delegations that are unusable
+ * @inode: inode to process
+ * @stateid: the delegation's stateid
+ *
+ * If the server ACK-ed our FREE_STATEID then clean
+ * up the delegation, else mark and keep the revoked state.
+ */
+void nfs_remove_bad_delegation(struct inode *inode,
+               const nfs4_stateid *stateid)
+{
+       if (stateid && stateid->type == NFS4_FREED_STATEID_TYPE)
+               nfs_delegation_mark_returned(inode, stateid);
+       else
+               nfs_revoke_delegation(inode, stateid);
+}
+EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
+
 /**
  * nfs_expire_unused_delegation_types
  * @clp: client to process
index 7d383d29a99552317eabeee6757bc0a85137e4f6..d3ca91f60fc1e790a9d5302b0a4b4ef81ccb8671 100644 (file)
@@ -67,8 +67,7 @@ struct nfs4_minor_version_ops {
        void    (*free_lock_state)(struct nfs_server *,
                        struct nfs4_lock_state *);
        int     (*test_and_free_expired)(struct nfs_server *,
-                                        const nfs4_stateid *,
-                                        const struct cred *);
+                                        nfs4_stateid *, const struct cred *);
        struct nfs_seqid *
                (*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
        void    (*session_trunk)(struct rpc_clnt *clnt,
index d9c7035d5771013a78c88350750cccf132c43ba4..6471db331863b585b04088d2372fece0324a4e84 100644 (file)
@@ -105,7 +105,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
                bool is_privileged);
 static int nfs41_test_stateid(struct nfs_server *, const nfs4_stateid *,
                              const struct cred *);
-static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
+static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
                              const struct cred *, bool);
 #endif
 
@@ -2903,16 +2903,14 @@ static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
 }
 
 static int nfs40_test_and_free_expired_stateid(struct nfs_server *server,
-                                              const nfs4_stateid *stateid,
-                                              const struct cred *cred)
+                                              nfs4_stateid *stateid, const struct cred *cred)
 {
        return -NFS4ERR_BAD_STATEID;
 }
 
 #if defined(CONFIG_NFS_V4_1)
 static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
-                                              const nfs4_stateid *stateid,
-                                              const struct cred *cred)
+                                              nfs4_stateid *stateid, const struct cred *cred)
 {
        int status;
 
@@ -2921,6 +2919,7 @@ static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
                break;
        case NFS4_INVALID_STATEID_TYPE:
        case NFS4_SPECIAL_STATEID_TYPE:
+       case NFS4_FREED_STATEID_TYPE:
                return -NFS4ERR_BAD_STATEID;
        case NFS4_REVOKED_STATEID_TYPE:
                goto out_free;
@@ -10617,7 +10616,7 @@ static const struct rpc_call_ops nfs41_free_stateid_ops = {
  * Note: this function is always asynchronous.
  */
 static int nfs41_free_stateid(struct nfs_server *server,
-               const nfs4_stateid *stateid,
+               nfs4_stateid *stateid,
                const struct cred *cred,
                bool privileged)
 {
@@ -10657,6 +10656,7 @@ static int nfs41_free_stateid(struct nfs_server *server,
        if (IS_ERR(task))
                return PTR_ERR(task);
        rpc_put_task(task);
+       stateid->type = NFS4_FREED_STATEID_TYPE;
        return 0;
 }
 
index 07635f87a3fd77802924846ded1efcdc8c1208d6..e947af6a3684a4ae4abedbc56ff1811d15c23f3c 100644 (file)
@@ -72,6 +72,7 @@ struct nfs4_stateid_struct {
                NFS4_LAYOUT_STATEID_TYPE,
                NFS4_PNFS_DS_STATEID_TYPE,
                NFS4_REVOKED_STATEID_TYPE,
+               NFS4_FREED_STATEID_TYPE,
        } type;
 };