git: 866dd6335a6c - main - unionfs: various locking fixes
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 06 Nov 2021 14:02:17 UTC
The branch main has been updated by jah: URL: https://cgit.FreeBSD.org/src/commit/?id=866dd6335a6c74cc7615a8ad8ae2f2b2ec4ece94 commit 866dd6335a6c74cc7615a8ad8ae2f2b2ec4ece94 Author: Jason A. Harmening <jah@FreeBSD.org> AuthorDate: 2021-10-24 17:24:18 +0000 Commit: Jason A. Harmening <jah@FreeBSD.org> CommitDate: 2021-11-06 14:08:33 +0000 unionfs: various locking fixes --Clearing cached subdirectories in unionfs_noderem() should be done under the vnode interlock --When preparing to switch the vnode lock in both unionfs_node_update() and unionfs_noderem(), the incoming lock should be acquired before updating the v_vnlock field to point to it. Otherwise we effectively break the locking contract for a brief window. Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D32629 --- sys/fs/unionfs/union_subr.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/sys/fs/unionfs/union_subr.c b/sys/fs/unionfs/union_subr.c index 326483bcc400..7bde71bb17b1 100644 --- a/sys/fs/unionfs/union_subr.c +++ b/sys/fs/unionfs/union_subr.c @@ -439,6 +439,9 @@ unionfs_noderem(struct vnode *vp, struct thread *td) struct vnode *dvp; int count; + if (lockmgr(&(vp->v_lock), LK_EXCLUSIVE, NULL) != 0) + panic("the lock for deletion is unacquirable."); + /* * Use the interlock to protect the clearing of v_data to * prevent faults in unionfs_lock(). @@ -459,6 +462,22 @@ unionfs_noderem(struct vnode *vp, struct thread *td) 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 + * be necessary during forced unmount, when the vnode may + * be reclaimed with a non-zero use count. Otherwise the + * reference held by each child should prevent reclamation. + */ + for (count = 0; count <= UNIONFSHASHMASK; count++) { + hd = unp->un_hashtbl + count; + LIST_FOREACH_SAFE(unp_t1, hd, un_hash, unp_t2) { + LIST_REMOVE(unp_t1, un_hash); + unp_t1->un_hash.le_next = NULL; + unp_t1->un_hash.le_prev = NULL; + } + } + } VI_UNLOCK(vp); if (lvp != NULLVP) @@ -469,9 +488,6 @@ unionfs_noderem(struct vnode *vp, struct thread *td) if (dvp != NULLVP) unionfs_rem_cached_vnode(unp, dvp); - if (lockmgr(vp->v_vnlock, LK_EXCLUSIVE, VI_MTX(vp)) != 0) - panic("the lock for deletion is unacquirable."); - if (lvp != NULLVP) vrele(lvp); if (uvp != NULLVP) @@ -483,14 +499,6 @@ unionfs_noderem(struct vnode *vp, struct thread *td) } if (unp->un_hashtbl != NULL) { - for (count = 0; count <= UNIONFSHASHMASK; count++) { - hd = unp->un_hashtbl + count; - LIST_FOREACH_SAFE(unp_t1, hd, un_hash, unp_t2) { - LIST_REMOVE(unp_t1, un_hash); - unp_t1->un_hash.le_next = NULL; - unp_t1->un_hash.le_prev = NULL; - } - } hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, UNIONFSHASHMASK); } @@ -798,15 +806,16 @@ unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp, dvp = unp->un_dvp; /* - * lock update + * Uppdate the upper vnode's lock state to match the lower vnode, + * and then switch the unionfs vnode's lock to the upper vnode. */ + lockrec = lvp->v_vnlock->lk_recurse; + for (count = 0; count < lockrec; count++) + vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY); VI_LOCK(vp); unp->un_uppervp = uvp; vp->v_vnlock = uvp->v_vnlock; VI_UNLOCK(vp); - lockrec = lvp->v_vnlock->lk_recurse; - for (count = 0; count < lockrec; count++) - vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY); /* * Re-cache the unionfs vnode against the upper vnode