Index: sys/fs/unionfs/union_subr.c =================================================================== --- sys/fs/unionfs/union_subr.c +++ sys/fs/unionfs/union_subr.c @@ -57,6 +57,8 @@ #include #include +#include + #include #include @@ -435,6 +437,7 @@ struct vnode *uvp; struct vnode *dvp; int count; + int writerefs; KASSERT(vp->v_vnlock->lk_recurse == 0, ("%s: vnode %p locked recursively", __func__, vp)); @@ -454,13 +457,6 @@ vp->v_vnlock = &(vp->v_lock); vp->v_data = NULL; vp->v_object = NULL; - if (vp->v_writecount > 0) { - if (uvp != NULL) - VOP_ADD_WRITECOUNT(uvp, -vp->v_writecount); - else if (lvp != NULL) - VOP_ADD_WRITECOUNT(lvp, -vp->v_writecount); - } else if (vp->v_writecount < 0) - vp->v_writecount = 0; if (unp->un_hashtbl != NULL) { /* * Clear out any cached child vnodes. This should only @@ -479,6 +475,20 @@ } VI_UNLOCK(vp); + writerefs = atomic_load_int(&vp->v_writecount); + VNASSERT(writerefs >= 0, vp, + ("%s: negative write count %d", __func__, writerefs)); + /* + * If we were opened for write, we leased the write reference + * to the lower vnode. If this is a reclamation due to the + * forced unmount, undo the reference now. + */ + if (writerefs > 0) { + if (uvp != NULL) + VOP_ADD_WRITECOUNT(uvp, -writerefs); + else if (lvp != NULL) + VOP_ADD_WRITECOUNT(lvp, -writerefs); + } if (lvp != NULLVP) VOP_UNLOCK(lvp); if (uvp != NULLVP) Index: sys/fs/unionfs/union_vnops.c =================================================================== --- sys/fs/unionfs/union_vnops.c +++ sys/fs/unionfs/union_vnops.c @@ -61,6 +61,8 @@ #include +#include + #include #include #include @@ -2523,26 +2525,26 @@ { struct vnode *tvp, *vp; struct unionfs_node *unp; - int error; + int error, writerefs; vp = ap->a_vp; unp = VTOUNIONFS(vp); tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp; - VI_LOCK(vp); + error = VOP_ADD_WRITECOUNT(tvp, ap->a_inc); + if (error != 0) + return (error); + /* + * We need to track the write refs we've passed to the underlying + * vnodes so that we can undo them in case we are forcibly unmounted. + */ + writerefs = atomic_fetchadd_int(&vp->v_writecount, ap->a_inc); /* text refs are bypassed to lowervp */ - VNASSERT(vp->v_writecount >= 0, vp, ("wrong null writecount")); - VNASSERT(vp->v_writecount + ap->a_inc >= 0, vp, - ("wrong writecount inc %d", ap->a_inc)); - if (tvp != NULL) - error = VOP_ADD_WRITECOUNT(tvp, ap->a_inc); - else if (vp->v_writecount < 0) - error = ETXTBSY; - else - error = 0; - if (error == 0) - vp->v_writecount += ap->a_inc; - VI_UNLOCK(vp); - return (error); + VNASSERT(writerefs >= 0, vp, + ("%s: invalid write count %d", __func__, writerefs)); + VNASSERT(writerefs + ap->a_inc >= 0, vp, + ("%s: invalid write count inc %d + %d", __func__, + writerefs, ap->a_inc)); + return (0); } static int @@ -2667,6 +2669,38 @@ return (res); } +static int +unionfs_set_text(struct vop_set_text_args *ap) +{ + struct vnode *tvp; + struct unionfs_node *unp; + int error; + + /* + * We assume text refs are managed against lvp/uvp through the + * executable mapping backed by its VM object. We therefore don't + * need to track leased text refs in the case of a forcible unmount. + */ + unp = VTOUNIONFS(ap->a_vp); + ASSERT_VOP_LOCKED(ap->a_vp, __func__); + tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp; + error = VOP_SET_TEXT(tvp); + return (error); +} + +static int +unionfs_unset_text(struct vop_unset_text_args *ap) +{ + struct vnode *tvp; + struct unionfs_node *unp; + + ASSERT_VOP_LOCKED(ap->a_vp, __func__); + unp = VTOUNIONFS(ap->a_vp); + tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp; + VOP_UNSET_TEXT_CHECKED(tvp); + return (0); +} + struct vop_vector unionfs_vnodeops = { .vop_default = &default_vnodeops, @@ -2718,5 +2752,7 @@ .vop_vptofh = unionfs_vptofh, .vop_add_writecount = unionfs_add_writecount, .vop_vput_pair = unionfs_vput_pair, + .vop_set_text = unionfs_set_text, + .vop_unset_text = unionfs_unset_text, }; VFS_VOP_VECTOR_REGISTER(unionfs_vnodeops);