From eef4527a8a335a0a92f5e3c74c25d0d42bc2af45 Mon Sep 17 00:00:00 2001 From: rmacklem Date: Sun, 27 May 2012 14:20:46 +0000 Subject: [PATCH] MFC: r235332 PR# 165923 reported intermittent write failures for dirty memory mapped pages being written back on an NFS mount. Since any thread can call VOP_PUTPAGES() to write back a dirty page, the credentials of that thread may not have write access to the file on an NFS server. (Often the uid is 0, which may be mapped to "nobody" in the NFS server.) Although there is no completely correct fix for this (NFS servers check access on every write RPC instead of at open/mmap time), this patch avoids the common cases by holding onto a credential that recently opened the file for writing and uses that credential for the write RPCs being done by VOP_PUTPAGES() for both NFS clients. git-svn-id: svn://svn.freebsd.org/base/stable/8@236150 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sys/fs/nfsclient/nfs_clbio.c | 7 ++++++- sys/fs/nfsclient/nfs_clnode.c | 2 ++ sys/fs/nfsclient/nfs_clvnops.c | 16 ++++++++++++++++ sys/fs/nfsclient/nfsnode.h | 1 + sys/nfsclient/nfs_bio.c | 7 ++++++- sys/nfsclient/nfs_node.c | 2 ++ sys/nfsclient/nfs_vnops.c | 16 ++++++++++++++++ sys/nfsclient/nfsnode.h | 1 + 8 files changed, 50 insertions(+), 2 deletions(-) diff --git a/sys/fs/nfsclient/nfs_clbio.c b/sys/fs/nfsclient/nfs_clbio.c index 2a8579193..5d5d13705 100644 --- a/sys/fs/nfsclient/nfs_clbio.c +++ b/sys/fs/nfsclient/nfs_clbio.c @@ -271,7 +271,11 @@ ncl_putpages(struct vop_putpages_args *ap) vp = ap->a_vp; np = VTONFS(vp); td = curthread; /* XXX */ - cred = curthread->td_ucred; /* XXX */ + /* Set the cred to n_writecred for the write rpcs. */ + if (np->n_writecred != NULL) + cred = crhold(np->n_writecred); + else + cred = crhold(curthread->td_ucred); /* XXX */ nmp = VFSTONFS(vp->v_mount); pages = ap->a_m; count = ap->a_count; @@ -335,6 +339,7 @@ ncl_putpages(struct vop_putpages_args *ap) iomode = NFSWRITE_FILESYNC; error = ncl_writerpc(vp, &uio, cred, &iomode, &must_commit, 0); + crfree(cred); pmap_qremove(kva, npages); relpbuf(bp, &ncl_pbuf_freecnt); diff --git a/sys/fs/nfsclient/nfs_clnode.c b/sys/fs/nfsclient/nfs_clnode.c index 34bc739fb..a5515f615 100644 --- a/sys/fs/nfsclient/nfs_clnode.c +++ b/sys/fs/nfsclient/nfs_clnode.c @@ -297,6 +297,8 @@ ncl_reclaim(struct vop_reclaim_args *ap) FREE((caddr_t)dp2, M_NFSDIROFF); } } + if (np->n_writecred != NULL) + crfree(np->n_writecred); FREE((caddr_t)np->n_fhp, M_NFSFH); if (np->n_v4 != NULL) FREE((caddr_t)np->n_v4, M_NFSV4NODE); diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index 872b45e83..fc99a7072 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -480,6 +480,7 @@ nfs_open(struct vop_open_args *ap) struct vattr vattr; int error; int fmode = ap->a_mode; + struct ucred *cred; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) return (EOPNOTSUPP); @@ -570,7 +571,22 @@ nfs_open(struct vop_open_args *ap) } np->n_directio_opens++; } + + /* + * If this is an open for writing, capture a reference to the + * credentials, so they can be used by ncl_putpages(). Using + * these write credentials is preferable to the credentials of + * whatever thread happens to be doing the VOP_PUTPAGES() since + * the write RPCs are less likely to fail with EACCES. + */ + if ((fmode & FWRITE) != 0) { + cred = np->n_writecred; + np->n_writecred = crhold(ap->a_cred); + } else + cred = NULL; mtx_unlock(&np->n_mtx); + if (cred != NULL) + crfree(cred); vnode_create_vobject(vp, vattr.va_size, ap->a_td); return (0); } diff --git a/sys/fs/nfsclient/nfsnode.h b/sys/fs/nfsclient/nfsnode.h index 137457976..edc67e244 100644 --- a/sys/fs/nfsclient/nfsnode.h +++ b/sys/fs/nfsclient/nfsnode.h @@ -136,6 +136,7 @@ struct nfsnode { struct nfsv4node *n_v4; /* extra V4 stuff */ struct timespec n_unused4; struct timespec n_unused5; + struct ucred *n_writecred; /* Cred. for putpages */ }; #define n_atim n_un1.nf_atim diff --git a/sys/nfsclient/nfs_bio.c b/sys/nfsclient/nfs_bio.c index 72a988c1f..b4c686017 100644 --- a/sys/nfsclient/nfs_bio.c +++ b/sys/nfsclient/nfs_bio.c @@ -268,7 +268,11 @@ nfs_putpages(struct vop_putpages_args *ap) vp = ap->a_vp; np = VTONFS(vp); td = curthread; /* XXX */ - cred = curthread->td_ucred; /* XXX */ + /* Set the cred to n_writecred for the write rpcs. */ + if (np->n_writecred != NULL) + cred = crhold(np->n_writecred); + else + cred = crhold(curthread->td_ucred); /* XXX */ nmp = VFSTONFS(vp->v_mount); pages = ap->a_m; count = ap->a_count; @@ -332,6 +336,7 @@ nfs_putpages(struct vop_putpages_args *ap) iomode = NFSV3WRITE_FILESYNC; error = (nmp->nm_rpcops->nr_writerpc)(vp, &uio, cred, &iomode, &must_commit); + crfree(cred); pmap_qremove(kva, npages); relpbuf(bp, &nfs_pbuf_freecnt); diff --git a/sys/nfsclient/nfs_node.c b/sys/nfsclient/nfs_node.c index 77c1fb134..db50950e4 100644 --- a/sys/nfsclient/nfs_node.c +++ b/sys/nfsclient/nfs_node.c @@ -270,6 +270,8 @@ nfs_reclaim(struct vop_reclaim_args *ap) free((caddr_t)dp2, M_NFSDIROFF); } } + if (np->n_writecred != NULL) + crfree(np->n_writecred); if (np->n_fhsize > NFS_SMALLFH) { free((caddr_t)np->n_fhp, M_NFSBIGFH); } diff --git a/sys/nfsclient/nfs_vnops.c b/sys/nfsclient/nfs_vnops.c index 97fd91c8f..5a78c1538 100644 --- a/sys/nfsclient/nfs_vnops.c +++ b/sys/nfsclient/nfs_vnops.c @@ -510,6 +510,7 @@ nfs_open(struct vop_open_args *ap) struct vattr vattr; int error; int fmode = ap->a_mode; + struct ucred *cred; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) return (EOPNOTSUPP); @@ -566,7 +567,22 @@ nfs_open(struct vop_open_args *ap) } np->n_directio_opens++; } + + /* + * If this is an open for writing, capture a reference to the + * credentials, so they can be used by nfs_putpages(). Using + * these write credentials is preferable to the credentials of + * whatever thread happens to be doing the VOP_PUTPAGES() since + * the write RPCs are less likely to fail with EACCES. + */ + if ((fmode & FWRITE) != 0) { + cred = np->n_writecred; + np->n_writecred = crhold(ap->a_cred); + } else + cred = NULL; mtx_unlock(&np->n_mtx); + if (cred != NULL) + crfree(cred); vnode_create_vobject(vp, vattr.va_size, ap->a_td); return (0); } diff --git a/sys/nfsclient/nfsnode.h b/sys/nfsclient/nfsnode.h index 169613410..461439d82 100644 --- a/sys/nfsclient/nfsnode.h +++ b/sys/nfsclient/nfsnode.h @@ -141,6 +141,7 @@ struct nfsnode { struct nfs_attrcache_timestamp n_unused; struct timespec n_unused4; struct timespec n_unused5; + struct ucred *n_writecred; /* Cred. for putpages */ }; #define n_atim n_un1.nf_atim -- 2.45.0