null_vput_pair(): release use reference on dvp earlier
We might own the last use reference, and then vrele() at the end would need to take the dvp vnode lock to inactivate, which causes deadlock with vp. We cannot vrele() dvp from start since this might unlock ldvp. Handle it by holding the vnode and dropping use ref after lowerfs VOP_VPUT_PAIR() ended. This effectivaly requires unlock of the vp vnode after VOP_VPUT_PAIR(), so the call is changed to set unlock_vp to true unconditionally. This opens more opportunities for vp to be reclaimed, if lvp is still alive we reinstantiate vp with null_nodeget(). Reported and tested by: pho Reviewed by: mckusick Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D29178
This commit is contained in:
parent
44691b33cc
commit
16dea83410
@ -985,33 +985,50 @@ null_vput_pair(struct vop_vput_pair_args *ap)
|
|||||||
vpp = ap->a_vpp;
|
vpp = ap->a_vpp;
|
||||||
vp = NULL;
|
vp = NULL;
|
||||||
lvp = NULL;
|
lvp = NULL;
|
||||||
if (vpp != NULL) {
|
mp = NULL;
|
||||||
|
if (vpp != NULL)
|
||||||
vp = *vpp;
|
vp = *vpp;
|
||||||
if (vp != NULL) {
|
if (vp != NULL) {
|
||||||
|
lvp = NULLVPTOLOWERVP(vp);
|
||||||
|
vref(lvp);
|
||||||
|
if (!ap->a_unlock_vp) {
|
||||||
vhold(vp);
|
vhold(vp);
|
||||||
|
vhold(lvp);
|
||||||
mp = vp->v_mount;
|
mp = vp->v_mount;
|
||||||
lvp = NULLVPTOLOWERVP(vp);
|
vfs_ref(mp);
|
||||||
if (ap->a_unlock_vp)
|
|
||||||
vref(lvp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res = VOP_VPUT_PAIR(ldvp, &lvp, ap->a_unlock_vp);
|
res = VOP_VPUT_PAIR(ldvp, lvp != NULL ? &lvp : NULL, true);
|
||||||
|
if (vp != NULL && ap->a_unlock_vp)
|
||||||
|
vrele(vp);
|
||||||
|
vrele(dvp);
|
||||||
|
|
||||||
/* lvp might have been unlocked and vp reclaimed */
|
if (vp == NULL || ap->a_unlock_vp)
|
||||||
if (vp != NULL) {
|
return (res);
|
||||||
if (!ap->a_unlock_vp && vp->v_vnlock != lvp->v_vnlock) {
|
|
||||||
|
/* lvp has been unlocked and vp might be reclaimed */
|
||||||
|
VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
|
||||||
|
if (vp->v_data == NULL && vfs_busy(mp, MBF_NOWAIT) == 0) {
|
||||||
|
vput(vp);
|
||||||
|
vget(lvp, LK_EXCLUSIVE | LK_RETRY);
|
||||||
|
if (VN_IS_DOOMED(lvp)) {
|
||||||
|
vput(lvp);
|
||||||
|
vget(vp, LK_EXCLUSIVE | LK_RETRY);
|
||||||
|
} else {
|
||||||
error = null_nodeget(mp, lvp, &vp1);
|
error = null_nodeget(mp, lvp, &vp1);
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
vput(vp);
|
|
||||||
*vpp = vp1;
|
*vpp = vp1;
|
||||||
|
} else {
|
||||||
|
vget(vp, LK_EXCLUSIVE | LK_RETRY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ap->a_unlock_vp)
|
vfs_unbusy(mp);
|
||||||
vrele(vp);
|
|
||||||
vdrop(vp);
|
|
||||||
}
|
}
|
||||||
vrele(dvp);
|
vdrop(lvp);
|
||||||
|
vdrop(vp);
|
||||||
|
vfs_rel(mp);
|
||||||
|
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user