aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2020-02-22 20:50:30 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2020-02-22 20:50:30 +0000
commit0ff51c98d19739ee1c1e0046ffaade5051d6b210 (patch)
tree9ade0faeb4aee4a4c3118471e4af6dc46c9f418f
parent04869b812b8e19fbfbfd75299f3b00c186edb0d3 (diff)
downloadsrc-0ff51c98d19739ee1c1e0046ffaade5051d6b210.tar.gz
src-0ff51c98d19739ee1c1e0046ffaade5051d6b210.zip
Fix NFS client deadlock when read reports truncated node.
If node attribute returned in the reply for read rpc indicate truncation, and it happens that the vnode is exclusively locked, update of the node attributes would try to shrink vnode size. Since during the read some vnode pages were busied by the reading thread, vnode_pager_setsize() deadlocks waiting for the busy state owned by the caller. Use a thread-local flag to indicate that NFS read owns some (s)busy pages states and postpone the call to vnode_pager_setsize() until the thread relinguishes the ownership. Diagnosed by: rlibby Tested by: pho, rlibby Sponsored by: The FreeBSD Foundation MFC after: 1 week
Notes
Notes: svn path=/head/; revision=358252
-rw-r--r--sys/fs/nfsclient/nfs_clbio.c51
-rw-r--r--sys/fs/nfsclient/nfs_clport.c3
-rw-r--r--sys/sys/proc.h2
3 files changed, 39 insertions, 17 deletions
diff --git a/sys/fs/nfsclient/nfs_clbio.c b/sys/fs/nfsclient/nfs_clbio.c
index d8d4600c3ff9..a2cfcada2218 100644
--- a/sys/fs/nfsclient/nfs_clbio.c
+++ b/sys/fs/nfsclient/nfs_clbio.c
@@ -425,14 +425,11 @@ int
ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
{
struct nfsnode *np = VTONFS(vp);
- int biosize, i;
struct buf *bp, *rabp;
struct thread *td;
struct nfsmount *nmp = VFSTONFS(vp->v_mount);
daddr_t lbn, rabn;
- int bcount;
- int seqcount;
- int nra, error = 0, n = 0, on = 0;
+ int biosize, bcount, error, i, n, nra, on, save2, seqcount;
off_t tmp_off;
KASSERT(uio->uio_rw == UIO_READ, ("ncl_read mode"));
@@ -464,6 +461,8 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
/* No caching/ no readaheads. Just read data into the user buffer */
return ncl_readrpc(vp, uio, cred);
+ n = 0;
+ on = 0;
biosize = vp->v_bufobj.bo_bsize;
seqcount = (int)((off_t)(ioflag >> IO_SEQSHIFT) * biosize / BKVASIZE);
@@ -471,6 +470,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
if (error)
return error;
+ save2 = curthread_pflags2_set(TDP2_SBPAGES);
do {
u_quad_t nsize;
@@ -495,7 +495,9 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
rabp = nfs_getcacheblk(vp, rabn, biosize, td);
if (!rabp) {
error = newnfs_sigintr(nmp, td);
- return (error ? error : EINTR);
+ if (error == 0)
+ error = EINTR;
+ goto out;
}
if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) {
rabp->b_flags |= B_ASYNC;
@@ -526,7 +528,9 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
if (!bp) {
error = newnfs_sigintr(nmp, td);
- return (error ? error : EINTR);
+ if (error == 0)
+ error = EINTR;
+ goto out;
}
/*
@@ -540,7 +544,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
error = ncl_doio(vp, bp, cred, td, 0);
if (error) {
brelse(bp);
- return (error);
+ goto out;
}
}
@@ -561,7 +565,9 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
bp = nfs_getcacheblk(vp, (daddr_t)0, NFS_MAXPATHLEN, td);
if (!bp) {
error = newnfs_sigintr(nmp, td);
- return (error ? error : EINTR);
+ if (error == 0)
+ error = EINTR;
+ goto out;
}
if ((bp->b_flags & B_CACHE) == 0) {
bp->b_iocmd = BIO_READ;
@@ -570,7 +576,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
if (error) {
bp->b_ioflags |= BIO_ERROR;
brelse(bp);
- return (error);
+ goto out;
}
}
n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
@@ -580,14 +586,17 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
NFSINCRGLOBAL(nfsstatsv1.biocache_readdirs);
if (np->n_direofoffset
&& uio->uio_offset >= np->n_direofoffset) {
- return (0);
+ error = 0;
+ goto out;
}
lbn = (uoff_t)uio->uio_offset / NFS_DIRBLKSIZ;
on = uio->uio_offset & (NFS_DIRBLKSIZ - 1);
bp = nfs_getcacheblk(vp, lbn, NFS_DIRBLKSIZ, td);
if (!bp) {
- error = newnfs_sigintr(nmp, td);
- return (error ? error : EINTR);
+ error = newnfs_sigintr(nmp, td);
+ if (error == 0)
+ error = EINTR;
+ goto out;
}
if ((bp->b_flags & B_CACHE) == 0) {
bp->b_iocmd = BIO_READ;
@@ -612,12 +621,16 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
*/
for (i = 0; i <= lbn && !error; i++) {
if (np->n_direofoffset
- && (i * NFS_DIRBLKSIZ) >= np->n_direofoffset)
- return (0);
+ && (i * NFS_DIRBLKSIZ) >= np->n_direofoffset) {
+ error = 0;
+ goto out;
+ }
bp = nfs_getcacheblk(vp, i, NFS_DIRBLKSIZ, td);
if (!bp) {
error = newnfs_sigintr(nmp, td);
- return (error ? error : EINTR);
+ if (error == 0)
+ error = EINTR;
+ goto out;
}
if ((bp->b_flags & B_CACHE) == 0) {
bp->b_iocmd = BIO_READ;
@@ -646,7 +659,7 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
* we give up.
*/
if (error)
- return (error);
+ goto out;
}
/*
@@ -706,6 +719,12 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
if (bp != NULL)
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n > 0);
+out:
+ curthread_pflags2_restore(save2);
+ if ((curthread->td_pflags2 & TDP2_SBPAGES) == 0) {
+ NFSLOCKNODE(np);
+ ncl_pager_setsize(vp, NULL);
+ }
return (error);
}
diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c
index 77ecdd255ea2..017b81776579 100644
--- a/sys/fs/nfsclient/nfs_clport.c
+++ b/sys/fs/nfsclient/nfs_clport.c
@@ -597,7 +597,8 @@ ncl_pager_setsize(struct vnode *vp, u_quad_t *nsizep)
setnsize = false;
if (object != NULL && nsize != object->un_pager.vnp.vnp_size) {
- if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE)
+ if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE &&
+ (curthread->td_pflags2 & TDP2_SBPAGES) == 0)
setnsize = true;
else
np->n_flag |= NVNSETSZSKIP;
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 2b574cb1c7b2..bbae5cf1caae 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -515,6 +515,8 @@ do { \
#define TDP_EXECVMSPC 0x40000000 /* Execve destroyed old vmspace */
#define TDP_SIGFASTPENDING 0x80000000 /* Pending signal due to sigfastblock */
+#define TDP2_SBPAGES 0x00000001 /* Owns sbusy on some pages */
+
/*
* Reasons that the current thread can not be run yet.
* More than one may apply.