svn commit: r358252 - in head/sys: fs/nfsclient sys

Konstantin Belousov kib at FreeBSD.org
Sat Feb 22 20:50:33 UTC 2020


Author: kib
Date: Sat Feb 22 20:50:30 2020
New Revision: 358252
URL: https://svnweb.freebsd.org/changeset/base/358252

Log:
  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

Modified:
  head/sys/fs/nfsclient/nfs_clbio.c
  head/sys/fs/nfsclient/nfs_clport.c
  head/sys/sys/proc.h

Modified: head/sys/fs/nfsclient/nfs_clbio.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clbio.c	Sat Feb 22 20:43:04 2020	(r358251)
+++ head/sys/fs/nfsclient/nfs_clbio.c	Sat Feb 22 20:50:30 2020	(r358252)
@@ -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 iof
 		/* 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 iof
 	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 iof
 			    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 iof
 
 		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 iof
 		    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 iof
 		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 iof
 		    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 iof
 		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 iof
 			 */
 			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 iof
 		     * we give up.
 		     */
 		    if (error)
-			    return (error);
+			    goto out;
 		}
 
 		/*
@@ -706,6 +719,12 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int iof
 	    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);
 }
 

Modified: head/sys/fs/nfsclient/nfs_clport.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clport.c	Sat Feb 22 20:43:04 2020	(r358251)
+++ head/sys/fs/nfsclient/nfs_clport.c	Sat Feb 22 20:50:30 2020	(r358252)
@@ -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;

Modified: head/sys/sys/proc.h
==============================================================================
--- head/sys/sys/proc.h	Sat Feb 22 20:43:04 2020	(r358251)
+++ head/sys/sys/proc.h	Sat Feb 22 20:50:30 2020	(r358252)
@@ -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.


More information about the svn-src-head mailing list