From nobody Sun Jun 29 21:18:14 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4bVhwR2KSXz60hKw; Sun, 29 Jun 2025 21:18:15 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4bVhwR1mTgz3kH7; Sun, 29 Jun 2025 21:18:15 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1751231895; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=tyDdPufmS1hMHZHUtmCDbosqAWQo1WX2LHI0/LkpjaM=; b=qHoWa2Npg+k1G69qh/hiLtCoPi91nZVbGmxFnF89cdyOmshwkQV5nSiswcdO1QIyqqt2yM ryDdq56wDx6t4cSRLOLrziPYfVnHuGQhFNgzLuOtI0muOcm/5AE11RdWhXWsySU7j9Hl3B vsEooqAhzpWzK4SPtOYfUl+DPIslP8vVejDasJho5TCe7C3U35/Dc/PqjpBtiX8FzeYze6 AvVbOjYNT1hhHovLfTwoUSkCqb9l5p8wqCxuWSwNRBKi2Io3fovp1aPECDZHFVIQjdp+SM s4GrP3Cp5I5QTPX8xJHfYg8oB16pfRhcLp9KLzW1lwHn/tsb712YL7ouUkMEog== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1751231895; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=tyDdPufmS1hMHZHUtmCDbosqAWQo1WX2LHI0/LkpjaM=; b=LfEYR1z/uoGthJfRKKSy8ROBt65I2+CNPkL9kao7cUcVi3J37+R7Dhtlozb5x+7P0X2l6U N46wjQTmiZ55jmijEH9wi+WePKxpdc0aywEasTmUrvpHoclq+Xw12I5lCNckq7mXjrn3Xk tMx0z9IuYg7eRHBbeNZaFAYy0AR00rD6LNcvq+3M3rux72sWeQB6hbtGwB33QkEKpW2wqY 4jWwxmj364BljmemkG80h57pQhHlASFK7ie4IeKvDGYlqRHU+bnYHs0Lb1nfW4+vrxV0Z5 ItcteOja4v+TQCMcF9a8Shf3TAY9Q2g1qIUs3nSEffwyfxocuR74ehzEWlNlLg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1751231895; a=rsa-sha256; cv=none; b=WWBdsYT/MhLNITJqYejHI4E/c58HW7J/01MiEngsHu9ukbWub8KhvnKq/J+7yufE+H4ixq eFdXlbnzK237X3wJJ6Y+qVYAogg3SSovodDU6lGY2f2RBeXWAW53nM8K5PZgVpTmC/cRxN 0cFPVPbL/yqoZ1tjcc4Td3EwChen/hzFJgyAe5oNbRMm1pzksiZfxmXaV4OImn3F1Pd7gf zl/vxuQd4VRd5wexDzHNX5hdgLaP9WcFvExGccdvPtxoIsIC7wwLALrhoVlIcqghvAwl+/ ViBzA7dDhAPMV2fRC4Z2IZzBVXPTFDg0eXYE5ryb+20tckAxfdfkKJi4RtiaHA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4bVhwR0ywdzcc0; Sun, 29 Jun 2025 21:18:15 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 55TLIE8s038971; Sun, 29 Jun 2025 21:18:14 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 55TLIE1r038968; Sun, 29 Jun 2025 21:18:14 GMT (envelope-from git) Date: Sun, 29 Jun 2025 21:18:14 GMT Message-Id: <202506292118.55TLIE1r038968@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Rick Macklem Subject: git: 171f66b0c2ca - main - nfscl: Optimize NFSv4 remove and rename for nocto option List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: rmacklem X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 171f66b0c2cab0b75c37eb1c815273da5282ff7e Auto-Submitted: auto-generated The branch main has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=171f66b0c2cab0b75c37eb1c815273da5282ff7e commit 171f66b0c2cab0b75c37eb1c815273da5282ff7e Author: Rick Macklem AuthorDate: 2025-06-29 21:15:02 +0000 Commit: Rick Macklem 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);