The branch main has been updated by rmacklem:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=171f66b0c2cab0b75c37eb1c815273da5282ff7e

commit 171f66b0c2cab0b75c37eb1c815273da5282ff7e
Author:     Rick Macklem <rmack...@freebsd.org>
AuthorDate: 2025-06-29 21:15:02 +0000
Commit:     Rick Macklem <rmack...@freebsd.org>
CommitDate: 2025-06-29 21:15:02 +0000

    nfscl: Optimize NFSv4 remove and rename for nocto option
    
    Without this patch a NFSv4.1/4.2 mount will return any
    delegations before doing a remove or rename over the file.
    This includes writing all dirty buffers back to the NFSv4
    server before doing the remove/rename.  For the common case
    where the file is being removed (the exception is when the
    file has multiple hard links), all the writing is wasted
    overhead.  Further, if the file has multiple hard links,
    the delegation is still valid and does not need to be returned.
    
    For NFSv4.0, a server did not know which client was doing
    the remove/rename and, as such, had no choice but to recall
    the delegation.  However, this is not the case for NFSv4.1/4.2.
    
    This patch avoids returning the delegation(s) for NFSv4.1/4.2
    mounts if the "nocto" mount option is specified.
    Also, with this patch, the NFSv4.1/4.2 compound tests to see if
    the file has actually been removed and handles the delegation(s)
    appropriately.
    
    This new behaviour is only enabled when the "nocto" mount option
    is specified and will only improve performance when NFSv4.1/4.2
    servers are issuing delegations and not recalling them for remove
    or rename being done by the same client as the one holding the
    delegation.
---
 sys/fs/nfs/nfs.h                |   2 +
 sys/fs/nfs/nfs_commonsubs.c     |   4 +-
 sys/fs/nfs/nfs_var.h            |  15 ++--
 sys/fs/nfsclient/nfs_clnode.c   |  24 ++++--
 sys/fs/nfsclient/nfs_clrpcops.c | 158 +++++++++++++++++++++++++++++++++-------
 sys/fs/nfsclient/nfs_clstate.c  |  40 ++++++----
 sys/fs/nfsclient/nfs_clvnops.c  | 126 +++++++++++++++++++++++++-------
 7 files changed, 286 insertions(+), 83 deletions(-)

diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h
index 9b09520b3257..e6a125b388a8 100644
--- a/sys/fs/nfs/nfs.h
+++ b/sys/fs/nfs/nfs.h
@@ -865,6 +865,8 @@ struct nfsslot {
 /* Enumerated type for nfsuserd state. */
 typedef enum { NOTRUNNING=0, STARTSTOP=1, RUNNING=2 } nfsuserd_state;
 
+typedef enum { UNKNOWN=0, DELETED=1, NLINK_ZERO=2, VALID=3 } nfsremove_status;
+
 #endif /* _KERNEL */
 
 #endif /* _NFS_NFS_H */
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 68d2f7d993f3..f46b0d282861 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -251,9 +251,9 @@ static struct {
        { NFSV4OP_CREATE, 5, "Create", 6, },
        { NFSV4OP_CREATE, 1, "Create", 6, },
        { NFSV4OP_CREATE, 3, "Create", 6, },
+       { NFSV4OP_REMOVE, 3, "Remove", 6, },
        { NFSV4OP_REMOVE, 1, "Remove", 6, },
-       { NFSV4OP_REMOVE, 1, "Remove", 6, },
-       { NFSV4OP_SAVEFH, 5, "Rename", 6, },
+       { NFSV4OP_SAVEFH, 7, "Rename", 6, },
        { NFSV4OP_SAVEFH, 6, "Link", 4, },
        { NFSV4OP_READDIR, 2, "Readdir", 7, },
        { NFSV4OP_READDIR, 2, "Readdir", 7, },
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 7206d12bd6fa..f59a898369e8 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -483,11 +483,13 @@ int nfsrpc_mknod(vnode_t, char *, int, struct vattr *, 
u_int32_t,
 int nfsrpc_create(vnode_t, char *, int, struct vattr *, nfsquad_t,
     int, struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
     struct nfsfh **, int *, int *);
-int nfsrpc_remove(vnode_t, char *, int, vnode_t, struct ucred *, NFSPROC_T *,
-    struct nfsvattr *, int *);
-int nfsrpc_rename(vnode_t, vnode_t, char *, int, vnode_t, vnode_t, char *, int,
-    struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
-    int *, int *);
+int nfsrpc_remove(struct vnode *, char *, int, struct vnode *,
+    struct nfsvattr *, int *, nfsremove_status *, struct nfsvattr *, int *,
+    struct ucred *, NFSPROC_T *);
+int nfsrpc_rename(struct vnode *, struct vnode *, char *, int, struct vnode *,
+    struct vnode *, char *, int, nfsremove_status *, struct nfsvattr *,
+    struct nfsvattr *, int *, int *, struct nfsvattr *, int *, struct ucred *,
+    NFSPROC_T *);
 int nfsrpc_link(vnode_t, vnode_t, char *, int,
     struct ucred *, NFSPROC_T *, struct nfsvattr *, struct nfsvattr *,
     int *, int *);
@@ -616,7 +618,7 @@ void nfscl_lockinit(struct nfsv4lock *);
 void nfscl_lockexcl(struct nfsv4lock *, void *);
 void nfscl_lockunlock(struct nfsv4lock *);
 void nfscl_lockderef(struct nfsv4lock *);
-void nfscl_delegreturnvp(vnode_t, NFSPROC_T *);
+void nfscl_delegreturnvp(struct vnode *, bool, NFSPROC_T *);
 void nfscl_docb(struct nfsrv_descript *, NFSPROC_T *);
 void nfscl_releasealllocks(struct nfsclclient *, vnode_t, NFSPROC_T *, void *,
     int);
@@ -656,6 +658,7 @@ void nfscl_freelayout(struct nfscllayout *);
 void nfscl_freeflayout(struct nfsclflayout *);
 void nfscl_freedevinfo(struct nfscldevinfo *);
 int nfscl_layoutcommit(vnode_t, NFSPROC_T *);
+void nfscl_startdelegrecall(struct nfsclclient *, struct nfsfh *);
 
 /* nfs_clport.c */
 int nfscl_nget(mount_t, vnode_t, struct nfsfh *,
diff --git a/sys/fs/nfsclient/nfs_clnode.c b/sys/fs/nfsclient/nfs_clnode.c
index be2024730cf0..f85f961d424e 100644
--- a/sys/fs/nfsclient/nfs_clnode.c
+++ b/sys/fs/nfsclient/nfs_clnode.c
@@ -205,7 +205,7 @@ nfs_freesillyrename(void *arg, __unused int pending)
 }
 
 static void
-ncl_releasesillyrename(struct vnode *vp, struct thread *td)
+ncl_releasesillyrename(struct vnode *vp, bool flushed, struct thread *td)
 {
        struct nfsnode *np;
        struct sillyrename *sp;
@@ -220,7 +220,8 @@ ncl_releasesillyrename(struct vnode *vp, struct thread *td)
                sp = NULL;
        if (sp != NULL) {
                NFSUNLOCKNODE(np);
-               (void) ncl_vinvalbuf(vp, 0, td, 1);
+               if (flushed)
+                       (void)ncl_vinvalbuf(vp, 0, td, 1);
                /*
                 * Remove the silly file that was rename'd earlier
                 */
@@ -238,9 +239,13 @@ ncl_inactive(struct vop_inactive_args *ap)
        struct vnode *vp = ap->a_vp;
        struct nfsnode *np;
        struct thread *td;
+       struct nfsmount *nmp;
+       bool flushed;
 
        td = curthread;
        np = VTONFS(vp);
+       nmp = VFSTONFS(vp->v_mount);
+       flushed = true;
        if (NFS_ISV4(vp) && vp->v_type == VREG) {
                NFSLOCKNODE(np);
                np->n_openstateid = NULL;
@@ -251,13 +256,18 @@ ncl_inactive(struct vop_inactive_args *ap)
                 * buffers/pages must be flushed before the close, so that the
                 * stateid is available for the writes.
                 */
-               vnode_pager_clean_sync(vp);
-               (void)ncl_flush(vp, MNT_WAIT, td, 1, 0);
+               if ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || !NFSHASNFSV4N(nmp) ||
+                   nfscl_mustflush(vp) != 0) {
+                       vnode_pager_clean_sync(vp);
+                       (void)ncl_flush(vp, MNT_WAIT, td, 1, 0);
+               } else {
+                       flushed = false;
+               }
                (void)nfsrpc_close(vp, 1, td);
        }
 
        NFSLOCKNODE(np);
-       ncl_releasesillyrename(vp, td);
+       ncl_releasesillyrename(vp, flushed, td);
 
        /*
         * NMODIFIED means that there might be dirty/stale buffers
@@ -294,7 +304,7 @@ ncl_reclaim(struct vop_reclaim_args *ap)
                nfs_reclaim_p(ap);
 
        NFSLOCKNODE(np);
-       ncl_releasesillyrename(vp, td);
+       ncl_releasesillyrename(vp, true, td);
 
        if (NFS_ISV4(vp) && vp->v_type == VREG) {
                np->n_openstateid = NULL;
@@ -315,7 +325,7 @@ ncl_reclaim(struct vop_reclaim_args *ap)
                MNT_ILOCK(mp);
                if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0) {
                        MNT_IUNLOCK(mp);
-                       nfscl_delegreturnvp(vp, td);
+                       nfscl_delegreturnvp(vp, true, td);
                } else
                        MNT_IUNLOCK(mp);
        } else
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 4ff56f75123c..c07da6f9275f 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -2859,22 +2859,28 @@ nfsmout:
  * Nfs remove rpc
  */
 int
-nfsrpc_remove(vnode_t dvp, char *name, int namelen, vnode_t vp,
-    struct ucred *cred, NFSPROC_T *p, struct nfsvattr *dnap, int *dattrflagp)
+nfsrpc_remove(struct vnode *dvp, char *name, int namelen, struct vnode *vp,
+    struct nfsvattr *nap, int *attrflagp, nfsremove_status *file_status,
+    struct nfsvattr *dnap, int *dattrflagp, struct ucred *cred, NFSPROC_T *p)
 {
-       u_int32_t *tl;
+       uint32_t *tl;
        struct nfsrv_descript nfsd, *nd = &nfsd;
        struct nfsnode *np;
        struct nfsmount *nmp;
        nfsv4stateid_t dstateid;
-       int error, ret = 0, i;
+       nfsattrbit_t attrbits;
+       int error, i, ret;
 
        *dattrflagp = 0;
+       *attrflagp = 0;
+       *file_status = UNKNOWN;
+       ret = 0;
        if (namelen > NFS_MAXNAMLEN)
                return (ENAMETOOLONG);
        nmp = VFSTONFS(dvp->v_mount);
 tryagain:
-       if (NFSHASNFSV4(nmp) && ret == 0) {
+       if (NFSHASNFSV4(nmp) && ((nmp->nm_flag & NFSMNT_NOCTO) == 0 ||
+           !NFSHASNFSV4N(nmp)) && ret == 0) {
                ret = nfscl_removedeleg(vp, p, &dstateid);
                if (ret == 1) {
                        NFSCL_REQSTART(nd, NFSPROC_RETDELEGREMOVE, vp, cred);
@@ -2899,9 +2905,19 @@ tryagain:
        }
        if (ret == 0)
                NFSCL_REQSTART(nd, NFSPROC_REMOVE, dvp, cred);
-       (void) nfsm_strtom(nd, name, namelen);
+       (void)nfsm_strtom(nd, name, namelen);
+       if (ret == 0 && (nd->nd_flag & ND_NFSV4) != 0) {
+               NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+               *tl = txdr_unsigned(NFSV4OP_PUTFH);
+               np = VTONFS(vp);
+               (void)nfsm_fhtom(nmp, nd, np->n_fhp->nfh_fh, 
np->n_fhp->nfh_len, 0);
+               NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+               NFSGETATTR_ATTRBIT(&attrbits);
+               *tl = txdr_unsigned(NFSV4OP_GETATTR);
+               (void)nfsrv_putattrbit(nd, &attrbits);
+       }
        error = nfscl_request(nd, dvp, p, cred);
-       if (error)
+       if (error != 0)
                return (error);
        if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
                /* For NFSv4, parse out any Delereturn replies. */
@@ -2924,7 +2940,41 @@ tryagain:
                }
                error = nfscl_wcc_data(nd, dvp, dnap, dattrflagp, NULL, NULL);
        }
-       if (nd->nd_repstat && !error)
+       if (ret == 0 && (nd->nd_flag & (ND_NFSV4 |
+           ND_NOMOREDATA)) == ND_NFSV4) {
+               /* Parse out the Remove reply for NFSPROC_REMOVE. */
+               NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED + 2 * NFSX_HYPER);
+               /* No use for change info for now. */
+               /* The Remove succeeded. */
+               nd->nd_repstat = 0;
+       }
+       if (ret == 0 && (nd->nd_flag & (ND_NFSV4 |
+           ND_NOMOREDATA)) == ND_NFSV4) {
+               /* Parse out the PutFH, Getattr for NFSPROC_REMOVE. */
+               NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+               if (*(tl + 1) != 0) {
+                       i = fxdr_unsigned(int, *(tl + 1));
+                       if (i == NFSERR_STALE)
+                               *file_status = DELETED;
+               } else {
+                       NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+                       if (*(tl + 1) != 0) {
+                               i = fxdr_unsigned(int, *(tl + 1));
+                               if (i == NFSERR_STALE)
+                                       *file_status = DELETED;
+                       } else {
+                               error = nfsm_loadattr(nd, nap);
+                               if (error == 0) {
+                                       *attrflagp = 1;
+                                       if (nap->na_nlink == 0)
+                                               *file_status = NLINK_ZERO;
+                                       else
+                                               *file_status = VALID;
+                               }
+                       }
+               }
+       }
+       if (nd->nd_repstat != 0 && error == 0)
                error = nd->nd_repstat;
 nfsmout:
        m_freem(nd->nd_mrep);
@@ -2935,12 +2985,14 @@ nfsmout:
  * Do an nfs rename rpc.
  */
 int
-nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char *fnameptr, int fnamelen,
-    vnode_t tdvp, vnode_t tvp, char *tnameptr, int tnamelen, struct ucred 
*cred,
-    NFSPROC_T *p, struct nfsvattr *fnap, struct nfsvattr *tnap,
-    int *fattrflagp, int *tattrflagp)
+nfsrpc_rename(struct vnode *fdvp, struct vnode *fvp, char *fnameptr,
+    int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr,
+    int tnamelen, nfsremove_status *tvp_status, struct nfsvattr *fnap,
+    struct nfsvattr *tnap, int *fattrflagp, int *tattrflagp,
+    struct nfsvattr *tvpnap, int *tvpattrflagp, struct ucred *cred,
+    NFSPROC_T *p)
 {
-       u_int32_t *tl;
+       uint32_t *tl;
        struct nfsrv_descript nfsd, *nd = &nfsd;
        struct nfsmount *nmp;
        struct nfsnode *np;
@@ -2950,11 +3002,14 @@ nfsrpc_rename(vnode_t fdvp, vnode_t fvp, char 
*fnameptr, int fnamelen,
 
        *fattrflagp = 0;
        *tattrflagp = 0;
+       *tvpattrflagp = 0;
+       *tvp_status = UNKNOWN;
        nmp = VFSTONFS(fdvp->v_mount);
        if (fnamelen > NFS_MAXNAMLEN || tnamelen > NFS_MAXNAMLEN)
                return (ENAMETOOLONG);
 tryagain:
-       if (NFSHASNFSV4(nmp) && ret == 0) {
+       if (NFSHASNFSV4(nmp) && ((nmp->nm_flag & NFSMNT_NOCTO) == 0 ||
+           !NFSHASNFSV4N(nmp)) && ret == 0) {
                ret = nfscl_renamedeleg(fvp, &fdstateid, &gotfd, tvp,
                    &tdstateid, &gottd, p);
                if (gotfd && gottd) {
@@ -3007,29 +3062,44 @@ tryagain:
        }
        if (ret == 0)
                NFSCL_REQSTART(nd, NFSPROC_RENAME, fdvp, cred);
-       if (nd->nd_flag & ND_NFSV4) {
+       if ((nd->nd_flag & ND_NFSV4) != 0) {
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(NFSV4OP_GETATTR);
                NFSWCCATTR_ATTRBIT(&attrbits);
-               (void) nfsrv_putattrbit(nd, &attrbits);
+               (void)nfsrv_putattrbit(nd, &attrbits);
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(NFSV4OP_PUTFH);
                (void)nfsm_fhtom(nmp, nd, VTONFS(tdvp)->n_fhp->nfh_fh,
                    VTONFS(tdvp)->n_fhp->nfh_len, 0);
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(NFSV4OP_GETATTR);
-               (void) nfsrv_putattrbit(nd, &attrbits);
+               (void)nfsrv_putattrbit(nd, &attrbits);
                nd->nd_flag |= ND_V4WCCATTR;
                NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
                *tl = txdr_unsigned(NFSV4OP_RENAME);
        }
-       (void) nfsm_strtom(nd, fnameptr, fnamelen);
-       if (!(nd->nd_flag & ND_NFSV4))
+       (void)nfsm_strtom(nd, fnameptr, fnamelen);
+       if ((nd->nd_flag & ND_NFSV4) == 0)
                (void)nfsm_fhtom(nmp, nd, VTONFS(tdvp)->n_fhp->nfh_fh,
                        VTONFS(tdvp)->n_fhp->nfh_len, 0);
-       (void) nfsm_strtom(nd, tnameptr, tnamelen);
+       (void)nfsm_strtom(nd, tnameptr, tnamelen);
+       if (ret == 0 && (nd->nd_flag & ND_NFSV4) != 0) {
+               NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+               /* When tvp == NULL, it doesn't matter which dvp is used. */
+               *tl = txdr_unsigned(NFSV4OP_PUTFH);
+               if (tvp != NULL)
+                       (void)nfsm_fhtom(nmp, nd, VTONFS(tvp)->n_fhp->nfh_fh,
+                           VTONFS(tvp)->n_fhp->nfh_len, 0);
+               else
+                       (void)nfsm_fhtom(nmp, nd, VTONFS(tdvp)->n_fhp->nfh_fh,
+                           VTONFS(tdvp)->n_fhp->nfh_len, 0);
+               NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+               *tl = txdr_unsigned(NFSV4OP_GETATTR);
+               NFSGETATTR_ATTRBIT(&attrbits);
+               (void)nfsrv_putattrbit(nd, &attrbits);
+       }
        error = nfscl_request(nd, fdvp, p, cred);
-       if (error)
+       if (error != 0)
                return (error);
        if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
                /* For NFSv4, parse out any Delereturn replies. */
@@ -3045,7 +3115,7 @@ tryagain:
                for (i = 0; i < (ret * 2); i++) {
                        if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) ==
                            ND_NFSV4) {
-                           NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+                           NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                            if (*(tl + 1)) {
                                if (i == 1 && ret > 1) {
                                    /*
@@ -3065,23 +3135,57 @@ tryagain:
                }
                /* Now, the first wcc attribute reply. */
                if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4) {
-                       NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+                       NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                        if (*(tl + 1))
                                nd->nd_flag |= ND_NOMOREDATA;
                }
                error = nfscl_wcc_data(nd, fdvp, fnap, fattrflagp, NULL, NULL);
                /* and the second wcc attribute reply. */
                if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
-                   !error) {
-                       NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+                   error == 0) {
+                       NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
                        if (*(tl + 1))
                                nd->nd_flag |= ND_NOMOREDATA;
                }
-               if (!error)
+               if (error == 0)
                        error = nfscl_wcc_data(nd, tdvp, tnap, tattrflagp,
                            NULL, NULL);
        }
-       if (nd->nd_repstat && !error)
+       if ((nd->nd_flag & (ND_NFSV4 | ND_NOMOREDATA)) == ND_NFSV4 &&
+           ret == 0 && error == 0) {
+               /* Parse out the rename successful reply. */
+               NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED +
+                   4 * NFSX_HYPER);
+               nd->nd_repstat = 0;     /* Rename succeeded. */
+               /* Parse PutFH reply for tvp. */
+               NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+               if (*(tl + 1) != 0) {
+                       if (tvp != NULL) {
+                               i = fxdr_unsigned(int, *(tl + 1));
+                               if (i == NFSERR_STALE)
+                                       *tvp_status = DELETED;
+                       }
+               } else {
+                       NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+                       if (*(tl + 1) != 0) {
+                               if (tvp != NULL) {
+                                       i = fxdr_unsigned(int, *(tl + 1));
+                                       if (i == NFSERR_STALE)
+                                               *tvp_status = DELETED;
+                               }
+                       } else {
+                               error = nfsm_loadattr(nd, tvpnap);
+                               if (error == 0 && tvp != NULL) {
+                                       *tvpattrflagp = 1;
+                                       if (tvpnap->na_nlink == 0)
+                                               *tvp_status = NLINK_ZERO;
+                                       else
+                                               *tvp_status = VALID;
+                               }
+                       }
+               }
+       }
+       if (nd->nd_repstat != 0 && error == 0)
                error = nd->nd_repstat;
 nfsmout:
        m_freem(nd->nd_mrep);
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index adfb68f5d8f5..fe3682ae437e 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -3508,7 +3508,7 @@ nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T 
*p,
  * Return any delegation for this vp.
  */
 void
-nfscl_delegreturnvp(vnode_t vp, NFSPROC_T *p)
+nfscl_delegreturnvp(struct vnode *vp, bool retdeleg, NFSPROC_T *p)
 {
        struct nfsclclient *clp;
        struct nfscldeleg *dp;
@@ -3531,12 +3531,15 @@ nfscl_delegreturnvp(vnode_t vp, NFSPROC_T *p)
        if (clp != NULL)
                dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
                    np->n_fhp->nfh_len);
-       if (dp != NULL) {
+       if (dp != NULL &&
+           (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0) {
                nfscl_cleandeleg(dp);
                nfscl_freedeleg(&clp->nfsc_deleg, dp, false);
                NFSUNLOCKCLSTATE();
-               newnfs_copycred(&dp->nfsdl_cred, cred);
-               nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
+               if (retdeleg) {
+                       newnfs_copycred(&dp->nfsdl_cred, cred);
+                       nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
+               }
                free(dp, M_NFSCLDELEG);
        } else
                NFSUNLOCKCLSTATE();
@@ -3716,18 +3719,10 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
                                        clp = nfscl_getclnt(cbident);
                                else
                                        clp = nfscl_getclntsess(sessionid);
-                               if (clp != NULL) {
-                                       dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
-                                           nfhp->nfh_len);
-                                       if (dp != NULL && (dp->nfsdl_flags &
-                                           NFSCLDL_DELEGRET) == 0) {
-                                               dp->nfsdl_flags |=
-                                                   NFSCLDL_RECALL;
-                                               wakeup((caddr_t)clp);
-                                       }
-                               } else {
+                               if (clp != NULL)
+                                       nfscl_startdelegrecall(clp, nfhp);
+                               else
                                        error = NFSERR_SERVERFAULT;
-                               }
                                NFSUNLOCKCLSTATE();
                        }
                        if (nfhp != NULL)
@@ -5957,3 +5952,18 @@ tryagain:
        NFSUNLOCKCLSTATE();
        return (0);
 }
+
+/*
+ * Start the recall of a delegation.  Called for CB_RECALL and REMOVE
+ * when nlink == 0 after the REMOVE.
+ */
+void nfscl_startdelegrecall(struct nfsclclient *clp, struct nfsfh *nfhp)
+{
+       struct nfscldeleg *dp;
+
+       dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
+       if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0) {
+               dp->nfsdl_flags |= NFSCLDL_RECALL;
+               wakeup((caddr_t)clp);
+       }
+}
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index c2185992f994..84046cdb503e 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -106,6 +106,7 @@ uint32_t    nfscl_accesscache_load_done_id;
 extern struct nfsstatsv1 nfsstatsv1;
 extern int nfsrv_useacl;
 extern int nfscl_debuglevel;
+NFSCLSTATEMUTEX;
 MALLOC_DECLARE(M_NEWNFSREQ);
 
 static vop_read_t      nfsfifo_read;
@@ -250,10 +251,13 @@ VFS_VOP_VECTOR_REGISTER(newnfs_fifoops);
 static int nfs_mknodrpc(struct vnode *dvp, struct vnode **vpp,
     struct componentname *cnp, struct vattr *vap);
 static int nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name,
-    int namelen, struct ucred *cred, struct thread *td);
+    int namelen, struct ucred *cred, struct thread *td, bool silly);
+static void nfs_removestatus(struct vnode *vp, nfsremove_status file_status,
+    bool silly, struct thread *td);
 static int nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp,
     char *fnameptr, int fnamelen, struct vnode *tdvp, struct vnode *tvp,
-    char *tnameptr, int tnamelen, struct ucred *cred, struct thread *td);
+    char *tnameptr, int tnamelen, bool silly, struct ucred *cred,
+    struct thread *td);
 static int nfs_renameit(struct vnode *sdvp, struct vnode *svp,
     struct componentname *scnp, struct sillyrename *sp);
 
