From: Chuck Lever <cel@kernel.org>
To: Calum Mackay <calum.mackay@oracle.com>
Cc: <linux-nfs@vger.kernel.org>
Subject: [PATCH v1 3/8] pynfs: add test for a delegation request after a recall
Date: Thu, 8 Oct 2026 11:47:24 -0400 [thread overview]
Message-ID: <20261008154729.77787-4-cel@kernel.org> (raw)
In-Reply-To: <20261008154729.77787-1-cel@kernel.org>
RFC 8881 Section 10.4.7 names re-establishing a recalled delegation
as a use for a delegation want. No test asks for a delegation again
once the conflicting open is gone, so a server that never grants one
on that file again, or that misreports why, goes unnoticed.
Add DELEG29. It has a second client recall a read delegation and
then close its open. The first client then sends OPEN(CLAIM_FH) with
WANT_READ_DELEG three times, at 0, 65, and 100 seconds, and the
failure message gives the outcome of each. The test accepts a
delegation or OPEN_DELEGATE_NONE_EXT with WND4_CONTENTION. A reply
that names another reason, or that is a bare OPEN_DELEGATE_NONE,
fails it. The delays separate a refusal that ends from one that does
not. NFSD, for one, blocks a recalled file for at least 30 seconds
and ages the block only when a delegation request arrives, so the
block can outlast the second request.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
nfs4.1/server41tests/st_delegation.py | 73 +++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/nfs4.1/server41tests/st_delegation.py b/nfs4.1/server41tests/st_delegation.py
index 56d227127a15..8addde6c4fbf 100644
--- a/nfs4.1/server41tests/st_delegation.py
+++ b/nfs4.1/server41tests/st_delegation.py
@@ -727,3 +727,76 @@ def testClaimFHUpgradeReadDeleg(t, env):
check(res)
res = close_file(sess1, fh, stateid=upgraded)
check(res)
+
+def testClaimFHAfterRecall(t, env):
+ """OPEN(CLAIM_FH) with WANT_READ_DELEG after the delegation was
+ recalled and the conflicting open has been closed
+
+ The request is made three times: right after the conflicting open
+ is closed, 65 seconds later, and 100 seconds later. A server can
+ refuse a delegation on a recently recalled file for a while, and
+ it can age that refusal only when a request arrives. The last
+ request shows whether the refusal ever ends. Any reply may carry
+ a delegation or WND4_CONTENTION.
+
+ FLAGS: open deleg
+ CODE: DELEG30
+ """
+ recall = threading.Event()
+ def pre_hook(arg, env):
+ env.notify = recall.set
+ def post_hook(arg, env, res):
+ return res
+ name = env.testname(t)
+ sess1 = env.c1.new_client_session(b"%s_1" % name)
+ sess1.client.cb_pre_hook(OP_CB_RECALL, pre_hook)
+ sess1.client.cb_post_hook(OP_CB_RECALL, post_hook)
+ access = OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WANT_READ_DELEG
+ fh, stateid, deleg = __create_file_with_deleg(sess1, name, access)
+
+ sess2 = env.c1.new_client_session(b"%s_2" % name)
+ open_op = op.open(0, OPEN4_SHARE_ACCESS_BOTH, OPEN4_SHARE_DENY_NONE,
+ open_owner4(0, b"My Open Owner 2"),
+ openflag4(OPEN4_NOCREATE), open_claim4(CLAIM_NULL, name))
+ slot = sess2.compound_async(env.home + [open_op])
+ completed = recall.wait(2)
+ env.sleep(.1)
+ res = sess1.compound([op.putfh(fh), op.delegreturn(deleg.read.stateid)])
+ check(res)
+ res = sess2.listen(slot)
+ if not completed:
+ fail("delegation break not received")
+ for i in range(5):
+ if res.status != NFS4ERR_DELAY:
+ break
+ env.sleep(1)
+ res = sess2.compound(env.home + [open_op])
+ check(res)
+ res = close_file(sess2, fh, stateid=res.resarray[-1].stateid)
+ check(res)
+
+ passed = True
+ results = []
+ elapsed = 0
+ for delay in (0, 65, 100):
+ # Sleep in short steps so that the lease does not expire
+ while elapsed < delay:
+ env.sleep(5)
+ elapsed += 5
+ check(sess1.compound([]))
+ res = _open_claim_fh(sess1, fh, name, access)
+ check(res)
+ stateid = res.resarray[-1].stateid
+ deleg = res.resarray[-1].delegation
+ _delegreturn(sess1, fh, deleg)
+ if (not _got_deleg(deleg) and
+ (deleg.delegation_type != OPEN_DELEGATE_NONE_EXT or
+ deleg.ond_why != WND4_CONTENTION)):
+ passed = False
+ results.append("after %i seconds: %s" % (delay, _deleg_desc(deleg)))
+ results = "; ".join(results)
+ res = close_file(sess1, fh, stateid=stateid)
+ check(res)
+ if not passed:
+ fail(results)
+ print(results)
--
2.55.0
next prev parent reply other threads:[~2026-10-08 15:47 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-08 15:47 [PATCH v1 0/8] Test the WANT_*_DELEG OPEN flags Chuck Lever
2026-10-08 15:47 ` [PATCH v1 1/8] pynfs: add test for OPEN(CLAIM_FH) with WANT_READ_DELEG Chuck Lever
2026-10-09 7:58 ` Substituting WANT_DELEGATION with OPEN(CLAIM_FH)? " Lionel Cons
2026-10-08 15:47 ` [PATCH v1 2/8] pynfs: add test for OPEN(CLAIM_FH) delegation request on an open file Chuck Lever
2026-10-08 15:47 ` Chuck Lever [this message]
2026-10-08 15:47 ` [PATCH v1 4/8] pynfs: add tests for SETATTR by a delegation holder Chuck Lever
2026-10-08 15:47 ` [PATCH v1 5/8] pynfs: add test for WANT_OPEN_XOR_DELEGATION through CLAIM_FH Chuck Lever
2026-10-08 15:47 ` [PATCH v1 6/8] pynfs: add test for a CLAIM_FH delegation that outlives its open Chuck Lever
2026-10-08 15:47 ` [PATCH v1 7/8] pynfs: add tests for delegation upgrade and downgrade wants Chuck Lever
2026-10-08 15:47 ` [PATCH v1 8/8] pynfs: add test for OPEN with WANT_CANCEL Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261008154729.77787-4-cel@kernel.org \
--to=cel@kernel.org \
--cc=calum.mackay@oracle.com \
--cc=linux-nfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.