When processing an out-of-order OPEN stateid on an NFSv4.1 or later mount, nfs_set_open_stateid_locked() waits for stateid updates to be processed in order. A five-second timeout is intended to prevent indefinite waits when preceding updates never arrive. However, each iteration calls schedule_timeout() with a fresh five-second timeout. An update to the shared open state can wake the caller without satisfying its ordering check. The remaining timeout is then discarded, and the next iteration starts another full wait. Under sustained concurrent OPEN/CLOSE activity on the same open state, repeated wakeups can prevent the timeout from ever expiring. Some tasks can remain stuck in update_open_stateid() while others continue making progress. Compute a single deadline on entry and pass only the remaining time to schedule_timeout(). Once the deadline expires, take the existing timeout path rather than starting another wait. This prevents wakeups from extending the wait indefinitely. Observed on Linux 6.8.0 and reproduced on 7.3.0 with 96 concurrent O_RDWR|O_TRUNC open/close loops on one NFSv4.1/TCP file (nconnect=16). Ten workers made no progress for over 300 seconds while others ran. With the fix, all workers resumed after transient stalls. Fixes: c9399f21c215 ("NFSv4: Fix OPEN / CLOSE race") Cc: stable@vger.kernel.org Signed-off-by: Tim Menninger --- fs/nfs/nfs4proc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e1018b03c1cbd..f2cb8e788c017 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1722,6 +1722,8 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, { DEFINE_WAIT(wait); int status = 0; + unsigned long deadline = jiffies + 5 * HZ; + signed long remaining; for (;;) { if (nfs_stateid_is_sequential(state, stateid)) @@ -1756,7 +1758,8 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, if (!fatal_signal_pending(current) && !nfs_current_task_exiting()) { - if (schedule_timeout(5*HZ) == 0) + remaining = (long)(deadline - jiffies); + if (remaining <= 0 || schedule_timeout(remaining) == 0) status = -EAGAIN; else status = 0; -- 2.53.0