Handle possible vnode reclamation after ncl_vinvalbuf() call.

ncl_vinvalbuf() might need to upgrade vnode lock, allowing the vnode
to be reclaimed by other thread.  Handle the situation, indicated by
the returned error zero and VI_DOOMED iflag set, converting it into
EBADF.  Handle all calls, even where the vnode is exclusively locked
right now.

Reviewed by:	markj, rmacklem
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	2 weeks
X-Differential revision:	https://reviews.freebsd.org/D10241
This commit is contained in:
Konstantin Belousov 2017-04-05 17:11:39 +00:00
parent 0226f65940
commit 48fe926362
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=316529
2 changed files with 53 additions and 22 deletions

View File

@ -394,8 +394,8 @@ nfs_bioread_check_cons(struct vnode *vp, struct thread *td, struct ucred *cred)
*/
old_lock = ncl_upgrade_vnlock(vp);
if (vp->v_iflag & VI_DOOMED) {
ncl_downgrade_vnlock(vp, old_lock);
return (EBADF);
error = EBADF;
goto out;
}
mtx_lock(&np->n_mtx);
@ -406,7 +406,9 @@ nfs_bioread_check_cons(struct vnode *vp, struct thread *td, struct ucred *cred)
panic("nfs: bioread, not dir");
ncl_invaldir(vp);
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error)
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
error = EBADF;
if (error != 0)
goto out;
}
np->n_attrstamp = 0;
@ -429,7 +431,9 @@ nfs_bioread_check_cons(struct vnode *vp, struct thread *td, struct ucred *cred)
if (vp->v_type == VDIR)
ncl_invaldir(vp);
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error)
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
error = EBADF;
if (error != 0)
goto out;
mtx_lock(&np->n_mtx);
np->n_mtime = vattr.va_mtime;
@ -439,7 +443,7 @@ nfs_bioread_check_cons(struct vnode *vp, struct thread *td, struct ucred *cred)
}
out:
ncl_downgrade_vnlock(vp, old_lock);
return error;
return (error);
}
/*
@ -623,6 +627,9 @@ ncl_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
while (error == NFSERR_BAD_COOKIE) {
ncl_invaldir(vp);
error = ncl_vinvalbuf(vp, 0, td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
return (EBADF);
/*
* Yuck! The directory has been modified on the
* server. The only way to get the block is by
@ -943,8 +950,11 @@ ncl_write(struct vop_write_args *ap)
#endif
np->n_attrstamp = 0;
KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error)
error = ncl_vinvalbuf(vp, V_SAVE | ((ioflag &
IO_VMIO) != 0 ? V_VMIO : 0), td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
error = EBADF;
if (error != 0)
return (error);
} else
mtx_unlock(&np->n_mtx);
@ -1023,8 +1033,12 @@ ncl_write(struct vop_write_args *ap)
if (wouldcommit > nmp->nm_wcommitsize) {
np->n_attrstamp = 0;
KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error)
error = ncl_vinvalbuf(vp, V_SAVE | ((ioflag &
IO_VMIO) != 0 ? V_VMIO : 0), td, 1);
if (error == 0 &&
(vp->v_iflag & VI_DOOMED) != 0)
error = EBADF;
if (error != 0)
return (error);
wouldcommit = biosize;
}

View File

@ -520,6 +520,8 @@ nfs_open(struct vop_open_args *ap)
if (np->n_flag & NMODIFIED) {
mtx_unlock(&np->n_mtx);
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
return (EBADF);
if (error == EINTR || error == EIO) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
@ -556,6 +558,8 @@ nfs_open(struct vop_open_args *ap)
np->n_direofoffset = 0;
mtx_unlock(&np->n_mtx);
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
return (EBADF);
if (error == EINTR || error == EIO) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
@ -576,6 +580,8 @@ nfs_open(struct vop_open_args *ap)
if (np->n_directio_opens == 0) {
mtx_unlock(&np->n_mtx);
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
return (EBADF);
if (error) {
if (NFS_ISV4(vp))
(void) nfsrpc_close(vp, 0, ap->a_td);
@ -714,8 +720,11 @@ nfs_close(struct vop_close_args *ap)
* np->n_flag &= ~NMODIFIED;
*/
}
} else
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
} else {
error = ncl_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
return (EBADF);
}
mtx_lock(&np->n_mtx);
}
/*
@ -931,13 +940,13 @@ nfs_setattr(struct vop_setattr_args *ap)
if (np->n_flag & NMODIFIED) {
tsize = np->n_size;
mtx_unlock(&np->n_mtx);
if (vap->va_size == 0)
error = ncl_vinvalbuf(vp, 0, td, 1);
else
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error) {
vnode_pager_setsize(vp, tsize);
return (error);
error = ncl_vinvalbuf(vp, vap->va_size == 0 ?
0 : V_SAVE, td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
error = EBADF;
if (error != 0) {
vnode_pager_setsize(vp, tsize);
return (error);
}
/*
* Call nfscl_delegmodtime() to set the modify time
@ -961,8 +970,10 @@ nfs_setattr(struct vop_setattr_args *ap)
if ((vap->va_mtime.tv_sec != VNOVAL || vap->va_atime.tv_sec != VNOVAL) &&
(np->n_flag & NMODIFIED) && vp->v_type == VREG) {
mtx_unlock(&np->n_mtx);
if ((error = ncl_vinvalbuf(vp, V_SAVE, td, 1)) != 0 &&
(error == EINTR || error == EIO))
error = ncl_vinvalbuf(vp, V_SAVE, td, 1);
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
return (EBADF);
if (error == EINTR || error == EIO)
return (error);
} else
mtx_unlock(&np->n_mtx);
@ -1665,8 +1676,10 @@ nfs_remove(struct vop_remove_args *ap)
* unnecessary delayed writes later.
*/
error = ncl_vinvalbuf(vp, 0, cnp->cn_thread, 1);
/* Do the rpc */
if (error != EINTR && error != EIO)
if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
error = EBADF;
else if (error != EINTR && error != EIO)
/* Do the rpc */
error = nfs_removerpc(dvp, vp, cnp->cn_nameptr,
cnp->cn_namelen, cnp->cn_cred, cnp->cn_thread);
/*
@ -3055,6 +3068,10 @@ nfs_advlock(struct vop_advlock_args *ap)
if ((np->n_flag & NMODIFIED) || ret ||
np->n_change != va.va_filerev) {
(void) ncl_vinvalbuf(vp, V_SAVE, td, 1);
if ((vp->v_iflag & VI_DOOMED) != 0) {
NFSVOPUNLOCK(vp, 0);
return (EBADF);
}
np->n_attrstamp = 0;
KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
ret = VOP_GETATTR(vp, &va, cred);