diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 672f673..7c27ba7 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -1014,17 +1014,28 @@ int insmntque1(struct vnode *vp, struct mount *mp, void (*dtr)(struct vnode *, void *), void *dtr_arg) { + int locked; KASSERT(vp->v_mount == NULL, ("insmntque: vnode already on per mount vnode list")); VNASSERT(mp != NULL, vp, ("Don't call insmntque(foo, NULL)")); +#ifdef DEBUG_VFS_LOCKS + if (!VFS_NEEDSGIANT(mp)) + ASSERT_VOP_ELOCKED(vp, + "insmntque: mp-safe fs and non-locked vp"); +#endif MNT_ILOCK(mp); if ((mp->mnt_kern_flag & MNTK_NOINSMNTQ) != 0 && - mp->mnt_nvnodelistsize == 0) { - MNT_IUNLOCK(mp); - if (dtr != NULL) - dtr(vp, dtr_arg); - return (EBUSY); + ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + mp->mnt_nvnodelistsize == 0)) { + locked = VOP_ISLOCKED(vp); + if (!locked || (locked == LK_EXCLUSIVE && + !(vp->v_vflag & VV_FORCEINSMQ))) { + MNT_IUNLOCK(mp); + if (dtr != NULL) + dtr(vp, dtr_arg); + return (EBUSY); + } } vp->v_mount = mp; MNT_REF(mp); @@ -3140,9 +3151,13 @@ vfs_allocate_syncvnode(struct mount *mp) return (error); } vp->v_type = VNON; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + vp->v_vflag |= VV_FORCEINSMQ; error = insmntque(vp, mp); if (error != 0) panic("vfs_allocate_syncvnode: insmntque failed"); + vp->v_vflag &= ~VV_FORCEINSMQ; + VOP_UNLOCK(vp, 0); /* * Place the vnode onto the syncer worklist. We attempt to * scatter them about on the list so that they will go off diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 0d191e3..5591615 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -248,6 +248,7 @@ struct xvnode { #define VV_NOKNOTE 0x0200 /* don't activate knotes on this vnode */ #define VV_DELETED 0x0400 /* should be removed */ #define VV_MD 0x0800 /* vnode backs the md device */ +#define VV_FORCEINSMQ 0x1000 /* force the insmntque to succeed */ /* * Vnode attributes. A field value of VNOVAL represents a field whose value diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h index a496542..3c8d323 100644 --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -87,6 +87,9 @@ int ffs_valloc(struct vnode *, int, struct ucred *, struct vnode **); int ffs_vfree(struct vnode *, ino_t, int); vfs_vget_t ffs_vget; +int ffs_vgetf(struct mount *, ino_t, int, struct vnode **, int); + +#define FFSV_FORCEINSMQ 0x0001 extern struct vop_vector ffs_vnodeops1; extern struct vop_vector ffs_fifoops1; diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c index 657d46c..a3a76ea 100644 --- a/sys/ufs/ffs/ffs_softdep.c +++ b/sys/ufs/ffs/ffs_softdep.c @@ -912,8 +912,8 @@ process_worklist_item(mp, flags) wk->wk_state |= INPROGRESS; ump->softdep_on_worklist_inprogress++; FREE_LOCK(&lk); - ffs_vget(mp, WK_DIRREM(wk)->dm_oldinum, - LK_NOWAIT | LK_EXCLUSIVE, &vp); + ffs_vgetf(mp, WK_DIRREM(wk)->dm_oldinum, + LK_NOWAIT | LK_EXCLUSIVE, &vp, FFSV_FORCEINSMQ); ACQUIRE_LOCK(&lk); wk->wk_state &= ~INPROGRESS; ump->softdep_on_worklist_inprogress--; @@ -1067,8 +1067,11 @@ softdep_flushfiles(oldmnt, flags, td) int flags; struct thread *td; { - int error, count, loopcnt; + int error, count, loopcnt, retry_flush_count, retry; + loopcnt = 10; + retry_flush_count = 3; +retry_flush: error = 0; /* @@ -1077,7 +1080,7 @@ softdep_flushfiles(oldmnt, flags, td) * creates. In theory, this loop can happen at most twice, * but we give it a few extra just to be sure. */ - for (loopcnt = 10; loopcnt > 0; loopcnt--) { + for (; loopcnt > 0; loopcnt--) { /* * Do another flush in case any vnodes were brought in * as part of the cleanup operations. @@ -1100,6 +1103,24 @@ softdep_flushfiles(oldmnt, flags, td) } if (!error) error = softdep_waitidle(oldmnt); + if (!error) { + if (oldmnt->mnt_kern_flag & MNTK_UNMOUNT) { + retry = 0; + MNT_ILOCK(oldmnt); + KASSERT((oldmnt->mnt_kern_flag & MNTK_NOINSMNTQ) != 0, + ("softdep_flushfiles: !MNTK_NOINSMNTQ")); + if (oldmnt->mnt_nvnodelistsize > 0) { + if (--retry_flush_count > 0) { + retry = 1; + loopcnt = 3; + } else + error = EBUSY; + } + MNT_IUNLOCK(oldmnt); + if (retry) + goto retry_flush; + } + } return (error); } @@ -2769,8 +2790,9 @@ handle_workitem_freeblocks(freeblks, flags) */ if (freeblks->fb_chkcnt != blocksreleased && (fs->fs_flags & FS_UNCLEAN) != 0 && - ffs_vget(freeblks->fb_list.wk_mp, freeblks->fb_previousinum, - (flags & LK_NOWAIT) | LK_EXCLUSIVE, &vp) == 0) { + ffs_vgetf(freeblks->fb_list.wk_mp, freeblks->fb_previousinum, + (flags & LK_NOWAIT) | LK_EXCLUSIVE, &vp, FFSV_FORCEINSMQ) + == 0) { ip = VTOI(vp); DIP_SET(ip, i_blocks, DIP(ip, i_blocks) + \ freeblks->fb_chkcnt - blocksreleased); @@ -3557,8 +3579,8 @@ handle_workitem_remove(dirrem, xp) int error; if ((vp = xp) == NULL && - (error = ffs_vget(dirrem->dm_list.wk_mp, - dirrem->dm_oldinum, LK_EXCLUSIVE, &vp)) != 0) { + (error = ffs_vgetf(dirrem->dm_list.wk_mp, + dirrem->dm_oldinum, LK_EXCLUSIVE, &vp, FFSV_FORCEINSMQ)) != 0) { softdep_error("handle_workitem_remove: vget", error); return; } @@ -5073,9 +5095,11 @@ softdep_fsync(vp) * for details on possible races. */ FREE_LOCK(&lk); - if (ffs_vget(mp, parentino, LK_NOWAIT | LK_EXCLUSIVE, &pvp)) { + if (ffs_vgetf(mp, parentino, LK_NOWAIT | LK_EXCLUSIVE, &pvp, + FFSV_FORCEINSMQ)) { VOP_UNLOCK(vp, 0); - error = ffs_vget(mp, parentino, LK_EXCLUSIVE, &pvp); + error = ffs_vgetf(mp, parentino, LK_EXCLUSIVE, + &pvp, FFSV_FORCEINSMQ); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) return (error); @@ -5580,7 +5604,8 @@ flush_pagedep_deps(pvp, mp, diraddhdp) inum = dap->da_newinum; if (dap->da_state & MKDIR_BODY) { FREE_LOCK(&lk); - if ((error = ffs_vget(mp, inum, LK_EXCLUSIVE, &vp))) + if ((error = ffs_vgetf(mp, inum, LK_EXCLUSIVE, &vp, + FFSV_FORCEINSMQ))) break; if ((error=ffs_syncvnode(vp, MNT_NOWAIT)) || (error=ffs_syncvnode(vp, MNT_NOWAIT))) { @@ -5920,7 +5945,8 @@ clear_remove(td) if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) continue; FREE_LOCK(&lk); - if ((error = ffs_vget(mp, ino, LK_EXCLUSIVE, &vp))) { + if ((error = ffs_vgetf(mp, ino, LK_EXCLUSIVE, &vp, + FFSV_FORCEINSMQ))) { softdep_error("clear_remove: vget", error); vn_finished_write(mp); ACQUIRE_LOCK(&lk); @@ -5992,7 +6018,8 @@ clear_inodedeps(td) if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) continue; FREE_LOCK(&lk); - if ((error = ffs_vget(mp, ino, LK_EXCLUSIVE, &vp)) != 0) { + if ((error = ffs_vgetf(mp, ino, LK_EXCLUSIVE, &vp, + FFSV_FORCEINSMQ)) != 0) { softdep_error("clear_inodedeps: vget", error); vn_finished_write(mp); ACQUIRE_LOCK(&lk); diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 003e97f..af284b6 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -1310,6 +1310,17 @@ ffs_vget(mp, ino, flags, vpp) int flags; struct vnode **vpp; { + return (ffs_vgetf(mp, ino, flags, vpp, 0)); +} + +int +ffs_vgetf(mp, ino, flags, vpp, ffs_flags) + struct mount *mp; + ino_t ino; + int flags; + struct vnode **vpp; + int ffs_flags; +{ struct fs *fs; struct inode *ip; struct ufsmount *ump; @@ -1384,12 +1395,15 @@ ffs_vget(mp, ino, flags, vpp) td = curthread; lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); + if (ffs_flags & FFSV_FORCEINSMQ) + vp->v_vflag |= VV_FORCEINSMQ; error = insmntque(vp, mp); if (error != 0) { uma_zfree(uma_inode, ip); *vpp = NULL; return (error); } + vp->v_vflag &= ~VV_FORCEINSMQ; error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); if (error || *vpp != NULL) return (error);