@@ -829,9 +833,11 @@ nfs_close(struct vop_close_args *ap)
        struct ucred *cred;
        int error = 0, ret, localcred = 0;
        int fmode = ap->a_fflag;
+       struct nfsmount *nmp;
 
        if (NFSCL_FORCEDISM(vp->v_mount))
                return (0);
+       nmp = VFSTONFS(vp->v_mount);
        /*
         * During shutdown, a_cred isn't valid, so just use root.
         */
@@ -885,7 +891,9 @@ nfs_close(struct vop_close_args *ap)
                    error = ncl_flush(vp, MNT_WAIT, ap->a_td, cm, 0);
                    /* np->n_flag &= ~NMODIFIED; */
                } else if (NFS_ISV4(vp)) { 
-                       if (nfscl_mustflush(vp) != 0) {
+                       if (!NFSHASNFSV4N(nmp) ||
+                           (nmp->nm_flag & NFSMNT_NOCTO) == 0 ||
+                           nfscl_mustflush(vp) != 0) {
                                int cm = newnfs_commit_on_close ? 1 : 0;
                                if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) {
                                        NFSVOPLOCK(vp, LK_UPGRADE | LK_RETRY);
@@ -927,7 +935,7 @@ nfs_close(struct vop_close_args *ap)
             *     is the cause of some caching/coherency issue that might
             *     crop up.)
             */
-           if (VFSTONFS(vp->v_mount)->nm_negnametimeo == 0) {
+           if (nmp->nm_negnametimeo == 0) {
                    np->n_attrstamp = 0;
                    KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
            }
@@ -944,7 +952,7 @@ nfs_close(struct vop_close_args *ap)
                 */
                if (error == 0 && nfscl_nodeleg(vp, 0) != 0 &&
                    vp->v_type == VREG &&
-                   (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NOCTO) == 0) {
+                   (nmp->nm_flag & NFSMNT_NOCTO) == 0) {
                        ret = nfsrpc_getattr(vp, cred, ap->a_td, &nfsva);
                        if (!ret) {
                                np->n_change = nfsva.na_filerev;
@@ -1025,8 +1033,9 @@ nfs_getattr(struct vop_getattr_args *ap)
                        return (0);
                }
        }
+
        error = nfsrpc_getattr(vp, ap->a_cred, td, &nfsva);
-       if (!error)
+       if (error == 0)
                error = nfscl_loadattrcache(&vp, &nfsva, vap, 0, 0);
        if (!error) {
                /*
@@ -1999,6 +2008,7 @@ nfs_remove(struct vop_remove_args *ap)
        struct nfsnode *np = VTONFS(vp);
        int error = 0;
        struct vattr vattr;
+       struct nfsmount *nmp;
 
        KASSERT(vrefcnt(vp) > 0, ("nfs_remove: bad v_usecount"));
        if (vp->v_type == VDIR)
@@ -2006,6 +2016,7 @@ nfs_remove(struct vop_remove_args *ap)
        else if (vrefcnt(vp) == 1 || (np->n_sillyrename &&
            VOP_GETATTR(vp, &vattr, cnp->cn_cred) == 0 &&
            vattr.va_nlink > 1)) {
+               nmp = VFSTONFS(vp->v_mount);
                /*
                 * Purge the name cache so that the chance of a lookup for
                 * the name succeeding while the remove is in progress is
@@ -2017,12 +2028,19 @@ nfs_remove(struct vop_remove_args *ap)
                /*
                 * throw away biocache buffers, mainly to avoid
                 * unnecessary delayed writes later.
+                * Flushing here would be more correct for the case
+                * where nfs_close() did not do a flush.  However, it
+                * could be a large performance hit for some servers
+                * and only matters when the file name being removed is
+                * one of multiple hard links.
                 */
-               error = ncl_vinvalbuf(vp, 0, curthread, 1);
+               if (!NFSHASNFSV4(nmp) || !NFSHASNFSV4N(nmp) ||
+                   (nmp->nm_flag & NFSMNT_NOCTO) == 0)
+                       error = ncl_vinvalbuf(vp, 0, curthread, 1);
                if (error != EINTR && error != EIO)
                        /* Do the rpc */
                        error = nfs_removerpc(dvp, vp, cnp->cn_nameptr,
-                           cnp->cn_namelen, cnp->cn_cred, curthread);
+                           cnp->cn_namelen, cnp->cn_cred, curthread, false);
                /*
                 * Kludge City: If the first reply to the remove rpc is lost..
                 *   the reply to the retransmitted request will be ENOENT
@@ -2053,7 +2071,32 @@ ncl_removeit(struct sillyrename *sp, struct vnode *vp)
        if (sp->s_dvp->v_type == VBAD)
                return (0);
        return (nfs_removerpc(sp->s_dvp, vp, sp->s_name, sp->s_namlen,
-           sp->s_cred, NULL));
+           sp->s_cred, NULL, true));
+}
+
+/*
+ * Handle the nfsremove_status reply from the RPC function.
+ */
+static void
+nfs_removestatus(struct vnode *vp, nfsremove_status file_status,
+    bool silly, struct thread *td)
+{
+
+       switch (file_status) {
+       case NLINK_ZERO:
+               /* Get rid of any delegation. */
+               nfscl_delegreturnvp(vp, false, td);
+               /* FALLTHROUGH */
+       case DELETED:
+               /* Throw away buffer cache blocks. */
+               (void)ncl_vinvalbuf(vp, 0, td, 1);
+               break;
+       case VALID:
+               /* Nothing to do, delegation is still ok. */
+               break;
+       default:
+               break;
+       }
 }
 
 /*
@@ -2061,17 +2104,20 @@ ncl_removeit(struct sillyrename *sp, struct vnode *vp)
  */
 static int
 nfs_removerpc(struct vnode *dvp, struct vnode *vp, char *name,
-    int namelen, struct ucred *cred, struct thread *td)
+    int namelen, struct ucred *cred, struct thread *td, bool silly)
 {
-       struct nfsvattr dnfsva;
+       struct nfsvattr dnfsva, nfsva;
        struct nfsnode *dnp = VTONFS(dvp);
-       int error = 0, dattrflag;
+       struct nfsmount *nmp;
+       int attrflag, error = 0, dattrflag;
+       nfsremove_status file_status;
 
+       nmp = VFSTONFS(dvp->v_mount);
        NFSLOCKNODE(dnp);
        dnp->n_flag |= NREMOVEINPROG;
        NFSUNLOCKNODE(dnp);
-       error = nfsrpc_remove(dvp, name, namelen, vp, cred, td, &dnfsva,
-           &dattrflag);
+       error = nfsrpc_remove(dvp, name, namelen, vp, &nfsva, &attrflag,
+           &file_status, &dnfsva, &dattrflag, cred, td);
        NFSLOCKNODE(dnp);
        if ((dnp->n_flag & NREMOVEWANT)) {
                dnp->n_flag &= ~(NREMOVEWANT | NREMOVEINPROG);
@@ -2081,11 +2127,19 @@ nfs_removerpc(struct vnode *dvp, struct vnode *vp, char 
*name,
                dnp->n_flag &= ~NREMOVEINPROG;
                NFSUNLOCKNODE(dnp);
        }
-       if (dattrflag)
+
+       if (NFSHASNFSV4(nmp) && NFSHASNFSV4N(nmp)) {
+               if (file_status != DELETED && attrflag != 0)
+                       (void)nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
+               if ((nmp->nm_flag & NFSMNT_NOCTO) != 0)
+                       nfs_removestatus(vp, file_status, silly, td);
+       }
+
+       if (dattrflag != 0)
                (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, 0, 1);
        NFSLOCKNODE(dnp);
        dnp->n_flag |= NMODIFIED;
-       if (!dattrflag) {
+       if (dattrflag == 0) {
                dnp->n_attrstamp = 0;
                KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp);
        }
@@ -2110,6 +2164,7 @@ nfs_rename(struct vop_rename_args *ap)
        struct nfsnode *fnp = VTONFS(ap->a_fvp);
        struct nfsnode *tdnp = VTONFS(ap->a_tdvp);
        struct nfsv4node *newv4 = NULL;
+       struct nfsmount *nmp;
        int error;
 
        /* Check for cross-device rename */
@@ -2118,6 +2173,7 @@ nfs_rename(struct vop_rename_args *ap)
                error = EXDEV;
                goto out;
        }
+       nmp = VFSTONFS(fvp->v_mount);
 
        if (fvp == tvp) {
                printf("nfs_rename: fvp == tvp (can't happen)\n");
@@ -2140,11 +2196,15 @@ nfs_rename(struct vop_rename_args *ap)
         * that was written back to our cache earlier. Not checking for
         * this condition can result in potential (silent) data loss.
         */
-       error = VOP_FSYNC(fvp, MNT_WAIT, curthread);
+       if ((nmp->nm_flag & NFSMNT_NOCTO) == 0 || !NFSHASNFSV4(nmp) ||
+           !NFSHASNFSV4N(nmp) || nfscl_mustflush(fvp) != 0)
+               error = VOP_FSYNC(fvp, MNT_WAIT, curthread);
        NFSVOPUNLOCK(fvp);
-       if (!error && tvp)
+       if (error == 0 && tvp != NULL && ((nmp->nm_flag & NFSMNT_NOCTO) == 0 ||
+           !NFSHASNFSV4(nmp) || !NFSHASNFSV4N(nmp) ||
+           nfscl_mustflush(tvp) != 0))
                error = VOP_FSYNC(tvp, MNT_WAIT, curthread);
-       if (error)
+       if (error != 0)
                goto out;
 
        /*
@@ -2159,7 +2219,7 @@ nfs_rename(struct vop_rename_args *ap)
        }
 
        error = nfs_renamerpc(fdvp, fvp, fcnp->cn_nameptr, fcnp->cn_namelen,
-           tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred,
+           tdvp, tvp, tcnp->cn_nameptr, tcnp->cn_namelen, false, tcnp->cn_cred,
            curthread);
 
        if (error == 0 && NFS_ISV4(tdvp)) {
@@ -2228,7 +2288,7 @@ nfs_renameit(struct vnode *sdvp, struct vnode *svp, 
struct componentname *scnp,
 {
 
        return (nfs_renamerpc(sdvp, svp, scnp->cn_nameptr, scnp->cn_namelen,
-           sdvp, NULL, sp->s_name, sp->s_namlen, scnp->cn_cred,
+           sdvp, NULL, sp->s_name, sp->s_namlen, true, scnp->cn_cred,
            curthread));
 }
 
@@ -2238,16 +2298,19 @@ nfs_renameit(struct vnode *sdvp, struct vnode *svp, 
struct componentname *scnp,
 static int
 nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, char *fnameptr,
     int fnamelen, struct vnode *tdvp, struct vnode *tvp, char *tnameptr,
-    int tnamelen, struct ucred *cred, struct thread *td)
+    int tnamelen, bool silly, struct ucred *cred, struct thread *td)
 {
-       struct nfsvattr fnfsva, tnfsva;
+       struct nfsvattr fnfsva, tnfsva, tvpnfsva;
        struct nfsnode *fdnp = VTONFS(fdvp);
        struct nfsnode *tdnp = VTONFS(tdvp);
-       int error = 0, fattrflag, tattrflag;
+       struct nfsmount *nmp;
+       int error = 0, fattrflag, tattrflag, tvpattrflag;
+       nfsremove_status tvp_status;
 
+       nmp = VFSTONFS(fdvp->v_mount);
        error = nfsrpc_rename(fdvp, fvp, fnameptr, fnamelen, tdvp, tvp,
-           tnameptr, tnamelen, cred, td, &fnfsva, &tnfsva, &fattrflag,
-           &tattrflag);
+           tnameptr, tnamelen, &tvp_status, &fnfsva, &tnfsva, &fattrflag,
+           &tattrflag, &tvpnfsva, &tvpattrflag, cred, td);
        NFSLOCKNODE(fdnp);
        fdnp->n_flag |= NMODIFIED;
        if (fattrflag != 0) {
@@ -2268,6 +2331,15 @@ nfs_renamerpc(struct vnode *fdvp, struct vnode *fvp, 
char *fnameptr,
                NFSUNLOCKNODE(tdnp);
                KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(tdvp);
        }
+
+       if (tvp != NULL) {
+               if (NFSHASNFSV4(nmp) && NFSHASNFSV4N(nmp) &&
+                   (nmp->nm_flag & NFSMNT_NOCTO) != 0)
+                       nfs_removestatus(tvp, tvp_status, silly, td);
+               if (!silly && tvpattrflag != 0)
+                       (void)nfscl_loadattrcache(&tvp, &tvpnfsva, NULL, 0, 1);
+       }
+
        if (error && NFS_ISV4(fdvp))
                error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0);
        return (error);
@@ -2291,7 +2363,9 @@ nfs_link(struct vop_link_args *ap)
         * doesn't get "out of sync" with the server.
         * XXX There should be a better way!
         */
+#ifdef notnow
        VOP_FSYNC(vp, MNT_WAIT, curthread);
+#endif
 
        error = nfsrpc_link(tdvp, vp, cnp->cn_nameptr, cnp->cn_namelen,
            cnp->cn_cred, curthread, &dnfsva, &nfsva, &attrflag, &dattrflag);

Reply via email to