Re: [nfsv4] Re: simple NFSv4.1/4.2 test of remove while holding a delegation
Jeff Layton <[email protected]> Tue, 10 Jun 2025 08:59:42 -0400
| Newsgroups | gmane.linux.nfs,gmane.ietf.nfsv4 |
|---|---|
| Message-ID | <[email protected]> |
On Tue, 2025-06-10 at 05:42 -0700, Rick Macklem wrote: > On Tue, Jun 10, 2025 at 4:51=E2=80=AFAM Jeff Layton <[email protected]> = wrote: > >=20 > > On Mon, 2025-06-09 at 18:06 -0700, Rick Macklem wrote: > > > On Mon, Jun 9, 2025 at 5:17=E2=80=AFPM Dai Ngo <[email protected]> w= rote: > > > >=20 > > > > On 6/9/25 4:35 PM, Rick Macklem wrote: > > > > > Hi, > > > > >=20 > > > > > I hope you don't mind a cross-post, but I thought both groups > > > > > might find this interesting... > > > > >=20 > > > > > I have been creating a compound RPC that does REMOVE and > > > > > then tries to determine if the file object has been removed and > > > > > I was surprised to see quite different results from the Linux knf= sd > > > > > and Solaris 11.4 NFSv4.1/4.2 servers. I think both these servers > > > > > provide FH4_PERSISTENT file handles, although I suppose I > > > > > should check that? > > > > >=20 > > > > > First, the test OPEN/CREATEs a regular file called "foo" (only on= e > > > > > hard link) and acquires a write delegation for it. > > > > > Then a compound does the following: > > > > > ... > > > > > REMOVE foo > > > > > PUTFH fh for foo > > > > > GETATTR > > > > >=20 > > > > > For the Solaris 11.4 server, the server CB_RECALLs the > > > > > delegation and then replies NFS4ERR_STALE for the PUTFH above. > > > > > (The FreeBSD server currently does the same.) > > > > >=20 > > > > > For a fairly recent Linux (6.12) knfsd, the above replies NFS_OK > > > > > with nlinks =3D=3D 0 in the GETATTR reply. > > > > >=20 > > > > > Hmm. So I've looked in RFC8881 (I'm terrible at reading it so I > > > > > probably missed something) and I cannot find anything that states > > > > > either of the above behaviours is incorrect. > >=20 > > This seems outside the scope of the spec. What you're probably seeing > > is just differences in the implementation details of the two servers. > >=20 > > > > > (NFS4ERR_STALE is listed as an error code for PUTFH, but the > > > > > description of PUTFH only says that it sets the CFH to the fh arg= . > > > > > It does not say anything w.r.t. the fh arg. needing to be for a f= ile > > > > > that still exists.) Neither of these servers sets > > > > > OPEN4_RESULT_PRESERVE_UNLINKED in the OPEN reply. > > > > >=20 > > > > > So, it looks like "file object no longer exists" is indicated eit= her > > > > > by a NFS4ERR_STALE reply to either PUTFH or GETATTR > > > > > OR > > > > > by a successful reply, but with nlinks =3D=3D 0 for the GETATTR r= eply. > > > > >=20 > > > > > To be honest, I kinda like the Linux knfsd version, but I am wond= ering > > > > > if others think that both of these replies is correct? > > > > >=20 > > > > > Also, is the CB_RECALL needed when the delegation is held by > > > > > the same client as the one doing the REMOVE? > > > >=20 > > > > The Linux NFSD detects the delegation belongs to the same client th= at > > > > causes the conflict (due to REMOVE) and skips the CB_RECALL. This i= s > > > > an optimization based on the assumption that the client would handl= e > > > > the conflict locally. > > > And then what does the server do with the delegation? > > > - Does it just discard it, since the file object has been deleted? > > > OR > > > - Does it guarantee that a DELEGRETURN done after the REMOVE will > > > still work (which seems to be the case for the 6.12 server I am usi= ng for > > > testing). > > >=20 > >=20 > > The latter. The file on the server is still being held open by virtue > > of the fact that the client holds a delegation stateid on it. > >=20 > > The inode will still exist in core (with nlinks =3D=3D 0) until its las= t > > reference is released (here, when the client does the final > > DELEGRETURN). Aside from the fact that it's now disconnected from the > > filesystem namespace, it's still "alive", and reachable via filehandle. > Thanks for the info. (I had a hunch it was held by the delegation.) > I'll guess that implies that LINK could still be done, bumping nlink to 1 > before the DELEGRETURN? That means that nlink =3D=3D 0 only guarantees > that the file object will be deleted if the client holds a write delegati= on and > ensures that LINK is not allowed before DELEGRETURN. >=20 I believe that LINK is actually prevented at that point. The VFS only allows flink() to work when nlink =3D=3D 0 on O_TMPFILE files, IIRC. IMO, that's a Linux implementation detail rather than something the NFS protocol or POSIX requires. > Although trying to avoid the WRITE, WRITE,...COMMIT to the server > just before a file is deleted seems worth the effort, it never seems to > be as easy as you'd think. >=20 Definitely. The problem of course is that you can't really know whether a REMOVE will actually delete the file. It'll remove the name, but link() could have raced in, and at that point you sort of have to do the writes. > >=20 > > > >=20 > > > > If the REMOVE was done by another client, the REMOVE will not compl= ete > > > > until the delegation is returned. If the PUTFH comes after the REMO= VE > > > > was completed, it'll fail with NFS4ERR_STALE since the file, speci= fied > > > > by the file handle, no longer exists. > > > Assuming the statement w.r.t. "fail with NFS4ERR_STALE" only applies = to > > > "REMOVE done by another client" then that sounds fine. > > > However if the "fail with NFS4ERR_STALE is supposed for happen after > > > REMOVE for same client" then that is not what I am seeing. > > > If you are curious, the packet trace is here. (Look at packet#58). > > > https://people.freebsd.org/~rmacklem/linux-remove.pcap > > >=20 > > > Btw, in case you are curious why I am doing this testing, I am trying > > > to figure out a good way for the FreeBSD client to handle temporary > > > files. Typically on POSIX they are done via the syscalls: > > >=20 > > > fd =3D open("foo", O_CREATE ...); > > > unlink("foo"); > > > write(fd,..), write(fd,..)... > > > read(fd,...), read(fd,...)... > > > close(fd); > > >=20 > > > If this happens quickly and is not too much writing, the writes > > > copy data into buffers/pages, the reads read the data out of > > > the pages and then it all gets deleted. > > >=20 > >=20 > > Yep, common pattern. > >=20 > > > Unfortunately, the CB_RECALL forces the NFSv4.n client > > > to do WRITE, WRITE,..COMMIT and then DELEGRETURN. > > > Then the REMOVE throws all the data away on the NFSv4.n > > > server. > > > --> As such, I really like not doing the CB_RECALL for "same client". > > > My concern is "what happens to the delegation after the file object (= "foo") > > > gets deleted? > > > It either needs to be thrown away by the NFSv4.n server or the > > > PUTFH, DELEGRETURN needs to work after the REMOVE. > >=20 > > I think the latter. A REMOVE just removes the filename from the > > namespace. What happens to the underlying inode/vnode/whathaveyou is > > undefined by the protocol. The delegation is effectively holding the > > file open, so it needs to continue to exist on the server, just as the > > file "foo" in your example above must exist after the unlink(). > >=20 > > > Otherwise, the NFSv4.n server may get constipated by the delegations, > > > which might be called stale, since the file object has been deleted. > > >=20 > > > --> I can do PUTFH, GETATTR after REMOVE in the same compound, > > > to find out if the file object has been deleted. But then, if a > > > PUTFH, DELEGRETURN fails with NFS4ERR_STALE, can I get > > > away with saying "the server should just discard the delegation = as > > > the client already has done so??. > > >=20 > > > Thanks for your comments, rick > > >=20 > >=20 > > If you still have an outstanding delegation after a REMOVE, then > > returning ESTALE on the filehandle at that point seems wrong. The > > delegation still exists, so the underlying filehandle should still > > exist. > >=20 > > Linux doesn't generally throw back an NFS4ERR_STALE until it just can't > > find the inode at all anymore. A dentry holds a reference to the inode, > > and open files hold a reference to the dentry. The remove just > > disconnects the dentry from the namespace and drops its refcount. When > > the DELEGRETURN issues the last close, the inode gets cleaned up and at > > that point you can't find it by filehandle anymore. > >=20 > > You probably want to aim for similar behavior in FreeBSD? > I'm not sure. So long as the server guarantees that the file object has b= een > deleted by the REMOVE, throwing NFS4ERR_STALE seems a reasonable alternat= ive? >=20 At that point won't you have to start returning writeback errors back to userland? What if you do this? fd =3D open("foo", O_CREATE ...); unlink("foo"); write(fd,..), write(fd,..)... fsync(fd); In the absence of a delegation, won't the fsync get back an error here because the file is now stale? > Note that the FreeBSD server does not handle NFSv4 OPENs and > DELEGATIONs like a POSIX open(2), so the file handle is no longer > valid once nlink =3D=3D 0 on the underlying vnode/inode. > (Again, I don't think there is anything in RFC8881 that specifies > what is correct behaviour for this?) >=20 > It's a case where I'd like to be able to test against all extant servers, > but none of the others show up at Bakeathons these days. Sigh. >=20 > Thanks for your comments, rick >=20 > >=20 > > > >=20 > > > > -Dai > > > >=20 > > > > > (I don't think it is, but there is a discussion in 18.25.4 which = says > > > > > "When the determination above cannot be made definitively because > > > > > delegations are being held, they MUST be recalled.." but everythi= ng > > > > > above that is a may/MAY, so it is not obvious to me if a server r= eally > > > > > needs to case?) > > > > >=20 > > > > > Any comments? Thanks, rick > > > > > ps: I am amazed when I learn these things about NFSv4.n after all > > > > > these years. > > > > >=20 > >=20 > >=20 > > -- > > Jeff Layton <[email protected]> --=20 Jeff Layton <[email protected]>