Index: sys/ufs/ffs/ffs_snapshot.c =================================================================== --- sys/ufs/ffs/ffs_snapshot.c +++ sys/ufs/ffs/ffs_snapshot.c @@ -368,8 +368,12 @@ if (error) goto out; bawrite(nbp); - if (cg % 10 == 0) - ffs_syncvnode(vp, MNT_WAIT, 0); + if (cg % 10 == 0) { + error = ffs_syncvnode(vp, MNT_WAIT, 0); + /* vp possibly reclaimed if unlocked */ + if (error != 0) + goto out; + } } /* * Copy all the cylinder group maps. Although the @@ -391,8 +395,8 @@ goto out; error = cgaccount(cg, vp, nbp, 1); bawrite(nbp); - if (cg % 10 == 0) - ffs_syncvnode(vp, MNT_WAIT, 0); + if (cg % 10 == 0 && error == 0) + error = ffs_syncvnode(vp, MNT_WAIT, 0); if (error) goto out; } Index: sys/ufs/ffs/ffs_softdep.c =================================================================== --- sys/ufs/ffs/ffs_softdep.c +++ sys/ufs/ffs/ffs_softdep.c @@ -748,7 +748,7 @@ static void clear_unlinked_inodedep(struct inodedep *); static struct inodedep *first_unlinked_inodedep(struct ufsmount *); static int flush_pagedep_deps(struct vnode *, struct mount *, - struct diraddhd *); + struct diraddhd *, struct buf *); static int free_pagedep(struct pagedep *); static int flush_newblk_dep(struct vnode *, struct mount *, ufs_lbn_t); static int flush_inodedep_deps(struct vnode *, struct mount *, ino_t); @@ -1389,6 +1389,60 @@ /* List of all filesystems mounted with soft updates */ static TAILQ_HEAD(, mount_softdeps) softdepmounts; +static int +get_parent_vp(struct vnode *vp, struct mount *mp, ino_t inum, struct buf *bp, + struct vnode **rvp) +{ + struct vnode *pvp; + int error; + + ASSERT_VOP_ELOCKED(vp, "child vnode must be locked"); + for (;;) { + error = ffs_vgetf(mp, inum, LK_EXCLUSIVE | LK_NOWAIT, &pvp, + FFSV_FORCEINSMQ); + if (error == 0) { + KASSERT(VTOI(pvp)->i_mode != 0, + ("vnode %p/ino %jx is not fully constructed", + vp, (uintmax_t)inum)); + *rvp = pvp; + return (0); + } + VOP_UNLOCK(vp); + if (bp != NULL) { + BUF_NOREC(bp); + BUF_UNLOCK(bp); + } + error = ffs_vgetf(mp, inum, LK_EXCLUSIVE, &pvp, FFSV_FORCEINSMQ); + if (error != 0) { + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + return (error); + } + KASSERT(VTOI(pvp)->i_mode != 0, + ("vnode %p/ino %jx is not fully constructed", + vp, (uintmax_t)inum)); + error = vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT); + if (error == 0) + break; + vput(pvp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + if (VN_IS_DOOMED(vp)) + return (ENOENT); + } + if (VN_IS_DOOMED(vp)) { + if (error == 0) + vput(pvp); + error = ENOENT; + } + if (bp != NULL) { + if (error == 0) + vput(pvp); + error = ERESTART; + } + if (error == 0) + *rvp = pvp; + return (error); +} + /* * This function cleans the worklist for a filesystem. * Each filesystem running with soft dependencies gets its own @@ -12629,25 +12683,9 @@ * for details on possible races. */ FREE_LOCK(ump); - if (ffs_vgetf(mp, parentino, LK_NOWAIT | LK_EXCLUSIVE, &pvp, - FFSV_FORCEINSMQ)) { - /* - * Unmount cannot proceed after unlock because - * caller must have called vn_start_write(). - */ - VOP_UNLOCK(vp); - error = ffs_vgetf(mp, parentino, LK_EXCLUSIVE, - &pvp, FFSV_FORCEINSMQ); - MPASS(VTOI(pvp)->i_mode != 0); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); - if (VN_IS_DOOMED(vp)) { - if (error == 0) - vput(pvp); - error = ENOENT; - } - if (error != 0) - return (error); - } + error = get_parent_vp(vp, mp, parentino, NULL, &pvp); + if (error != 0) + return (error); /* * All MKDIR_PARENT dependencies and all the NEWBLOCK pagedeps * that are contained in direct blocks will be resolved by @@ -12972,9 +13010,11 @@ for (i = 0; i < DAHASHSZ; i++) { if (LIST_FIRST(&pagedep->pd_diraddhd[i]) == 0) continue; - if ((error = flush_pagedep_deps(vp, wk->wk_mp, - &pagedep->pd_diraddhd[i]))) { - BUF_NOREC(bp); + error = flush_pagedep_deps(vp, wk->wk_mp, + &pagedep->pd_diraddhd[i], bp); + if (error != 0) { + if (error != ERESTART) + BUF_NOREC(bp); goto out_unlock; } } @@ -13208,10 +13248,11 @@ * Eliminate a pagedep dependency by flushing out all its diradd dependencies. */ static int -flush_pagedep_deps(pvp, mp, diraddhdp) +flush_pagedep_deps(pvp, mp, diraddhdp, locked_bp) struct vnode *pvp; struct mount *mp; struct diraddhd *diraddhdp; + struct buf *locked_bp; { struct inodedep *inodedep; struct inoref *inoref; @@ -13278,10 +13319,9 @@ } if (dap->da_state & MKDIR_BODY) { FREE_LOCK(ump); - if ((error = ffs_vgetf(mp, inum, LK_EXCLUSIVE, &vp, - FFSV_FORCEINSMQ))) + error = get_parent_vp(pvp, mp, inum, locked_bp, &vp); + if (error != 0) break; - MPASS(VTOI(vp)->i_mode != 0); error = flush_newblk_dep(vp, mp, 0); /* * If we still have the dependency we might need to @@ -13343,10 +13383,9 @@ */ if (dap == LIST_FIRST(diraddhdp)) { FREE_LOCK(ump); - if ((error = ffs_vgetf(mp, inum, LK_EXCLUSIVE, &vp, - FFSV_FORCEINSMQ))) + error = get_parent_vp(pvp, mp, inum, locked_bp, &vp); + if (error != 0) break; - MPASS(VTOI(vp)->i_mode != 0); error = ffs_update(vp, 1); vput(vp); if (error) Index: sys/ufs/ffs/ffs_vnops.c =================================================================== --- sys/ufs/ffs/ffs_vnops.c +++ sys/ufs/ffs/ffs_vnops.c @@ -325,6 +325,9 @@ if (!LIST_EMPTY(&bp->b_dep) && (error = softdep_sync_buf(vp, bp, wait ? MNT_WAIT : MNT_NOWAIT)) != 0) { + /* Lock order conflict, buffer was already unlocked. */ + if (error == ERESTART) + goto next; /* I/O error. */ if (error != EBUSY) { BUF_UNLOCK(bp);