summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2026-01-07 10:27:15 +0300
committerAnna Schumaker <anna.schumaker@oracle.com>2026-01-20 22:49:47 +0300
commita13bc3286cb380aeca0d68dcb80d7611520e0b9e (patch)
tree0c16e3c1600d93bad20d222cecb58541da0e4d61
parent300ca8123c901605eda5eba33c83dc6eb03d0a3c (diff)
downloadlinux-a13bc3286cb380aeca0d68dcb80d7611520e0b9e.tar.xz
NFS: make nfs_mark_return_unreferenced_delegations less aggressive
Currently nfs_mark_return_unreferenced_delegations marks all open but not referenced delegations (i.e., those were found by a previous pass) as return on close, which means that we'll return them on close without a way out. Replace this with only iterating delegations that are on the LRU list, and avoid delegations that are in use by an open files to avoid this. Delegations that were never referenced while open still are be prime candidates for return from the LRU if the number of delegations is over the watermark, or otherwise will be returned by the next nfs_mark_return_unreferenced_delegations pass after they are closed. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
-rw-r--r--fs/nfs/delegation.c24
1 files changed, 17 insertions, 7 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 848cb55073fc..4d5f1f3162b0 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -1129,15 +1129,21 @@ void nfs_remove_bad_delegation(struct inode *inode,
}
EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
-static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
+static bool nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
{
- struct nfs_delegation *delegation;
+ struct nfs_delegation *d, *n;
+ bool marked = false;
- list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
- if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
+ spin_lock(&server->delegations_lock);
+ list_for_each_entry_safe(d, n, &server->delegations_lru, entry) {
+ if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &d->flags))
continue;
- nfs_mark_return_if_closed_delegation(server, delegation);
+ list_move_tail(&d->entry, &server->delegations_return);
+ marked = true;
}
+ spin_unlock(&server->delegations_lock);
+
+ return marked;
}
/**
@@ -1148,13 +1154,17 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
{
struct nfs_server *server;
+ bool marked = false;
rcu_read_lock();
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
- nfs_mark_return_unreferenced_delegations(server);
+ marked |= nfs_mark_return_unreferenced_delegations(server);
rcu_read_unlock();
- nfs_delegation_run_state_manager(clp);
+ if (marked) {
+ set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+ nfs4_schedule_state_manager(clp);
+ }
}
/**