tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Making forced unmounts work



Forced unmounts will most likely panic the kernel.  The main problem is
other threads running a vnode operation when we come to clean and
reclaim an active vnode and therefore change its operation vector and
destroy the file system private data without locking or synchronisation.

One solution is to to suspend the file system while it gets unmounted.
This way all other threads running a vnode operation will wait for
fstrans_start() to succeed.  Changing fstrans_start() to detect a now
dead vnode it is possible to restart the vnode operation with the new
operations vector.

In short the attached diff:

- Adds a new kernel-internal errno ERESTARTVOP and changes VCALL() to
  restart a vnode operation once it returns ERESTARTVOP.

- Changes fstrans_start() to take an optional `hint vnode' and return
  ERESTARTVOP if the vnode becomes dead.

- Rearranges all file system operations to call fstrans_start() before
  accessing file system private data and check (and return) an error.

- Changes vfs_suspend() to optionally suspend a file system without
  syncing it to disk.  This feature is used when revoking a vnode.

- Changes dounmount() to suspend a file system during the unmount.

- Keeps the `struct mp' until all (dead) vnodes disappear.

- Adds some missing operations to deadfs.

Comments or objections?

--
J. Hannken-Illjes - hannken%eis.cs.tu-bs.de@localhost - TU Braunschweig 
(Germany)

Index: sys/sys/errno.h
===================================================================
RCS file: /cvsroot/src/sys/sys/errno.h,v
retrieving revision 1.39
diff -p -u -4 -r1.39 errno.h
--- sys/sys/errno.h     31 Oct 2006 00:38:07 -0000      1.39
+++ sys/sys/errno.h     26 Nov 2012 13:50:51 -0000
@@ -180,7 +180,8 @@
 #define        ERESTART        -3              /* restart syscall */
 #define        EPASSTHROUGH    -4              /* ioctl not handled by this 
layer */
 #define        EDUPFD          -5              /* Dup given fd */
 #define        EMOVEFD         -6              /* Move given fd */
+#define        ERESTARTVOP     -7              /* Restart this vnode operation 
*/
 #endif
 
 #endif /* !_SYS_ERRNO_H_ */
Index: sys/sys/vnode.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode.h,v
retrieving revision 1.237
diff -p -u -4 -r1.237 vnode.h
--- sys/sys/vnode.h     18 Nov 2012 18:39:24 -0000      1.237
+++ sys/sys/vnode.h     26 Nov 2012 13:50:55 -0000
@@ -190,8 +190,9 @@ typedef struct vnode vnode_t;
 #define        VV_ISTTY        0x00000004      /* vnode represents a tty */
 #define        VV_MAPPED       0x00000008      /* vnode might have user 
mappings */
 #define        VV_MPSAFE       0x00000010      /* file system code is MP safe 
*/
 #define        VV_LOCKSWORK    0x00000020      /* FS supports locking 
discipline */
+#define        VV_DEAD         0x00000040      /* vnode is active and dead */
 
 /*
  * The second set are locked by vp->v_interlock.
  */
@@ -510,12 +511,23 @@ struct vop_generic_args {
 
 /*
  * This call works for vnodes in the kernel.
  */
-#define        VCALL(VP,OFF,AP) VOCALL((VP)->v_op,(OFF),(AP))
 #define        VDESC(OP) (& __CONCAT(OP,_desc))
 #define        VOFFSET(OP) (VDESC(OP)->vdesc_offset)
 
+static __inline int __unused
+VCALL(struct vnode *vp, int offset, void *ap)
+{
+       int error;
+
+       do {
+               error = VOCALL(vp->v_op, offset, ap);
+       } while (__predict_false(error == ERESTARTVOP));
+
+       return error;
+}
+
 /* XXX This include should go away */
 #include <sys/mount.h>
 
 /*
Index: sys/sys/fstrans.h
===================================================================
RCS file: /cvsroot/src/sys/sys/fstrans.h,v
retrieving revision 1.10
diff -p -u -4 -r1.10 fstrans.h
--- sys/sys/fstrans.h   7 Nov 2008 00:15:42 -0000       1.10
+++ sys/sys/fstrans.h   26 Nov 2012 13:50:58 -0000
@@ -37,10 +37,13 @@
 #define        _SYS_FSTRANS_H_
 
 #include <sys/mount.h>
 
+/* Flags to VFS_SUSPENDCTL(): */
 #define SUSPEND_SUSPEND        0x0001          /* VFS_SUSPENDCTL: suspend */
 #define SUSPEND_RESUME 0x0002          /* VFS_SUSPENDCTL: resume */
+/* Flags to VFS_SUSPENDCTL() and vfs_suspend(): */
+#define SUSPEND_SYNC   0x0004          /* sync the file system to disk */
 
 enum fstrans_lock_type {
        FSTRANS_LAZY = 1,               /* Granted while not suspended */
        FSTRANS_SHARED = 2              /* Granted while not suspending */
@@ -56,11 +59,11 @@ enum fstrans_state {
        FSTRANS_SUSPENDED
 };
 
 void   fstrans_init(void);
-#define fstrans_start(mp, t)           _fstrans_start((mp), (t), 1)
-#define fstrans_start_nowait(mp, t)    _fstrans_start((mp), (t), 0)
-int    _fstrans_start(struct mount *, enum fstrans_lock_type, int);
+#define fstrans_start(mp, vp, t)       _fstrans_start((mp), (vp), (t), 1)
+#define fstrans_start_nowait(mp, vp, t)        _fstrans_start((mp), (vp), (t), 
0)
+int    _fstrans_start(struct mount *, struct vnode *, enum fstrans_lock_type, 
int);
 void   fstrans_done(struct mount *);
 int    fstrans_is_owner(struct mount *);
 int    fstrans_mount(struct mount *);
 void   fstrans_unmount(struct mount *);
Index: sys/kern/vfs_trans.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_trans.c,v
retrieving revision 1.25
diff -p -u -4 -r1.25 vfs_trans.c
--- sys/kern/vfs_trans.c        12 May 2009 11:42:12 -0000      1.25
+++ sys/kern/vfs_trans.c        26 Nov 2012 13:51:02 -0000
@@ -42,12 +42,14 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,
 #define _LWP_API_PRIVATE       /* Need _lwp_getspecific_by_lwp() */
 #endif
 
 #include <sys/param.h>
+#include <sys/kernel.h>
 #include <sys/systm.h>
 #include <sys/buf.h>
 #include <sys/kmem.h>
 #include <sys/mount.h>
+#include <sys/atomic.h>
 #include <sys/rwlock.h>
 #include <sys/vnode.h>
 #define _FSTRANS_API_PRIVATE
 #include <sys/fstrans.h>
@@ -62,21 +64,25 @@ struct fscow_handler {
        void *ch_arg;
 };
 struct fstrans_lwp_info {
        struct fstrans_lwp_info *fli_succ;
+       int fli_when;
        struct mount *fli_mount;
        int fli_trans_cnt;
        int fli_cow_cnt;
        enum fstrans_lock_type fli_lock_type;
 };
 struct fstrans_mount_info {
        enum fstrans_state fmi_state;
+       unsigned int fmi_refcnt;
        krwlock_t fmi_shared_lock;
        krwlock_t fmi_lazy_lock;
        krwlock_t fmi_cow_lock;
        SLIST_HEAD(, fscow_handler) fmi_cow_handler;
 };
 
+extern lwp_t *vrele_lwp;
+
 static specificdata_key_t lwp_data_key;
 static kmutex_t vfs_suspend_lock;      /* Serialize suspensions. */
 static pool_cache_t fstrans_cache;
 
@@ -107,10 +113,13 @@ fstrans_lwp_dtor(void *arg)
 {
        struct fstrans_lwp_info *fli, *fli_next;
 
        for (fli = arg; fli; fli = fli_next) {
+               KASSERT(fli->fli_mount != NULL);
                KASSERT(fli->fli_trans_cnt == 0);
                KASSERT(fli->fli_cow_cnt == 0);
+               if (fli->fli_mount)
+                       fstrans_unmount(fli->fli_mount);
                fli_next = fli->fli_succ;
                pool_cache_put(fstrans_cache, fli);
        }
 }
@@ -121,20 +130,27 @@ fstrans_lwp_dtor(void *arg)
 int
 fstrans_mount(struct mount *mp)
 {
        struct fstrans_mount_info *new;
+       int error;
 
+       error = vfs_busy(mp, NULL);
+       if (error)
+               return error;
        if ((new = kmem_alloc(sizeof(*new), KM_SLEEP)) == NULL)
                return ENOMEM;
        new->fmi_state = FSTRANS_NORMAL;
+       new->fmi_refcnt = 1;
        rw_init(&new->fmi_lazy_lock);
        rw_init(&new->fmi_shared_lock);
        SLIST_INIT(&new->fmi_cow_handler);
        rw_init(&new->fmi_cow_lock);
 
        mp->mnt_transinfo = new;
        mp->mnt_iflag |= IMNT_HAS_TRANS;
 
+       vfs_unbusy(mp, true, NULL);
+
        return 0;
 }
 
 /*
@@ -148,8 +164,11 @@ fstrans_unmount(struct mount *mp)
 
        if ((fmi = mp->mnt_transinfo) == NULL)
                return;
 
+       if (atomic_dec_uint_nv(&fmi->fmi_refcnt) > 0)
+               return;
+
        KASSERT(fmi->fmi_state == FSTRANS_NORMAL);
        rw_destroy(&fmi->fmi_lazy_lock);
        rw_destroy(&fmi->fmi_shared_lock);
        rw_enter(&fmi->fmi_cow_lock, RW_WRITER);
@@ -161,41 +180,50 @@ fstrans_unmount(struct mount *mp)
        rw_destroy(&fmi->fmi_cow_lock);
        kmem_free(fmi, sizeof(*fmi));
        mp->mnt_iflag &= ~IMNT_HAS_TRANS;
        mp->mnt_transinfo = NULL;
+
+       vfs_destroy(mp);
 }
 
 /*
  * Retrieve the per lwp info for this mount
  */
 static struct fstrans_lwp_info *
 fstrans_get_lwp_info(struct mount *mp)
 {
-       struct fstrans_lwp_info *fli, *new_fli;
+       struct fstrans_lwp_info *fli, *res;
+       struct fstrans_mount_info *fmi = mp->mnt_transinfo;
 
-       new_fli = NULL;
+       res = NULL;
        for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
                if (fli->fli_mount == mp)
-                       return fli;
-               else if (fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0 &&
-                   new_fli == NULL)
-                       new_fli = fli;
+                       res = fli;
+               else if (fli->fli_mount != NULL &&
+                   fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0 &&
+                   hardclock_ticks - fli->fli_when > 300 * hz) {
+                       fstrans_unmount(fli->fli_mount);
+                       fli->fli_mount = NULL;
+               }
+               if (res == NULL && fli->fli_mount == NULL)
+                       res = fli;
        }
 
-       if (new_fli == NULL) {
-               new_fli = pool_cache_get(fstrans_cache, PR_WAITOK);
-               new_fli->fli_trans_cnt = 0;
-               new_fli->fli_cow_cnt = 0;
-               new_fli->fli_succ = lwp_getspecific(lwp_data_key);
-               lwp_setspecific(lwp_data_key, new_fli);
+       if (res != NULL && res->fli_mount == mp)
+               return res;
+       else if (res == NULL) {
+               res = pool_cache_get(fstrans_cache, PR_WAITOK);
+               res->fli_succ = lwp_getspecific(lwp_data_key);
+               lwp_setspecific(lwp_data_key, res);
        }
 
-       KASSERT(new_fli->fli_trans_cnt == 0);
-       KASSERT(new_fli->fli_cow_cnt == 0);
-
-       new_fli->fli_mount = mp;
+       atomic_inc_uint(&fmi->fmi_refcnt);
+       res->fli_mount = mp;
+       res->fli_trans_cnt = 0;
+       res->fli_cow_cnt = 0;
+       res->fli_when = hardclock_ticks;
 
-       return new_fli;
+       return res;
 }
 
 /*
  * Start a transaction.  If this thread already has a transaction on this
@@ -204,18 +232,21 @@ fstrans_get_lwp_info(struct mount *mp)
  * A thread with a shared or lazy transaction lock cannot upgrade to an
  * exclusive one yet.
  */
 int
-_fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait)
+_fstrans_start(struct mount *mp, struct vnode *hintvp,
+    enum fstrans_lock_type lock_type, int wait)
 {
        krwlock_t *lock_p;
        krw_t lock_op;
        struct fstrans_lwp_info *fli;
        struct fstrans_mount_info *fmi;
 
        ASSERT_SLEEPABLE();
+       KASSERT(hintvp == NULL || hintvp->v_mount == mp);
 
-       if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
+       if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0 ||
+           curlwp == vrele_lwp)
                return 0;
 
        fli = fstrans_get_lwp_info(mp);
 
@@ -239,8 +270,13 @@ _fstrans_start(struct mount *mp, enum fs
                rw_enter(lock_p, lock_op);
        else if (rw_tryenter(lock_p, lock_op) == 0)
                return EBUSY;
 
+       if (hintvp != NULL && (hintvp->v_vflag & VV_DEAD) != 0) {
+               rw_exit(lock_p);
+               return ERESTARTVOP;
+       }
+
        fli->fli_trans_cnt = 1;
        fli->fli_lock_type = lock_type;
 
        return 0;
@@ -254,9 +290,10 @@ fstrans_done(struct mount *mp)
 {
        struct fstrans_lwp_info *fli;
        struct fstrans_mount_info *fmi;
 
-       if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
+       if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0 ||
+           curlwp == vrele_lwp)
                return;
 
        for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
                if (fli->fli_mount == mp) {
@@ -276,8 +313,9 @@ fstrans_done(struct mount *mp)
        if (fli->fli_lock_type == FSTRANS_LAZY)
                rw_exit(&fmi->fmi_lazy_lock);
        else
                rw_exit(&fmi->fmi_shared_lock);
+       fli->fli_when = hardclock_ticks;
 }
 
 /*
  * Check if this thread has an exclusive lock.
@@ -310,25 +348,29 @@ fstrans_is_owner(struct mount *mp)
 int
 fstrans_setstate(struct mount *mp, enum fstrans_state new_state)
 {
        struct fstrans_mount_info *fmi;
+       int error;
 
        fmi = mp->mnt_transinfo;
 
        switch (new_state) {
        case FSTRANS_SUSPENDING:
                KASSERT(fmi->fmi_state == FSTRANS_NORMAL);
-               fstrans_start(mp, FSTRANS_EXCL);
+               error = fstrans_start(mp, NULL, FSTRANS_EXCL);
+               KASSERT(error == 0);
                fmi->fmi_state = FSTRANS_SUSPENDING;
                break;
 
        case FSTRANS_SUSPENDED:
                KASSERT(fmi->fmi_state == FSTRANS_NORMAL ||
                        fmi->fmi_state == FSTRANS_SUSPENDING);
                KASSERT(fmi->fmi_state == FSTRANS_NORMAL ||
                        fstrans_is_owner(mp));
-               if (fmi->fmi_state == FSTRANS_NORMAL)
-                       fstrans_start(mp, FSTRANS_EXCL);
+               if (fmi->fmi_state == FSTRANS_NORMAL) {
+                       error = fstrans_start(mp, NULL, FSTRANS_EXCL);
+                       KASSERT(error == 0);
+               }
                rw_enter(&fmi->fmi_lazy_lock, RW_WRITER);
                fmi->fmi_state = FSTRANS_SUSPENDED;
                break;
 
@@ -367,24 +409,18 @@ fstrans_getstate(struct mount *mp)
 /*
  * Request a filesystem to suspend all operations.
  */
 int
-vfs_suspend(struct mount *mp, int nowait)
+vfs_suspend(struct mount *mp, int flags)
 {
        int error;
 
-       if (nowait) {
-               if (!mutex_tryenter(&vfs_suspend_lock))
-                       return EWOULDBLOCK;
-       } else
-               mutex_enter(&vfs_suspend_lock);
+       KASSERT((flags & ~(SUSPEND_SYNC)) == 0);
 
-       mutex_enter(&syncer_mutex);
+       mutex_enter(&vfs_suspend_lock);
 
-       if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND)) != 0) {
-               mutex_exit(&syncer_mutex);
+       if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND | flags)) != 0)
                mutex_exit(&vfs_suspend_lock);
-       }
 
        return error;
 }
 
@@ -395,9 +431,8 @@ void
 vfs_resume(struct mount *mp)
 {
 
        VFS_SUSPENDCTL(mp, SUSPEND_RESUME);
-       mutex_exit(&syncer_mutex);
        mutex_exit(&vfs_suspend_lock);
 }
 
 #if defined(DDB)
Index: sys/kern/vfs_mount.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_mount.c,v
retrieving revision 1.15
diff -p -u -4 -r1.15 vfs_mount.c
--- sys/kern/vfs_mount.c        27 Oct 2012 17:18:39 -0000      1.15
+++ sys/kern/vfs_mount.c        26 Nov 2012 13:51:06 -0000
@@ -88,8 +88,9 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_mount.c,
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/vfs_syscalls.h>
 #include <sys/vnode.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/genfs/genfs.h>
 #include <miscfs/syncfs/syncfs.h>
 #include <miscfs/specfs/specdev.h>
@@ -505,23 +506,13 @@ vflush(struct mount *mp, vnode_t *skipvp
                 */
                if (flags & FORCECLOSE) {
                        mutex_exit(&mntvnode_lock);
                        atomic_inc_uint(&vp->v_usecount);
-                       if (vp->v_type != VBLK && vp->v_type != VCHR) {
+                       if (vp->v_type != VBLK && vp->v_type != VCHR)
                                vclean(vp, DOCLOSE);
-                               vrelel(vp, 0);
-                       } else {
+                       else
                                vclean(vp, 0);
-                               vp->v_op = spec_vnodeop_p; /* XXXSMP */
-                               mutex_exit(vp->v_interlock);
-                               /*
-                                * The vnode isn't clean, but still resides
-                                * on the mount list.  Remove it. XXX This
-                                * is a bit dodgy.
-                                */
-                               vfs_insmntque(vp, NULL);
-                               vrele(vp);
-                       }
+                       vrelel(vp, 0);
                        mutex_enter(&mntvnode_lock);
                        continue;
                }
 #ifdef DEBUG
@@ -538,34 +529,8 @@ vflush(struct mount *mp, vnode_t *skipvp
        return (0);
 }
 
 /*
- * Remove clean vnodes from a mountpoint's vnode list.
- */
-void
-vfs_scrubvnlist(struct mount *mp)
-{
-       vnode_t *vp, *nvp;
-
-retry:
-       mutex_enter(&mntvnode_lock);
-       for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) {
-               nvp = TAILQ_NEXT(vp, v_mntvnodes);
-               mutex_enter(vp->v_interlock);
-               if ((vp->v_iflag & VI_CLEAN) != 0) {
-                       TAILQ_REMOVE(&mp->mnt_vnodelist, vp, v_mntvnodes);
-                       vp->v_mount = NULL;
-                       mutex_exit(&mntvnode_lock);
-                       mutex_exit(vp->v_interlock);
-                       vfs_destroy(mp);
-                       goto retry;
-               }
-               mutex_exit(vp->v_interlock);
-       }
-       mutex_exit(&mntvnode_lock);
-}
-
-/*
  * Mount a file system.
  */
 
 /*
@@ -781,10 +746,13 @@ err_unmounted:
  */
 int
 dounmount(struct mount *mp, int flags, struct lwp *l)
 {
-       vnode_t *coveredvp;
+       vnode_t *coveredvp, *vp;
        int error, async, used_syncer;
+       bool is_suspended;
+
+       is_suspended = false;
 
 #if NVERIEXEC > 0
        error = veriexec_unmountchk(mp);
        if (error)
@@ -826,12 +794,14 @@ dounmount(struct mount *mp, int flags, s
        cache_purgevfs(mp);     /* remove cache entries for this file sys */
        if (mp->mnt_syncer != NULL)
                vfs_deallocate_syncvnode(mp);
        error = 0;
-       if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+       if ((mp->mnt_iflag & IMNT_HAS_TRANS) != 0) {
+               error = vfs_suspend(mp, SUSPEND_SYNC);
+               is_suspended = (error == 0);
+       } else if ((mp->mnt_flag & MNT_RDONLY) == 0) {
                error = VFS_SYNC(mp, MNT_WAIT, l->l_cred);
        }
-       vfs_scrubvnlist(mp);
        if (error == 0 || (flags & MNT_FORCE)) {
                error = VFS_UNMOUNT(mp, flags);
        }
        if (error) {
@@ -839,21 +809,31 @@ dounmount(struct mount *mp, int flags, s
                        (void) vfs_allocate_syncvnode(mp);
                mp->mnt_iflag &= ~IMNT_UNMOUNT;
                mp->mnt_flag |= async;
                rw_exit(&mp->mnt_unmounting);
+               if (is_suspended)
+                       vfs_resume(mp);
                if (used_syncer)
                        mutex_exit(&syncer_mutex);
                return (error);
        }
-       vfs_scrubvnlist(mp);
+
        mutex_enter(&mountlist_lock);
        if ((coveredvp = mp->mnt_vnodecovered) != NULLVP)
                coveredvp->v_mountedhere = NULL;
        CIRCLEQ_REMOVE(&mountlist, mp, mnt_list);
        mp->mnt_iflag |= IMNT_GONE;
        mutex_exit(&mountlist_lock);
-       if (TAILQ_FIRST(&mp->mnt_vnodelist) != NULL)
-               panic("unmount: dangling vnode");
+
+       mutex_enter(&mntvnode_lock);
+       TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
+               if ((vp->v_vflag & VV_DEAD) == 0)
+                       panic("unmount: dangling vnode");
+       }
+       mutex_exit(&mntvnode_lock);
+
+       if (is_suspended)
+               vfs_resume(mp);
        if (used_syncer)
                mutex_exit(&syncer_mutex);
        vfs_hooks_unmount(mp);
        rw_exit(&mp->mnt_unmounting);
Index: sys/kern/vfs_vnode.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_vnode.c,v
retrieving revision 1.17
diff -p -u -4 -r1.17 vfs_vnode.c
--- sys/kern/vfs_vnode.c        12 Nov 2012 11:00:07 -0000      1.17
+++ sys/kern/vfs_vnode.c        26 Nov 2012 13:51:09 -0000
@@ -143,8 +143,9 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/vnode.h>
 #include <sys/wapbl.h>
+#include <sys/fstrans.h>
 
 #include <uvm/uvm.h>
 #include <uvm/uvm_readahead.h>
 
@@ -164,9 +165,9 @@ static kcondvar_t   vdrain_cv               __cacheline
 
 static vnodelst_t      vrele_list              __cacheline_aligned;
 static kmutex_t                vrele_lock              __cacheline_aligned;
 static kcondvar_t      vrele_cv                __cacheline_aligned;
-static lwp_t *         vrele_lwp               __cacheline_aligned;
+lwp_t *                        vrele_lwp               __cacheline_aligned;
 static int             vrele_pending           __cacheline_aligned;
 static int             vrele_gen               __cacheline_aligned;
 
 static int             cleanvnode(void);
@@ -1062,15 +1063,26 @@ vclean(vnode_t *vp, int flags)
 
        /* Purge name cache. */
        cache_purge(vp);
 
+       if (active && (flags & DOCLOSE) == 0 &&
+           (vp->v_type == VBLK || vp->v_type == VCHR)) {
+               vp->v_op = spec_vnodeop_p;
+               vp->v_vflag &= ~VV_LOCKSWORK;
+               vp->v_vflag |= VV_DEAD;
+               vfs_insmntque(vp, NULL);
+       } else {
+               vp->v_op = dead_vnodeop_p;
+               vp->v_vflag |= VV_LOCKSWORK;
+               if (active)
+                       vp->v_vflag |= VV_DEAD;
+       }
+
        /* Done with purge, notify sleepers of the grim news. */
        mutex_enter(vp->v_interlock);
-       vp->v_op = dead_vnodeop_p;
        vp->v_tag = VT_NON;
        KNOTE(&vp->v_klist, NOTE_REVOKE);
        vp->v_iflag &= ~VI_XLOCK;
-       vp->v_vflag &= ~VV_LOCKSWORK;
        if ((flags & DOCLOSE) != 0) {
                vp->v_iflag |= VI_CLEAN;
        }
        cv_broadcast(&vp->v_cv);
@@ -1109,9 +1121,11 @@ vrecycle(vnode_t *vp, kmutex_t *inter_lk
  */
 void
 vrevoke(vnode_t *vp)
 {
+       bool is_suspended;
        vnode_t *vq, **vpp;
+       struct mount *mp;
        enum vtype type;
        dev_t dev;
 
        KASSERT(vp->v_usecount > 0);
@@ -1119,12 +1133,25 @@ vrevoke(vnode_t *vp)
        mutex_enter(vp->v_interlock);
        if ((vp->v_iflag & VI_CLEAN) != 0) {
                mutex_exit(vp->v_interlock);
                return;
-       } else if (vp->v_type != VBLK && vp->v_type != VCHR) {
+       }
+
+       mp = vp->v_mount;
+       is_suspended = false;
+       if (mp != NULL && (mp->mnt_iflag & IMNT_HAS_TRANS) != 0) {
+               mutex_exit(vp->v_interlock);
+               if (vfs_suspend(mp, 0) == 0)
+                       is_suspended = true;
+               mutex_enter(vp->v_interlock);
+       }
+
+       if (vp->v_type != VBLK && vp->v_type != VCHR) {
                atomic_inc_uint(&vp->v_usecount);
                vclean(vp, DOCLOSE);
                vrelel(vp, 0);
+               if (is_suspended)
+                       vfs_resume(mp);
                return;
        } else {
                dev = vp->v_rdev;
                type = vp->v_type;
@@ -1154,8 +1181,11 @@ vrevoke(vnode_t *vp)
                mutex_enter(&device_lock);
                vq = *vpp;
        }
        mutex_exit(&device_lock);
+
+       if (is_suspended)
+                vfs_resume(mp);
 }
 
 /*
  * Eliminate all activity associated with a vnode in preparation for
Index: sys/miscfs/deadfs/dead_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/deadfs/dead_vnops.c,v
retrieving revision 1.51
diff -p -u -4 -r1.51 dead_vnops.c
--- sys/miscfs/deadfs/dead_vnops.c      12 Jun 2011 03:35:57 -0000      1.51
+++ sys/miscfs/deadfs/dead_vnops.c      26 Nov 2012 13:51:13 -0000
@@ -51,27 +51,27 @@ __KERNEL_RCSID(0, "$NetBSD: dead_vnops.c
 int    dead_open(void *);
 #define dead_close     genfs_nullop
 int    dead_read(void *);
 int    dead_write(void *);
+int    dead_lookup(void *);
 #define dead_fcntl     genfs_nullop
 int    dead_ioctl(void *);
 int    dead_poll(void *);
 #define dead_fsync     genfs_nullop
 #define dead_seek      genfs_nullop
-#define dead_inactive  genfs_nullop
+int    dead_inactive(void *);
 #define dead_reclaim   genfs_nullop
 int    dead_lock(void *);
-#define dead_unlock    genfs_unlock
+int    dead_unlock(void *);
 int    dead_bmap(void *);
 int    dead_strategy(void *);
 int    dead_print(void *);
-#define dead_islocked  genfs_islocked
+int    dead_islocked(void *);
 #define dead_bwrite    genfs_nullop
 #define dead_revoke    genfs_nullop
 int    dead_getpages(void *);
-#define dead_putpages  genfs_null_putpages
+int    dead_putpages(void *);
 
-int    chkvnlock(struct vnode *);
 int    dead_default_error(void *);
 
 int (**dead_vnodeop_p)(void *);
 
@@ -80,8 +80,9 @@ const struct vnodeopv_entry_desc dead_vn
        { &vop_open_desc, dead_open },                  /* open */
        { &vop_close_desc, dead_close },                /* close */
        { &vop_read_desc, dead_read },                  /* read */
        { &vop_write_desc, dead_write },                /* write */
+       { &vop_lookup_desc, dead_lookup },              /* lookup */
        { &vop_fcntl_desc, dead_fcntl },                /* fcntl */
        { &vop_ioctl_desc, dead_ioctl },                /* ioctl */
        { &vop_poll_desc, dead_poll },                  /* poll */
        { &vop_revoke_desc, dead_revoke },              /* revoke */
@@ -105,8 +106,19 @@ const struct vnodeopv_desc dead_vnodeop_
 
 int
 dead_default_error(void *v)
 {
+       struct vop_generic_args /* {
+               struct vnodeop_desc *a_desc;
+               <other random data follows, presumably>
+       } */ *ap = v;
+       struct vnode **vpp;
+
+       KASSERT(ap->a_desc->vdesc_vp_offsets != NULL &&
+           ap->a_desc->vdesc_vp_offsets[0] != VDESC_NO_OFFSET);
+       vpp = VOPARG_OFFSETTO(struct vnode**,
+           ap->a_desc->vdesc_vp_offsets[0], ap);
+       KASSERT(((*vpp)->v_vflag & VV_DEAD) != 0);
 
        return EBADF;
 }
 
@@ -116,8 +128,16 @@ dead_default_error(void *v)
 /* ARGSUSED */
 int
 dead_open(void *v)
 {
+       struct vop_open_args /* {
+               const struct vnodeop_desc *a_desc;
+               struct vnode *a_vp;
+               int a_mode;
+               kauth_cred_t a_cred;
+       } */ *ap = v;
+
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
 
        return (ENXIO);
 }
 
@@ -134,10 +154,10 @@ dead_read(void *v)
                int  a_ioflag;
                kauth_cred_t a_cred;
        } */ *ap = v;
 
-       if (chkvnlock(ap->a_vp))
-               panic("dead_read: lock");
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
        /*
         * Return EOF for tty devices, EIO for others
         */
        if ((ap->a_vp->v_vflag & VV_ISTTY) == 0)
@@ -158,13 +178,29 @@ dead_write(void *v)
                int  a_ioflag;
                kauth_cred_t a_cred;
        } */ *ap = v;
 
-       if (chkvnlock(ap->a_vp))
-               panic("dead_write: lock");
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
        return (EIO);
 }
 
+int
+dead_lookup(void *v)
+{
+       struct vop_lookup_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+       } */ *ap = v;
+
+       KASSERT((ap->a_dvp->v_vflag & VV_DEAD) != 0);
+
+       *(ap->a_vpp) = NULL;
+
+       return EIO;
+}
+
 /*
  * Device ioctl operation.
  */
 /* ARGSUSED */
@@ -179,14 +215,13 @@ dead_ioctl(void *v)
                kauth_cred_t a_cred;
                struct lwp *a_l;
        } */ *ap = v;
 
-       if (!chkvnlock(ap->a_vp))
-               return (EBADF);
-       return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap));
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
+       return (EBADF);
 }
 
-/* ARGSUSED */
 int
 dead_poll(void *v)
 {
        struct vop_poll_args /* {
@@ -194,14 +229,33 @@ dead_poll(void *v)
                int a_events;
                struct lwp *a_l;
        } */ *ap = v;
 
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
        /*
         * Let the user find out that the descriptor is gone.
         */
        return (ap->a_events);
 }
 
+/* ARGSUSED */
+int
+dead_inactive(void *v)
+{
+       struct vop_inactive_args /* {
+               struct vnode *a_vp;
+               bool *a_recycle;
+       } */ *ap = v;
+
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
+       *ap->a_recycle = false;
+       VOP_UNLOCK(ap->a_vp);
+
+       return 0;
+}
+
 /*
  * Just call the device strategy routine
  */
 int
@@ -212,16 +266,16 @@ dead_strategy(void *v)
                struct vnode *a_vp;
                struct buf *a_bp;
        } */ *ap = v;
        struct buf *bp;
-       if (ap->a_vp == NULL || !chkvnlock(ap->a_vp)) {
-               bp = ap->a_bp;
-               bp->b_error = EIO;
-               bp->b_resid = bp->b_bcount;
-               biodone(ap->a_bp);
-               return (EIO);
-       }
-       return (VOP_STRATEGY(ap->a_vp, ap->a_bp));
+
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
+       bp = ap->a_bp;
+       bp->b_error = EIO;
+       bp->b_resid = bp->b_bcount;
+       biodone(ap->a_bp);
+       return (EIO);
 }
 
 /*
  * Wait until the vnode has finished changing state.
@@ -233,13 +287,66 @@ dead_lock(void *v)
                struct vnode *a_vp;
                int a_flags;
                struct proc *a_p;
        } */ *ap = v;
-
-       if (!chkvnlock(ap->a_vp)) {
-               return genfs_lock(v);
+       struct vnode *vp = ap->a_vp;
+       int flags = ap->a_flags;
+       krw_t op;
+
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+       KASSERT((flags & ~(LK_EXCLUSIVE | LK_SHARED | LK_NOWAIT)) == 0);
+
+       op = ((flags & LK_EXCLUSIVE) != 0 ? RW_WRITER : RW_READER);
+       if ((flags & LK_NOWAIT) != 0) {
+               if (! rw_tryenter(&vp->v_lock, op)) {
+                       return EBUSY;
+               }
+               return 0;
        }
-       return (VCALL(ap->a_vp, VOFFSET(vop_lock), ap));
+
+       rw_enter(&vp->v_lock, op);
+
+       return 0;
+}
+
+/*
+ * Unlock the node.
+ */
+int
+dead_unlock(void *v)
+{
+       struct vop_unlock_args /* {
+               struct vnode *a_vp;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
+       rw_exit(&vp->v_lock);
+
+       return 0;
+}
+
+/*
+ * Return whether or not the node is locked.
+ */
+int
+dead_islocked(void *v)
+{
+       struct vop_islocked_args /* {
+               struct vnode *a_vp;
+       } */ *ap = v;
+       struct vnode *vp = ap->a_vp;
+
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
+       if (rw_write_held(&vp->v_lock))
+               return LK_EXCLUSIVE;
+
+       if (rw_read_held(&vp->v_lock))
+               return LK_SHARED;
+
+       return 0;
 }
 
 /*
  * Wait until the vnode has finished changing state.
@@ -254,11 +361,11 @@ dead_bmap(void *v)
                daddr_t *a_bnp;
                int *a_runp;
        } */ *ap = v;
 
-       if (!chkvnlock(ap->a_vp))
-               return (EIO);
-       return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp));
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
+       return (EIO);
 }
 
 /*
  * Print out the contents of a dead vnode.
@@ -284,28 +391,27 @@ dead_getpages(void *v)
                int a_advice;
                int a_flags;
        } */ *ap = v;
 
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
+
        if ((ap->a_flags & PGO_LOCKED) == 0)
                mutex_exit(ap->a_vp->v_interlock);
 
        return (EFAULT);
 }
 
-/*
- * We have to wait during times when the vnode is
- * in a state of change.
- */
 int
-chkvnlock(struct vnode *vp)
+dead_putpages(void *v)
 {
-       int locked = 0;
+        struct vop_putpages_args /* {
+       struct vnode *a_vp;
+               voff_t a_offlo;
+               voff_t a_offhi;
+               int a_flags;
+       } */ *ap = v;
 
-       mutex_enter(vp->v_interlock);
-       while (vp->v_iflag & VI_XLOCK) {
-               vwait(vp, VI_XLOCK);
-               locked = 1;
-       }
-       mutex_exit(vp->v_interlock);
+       KASSERT((ap->a_vp->v_vflag & VV_DEAD) != 0);
 
-       return (locked);
+       mutex_exit(ap->a_vp->v_interlock);
+       return (EFAULT);
 }
Index: sys/dev/fss.c
===================================================================
RCS file: /cvsroot/src/sys/dev/fss.c,v
retrieving revision 1.83
diff -p -u -4 -r1.83 fss.c
--- sys/dev/fss.c       28 Jul 2012 16:14:17 -0000      1.83
+++ sys/dev/fss.c       26 Nov 2012 13:51:17 -0000
@@ -60,8 +60,9 @@ __KERNEL_RCSID(0, "$NetBSD: fss.c,v 1.83
 #include <sys/simplelock.h>
 #include <sys/vfs_syscalls.h>          /* For do_sys_unlink(). */
 
 #include <miscfs/specfs/specdev.h>
+#include <miscfs/syncfs/syncfs.h>
 
 #include <dev/fssvar.h>
 
 #include <uvm/uvm.h>
@@ -840,18 +841,22 @@ fss_create_snapshot(struct fss_softc *sc
        /*
         * Activate the snapshot.
         */
 
-       if ((error = vfs_suspend(sc->sc_mount, 0)) != 0)
+       mutex_enter(&syncer_mutex);
+       if ((error = vfs_suspend(sc->sc_mount, SUSPEND_SYNC)) != 0) {
+               mutex_exit(&syncer_mutex);
                goto bad;
+       }
 
        microtime(&sc->sc_time);
 
        error = fscow_establish(sc->sc_mount, fss_copy_on_write, sc);
        if (error == 0)
                sc->sc_flags |= FSS_ACTIVE;
 
        vfs_resume(sc->sc_mount);
+       mutex_exit(&syncer_mutex);
 
        if (error != 0)
                goto bad;
 
Index: sys/ufs/ffs/ffs_snapshot.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_snapshot.c,v
retrieving revision 1.119
diff -p -u -4 -r1.119 ffs_snapshot.c
--- sys/ufs/ffs/ffs_snapshot.c  13 Mar 2012 18:41:13 -0000      1.119
+++ sys/ufs/ffs/ffs_snapshot.c  26 Nov 2012 13:51:21 -0000
@@ -63,8 +63,9 @@ __KERNEL_RCSID(0, "$NetBSD: ffs_snapshot
 #include <sys/fstrans.h>
 #include <sys/wapbl.h>
 
 #include <miscfs/specfs/specdev.h>
+#include <miscfs/syncfs/syncfs.h>
 
 #include <ufs/ufs/quota.h>
 #include <ufs/ufs/ufsmount.h>
 #include <ufs/ufs/inode.h>
@@ -251,12 +252,15 @@ ffs_snapshot(struct mount *mp, struct vn
        VOP_UNLOCK(vp);
        /*
         * All allocations are done, so we can now suspend the filesystem.
         */
-       error = vfs_suspend(vp->v_mount, 0);
+       mutex_enter(&syncer_mutex);
+       error = vfs_suspend(vp->v_mount, SUSPEND_SYNC);
        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
-       if (error)
+       if (error) {
+               mutex_exit(&syncer_mutex);
                goto out;
+       }
        suspended = true;
        getmicrotime(&starttime);
        /*
         * First, copy all the cylinder group maps that have changed.
@@ -383,8 +387,9 @@ out:
 
        if (suspended) {
                VOP_UNLOCK(vp);
                vfs_resume(vp->v_mount);
+               mutex_exit(&syncer_mutex);
                vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
 #ifdef DEBUG
                getmicrotime(&endtime);
                timersub(&endtime, &starttime, &endtime);
@@ -2067,9 +2072,10 @@ ffs_snapshot_read(struct vnode *vp, stru
        off_t fsbytes, bytesinfile;
        long size, xfersize, blkoffset;
        int error;
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(vp->v_mount, NULL, FSTRANS_SHARED);
+       KASSERT(error == 0);
        mutex_enter(&si->si_snaplock);
 
        if (ioflag & IO_ALTSEMANTICS)
                fsbytes = ip->i_size;
Index: sys/sys/mount.h
===================================================================
RCS file: /cvsroot/src/sys/sys/mount.h,v
retrieving revision 1.208
diff -p -u -4 -r1.208 mount.h
--- sys/sys/mount.h     5 Nov 2012 17:16:18 -0000       1.208
+++ sys/sys/mount.h     26 Nov 2012 13:51:25 -0000
@@ -409,9 +409,8 @@ int vfs_detach(struct vfsops *);
 void   vfs_reinit(void);
 struct vfsops *vfs_getopsbyname(const char *);
 void   vfs_delref(struct vfsops *);
 void   vfs_destroy(struct mount *);
-void   vfs_scrubvnlist(struct mount *);
 struct mount *vfs_mountalloc(struct vfsops *, struct vnode *);
 int    vfs_stdextattrctl(struct mount *, int, struct vnode *,
            int, const char *);
 void   vfs_insmntque(struct vnode *, struct mount *);
Index: sys/miscfs/genfs/genfs_io.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/genfs/genfs_io.c,v
retrieving revision 1.55
diff -p -u -4 -r1.55 genfs_io.c
--- sys/miscfs/genfs/genfs_io.c 22 May 2012 14:20:39 -0000      1.55
+++ sys/miscfs/genfs/genfs_io.c 26 Nov 2012 13:51:28 -0000
@@ -293,9 +293,14 @@ startover:
        UVMHIST_LOG(ubchist, "ridx %d npages %d startoff %ld endoff %ld",
            ridx, npages, startoffset, endoffset);
 
        if (!has_trans_wapbl) {
-               fstrans_start(vp->v_mount, FSTRANS_SHARED);
+               error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+               if (error) {
+                       if (error == ERESTARTVOP)
+                               mutex_enter(uobj->vmobjlock);
+                       return error;
+               }
                /*
                 * XXX: This assumes that we come here only via
                 * the mmio path
                 */
@@ -862,14 +867,17 @@ retry:
         */
 
        if (!has_trans && (flags & PGO_CLEANIT) != 0) {
                mutex_exit(slock);
-               if (pagedaemon) {
-                       error = fstrans_start_nowait(vp->v_mount, FSTRANS_LAZY);
-                       if (error)
-                               return error;
-               } else
-                       fstrans_start(vp->v_mount, FSTRANS_LAZY);
+               if (pagedaemon)
+                       error = fstrans_start_nowait(vp->v_mount, vp, 
FSTRANS_LAZY);
+               else
+                       error = fstrans_start(vp->v_mount, vp, FSTRANS_LAZY);
+               if (error) {
+                       if (error == ERESTARTVOP)
+                               mutex_enter(slock);
+                       return error;
+               }
                if (need_wapbl) {
                        error = WAPBL_BEGIN(vp->v_mount);
                        if (error) {
                                fstrans_done(vp->v_mount);
Index: sys/miscfs/genfs/genfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/genfs/genfs_vnops.c,v
retrieving revision 1.189
diff -p -u -4 -r1.189 genfs_vnops.c
--- sys/miscfs/genfs/genfs_vnops.c      30 Mar 2012 18:24:08 -0000      1.189
+++ sys/miscfs/genfs/genfs_vnops.c      26 Nov 2012 13:51:32 -0000
@@ -288,24 +288,28 @@ genfs_lock(void *v)
                int a_flags;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
        int flags = ap->a_flags;
+       int error;
        krw_t op;
 
        KASSERT((flags & ~(LK_EXCLUSIVE | LK_SHARED | LK_NOWAIT)) == 0);
 
        op = ((flags & LK_EXCLUSIVE) != 0 ? RW_WRITER : RW_READER);
        if ((flags & LK_NOWAIT) != 0) {
-               if (fstrans_start_nowait(vp->v_mount, FSTRANS_SHARED))
-                       return EBUSY;
+               error = fstrans_start_nowait(vp->v_mount, vp, FSTRANS_SHARED);
+               if (error)
+                       return error;
                if (! rw_tryenter(&vp->v_lock, op)) {
                        fstrans_done(vp->v_mount);
                        return EBUSY;
                }
                return 0;
        }
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
        rw_enter(&vp->v_lock, op);
 
        return 0;
 }
Index: sys/ufs/chfs/chfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/chfs/chfs_vnops.c,v
retrieving revision 1.13
diff -p -u -4 -r1.13 chfs_vnops.c
--- sys/ufs/chfs/chfs_vnops.c   5 Nov 2012 17:27:40 -0000       1.13
+++ sys/ufs/chfs/chfs_vnops.c   26 Nov 2012 13:51:36 -0000
@@ -627,8 +627,12 @@ chfs_read(void *v)
        bool usepc = false;
 
        dbg("chfs_read\n");
 
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
        vp = ap->a_vp;
        ip = VTOI(vp);
        ump = ip->ump;
        uio = ap->a_uio;
@@ -647,14 +651,16 @@ chfs_read(void *v)
        } else if (vp->v_type != VREG && vp->v_type != VDIR)
                panic("%s: type %d", READ_S, vp->v_type);
 #endif
        chmp = ip->chmp;
-       if ((u_int64_t)uio->uio_offset > ump->um_maxfilesize)
+       if ((u_int64_t)uio->uio_offset > ump->um_maxfilesize) {
+               fstrans_done(vp->v_mount);
                return (EFBIG);
-       if (uio->uio_resid == 0)
+       }
+       if (uio->uio_resid == 0) {
+               fstrans_done(vp->v_mount);
                return (0);
-
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       }
 
        if (uio->uio_offset >= ip->size)
                goto out;
 
@@ -776,8 +782,11 @@ chfs_write(void *v)
        vsize_t bytelen;
        bool async;
        struct ufsmount *ump;
 
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
 
        cred = ap->a_cred;
        ioflag = ap->a_ioflag;
        uio = ap->a_uio;
@@ -792,10 +801,12 @@ chfs_write(void *v)
        switch (vp->v_type) {
        case VREG:
                if (ioflag & IO_APPEND)
                        uio->uio_offset = ip->size;
-               if ((ip->flags & APPEND) && uio->uio_offset != ip->size)
+               if ((ip->flags & APPEND) && uio->uio_offset != ip->size) {
+                       fstrans_done(vp->v_mount);
                        return (EPERM);
+               }
                /* FALLTHROUGH */
        case VLNK:
                break;
        case VDIR:
@@ -814,8 +825,9 @@ chfs_write(void *v)
                    "uio->uio_resid (%llu) > ump->um_maxfilesize (%lld)\n",
                    (long long)uio->uio_offset,
                    (uint64_t)uio->uio_offset + uio->uio_resid,
                    (long long)ump->um_maxfilesize);
+               fstrans_done(vp->v_mount);
                return (EFBIG);
        }
        /*
         * Maybe this should be above the vnode op call, but so long as
@@ -827,14 +839,15 @@ chfs_write(void *v)
            l->l_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
                mutex_enter(proc_lock);
                psignal(l->l_proc, SIGXFSZ);
                mutex_exit(proc_lock);
+               fstrans_done(vp->v_mount);
                return (EFBIG);
        }
-       if (uio->uio_resid == 0)
+       if (uio->uio_resid == 0) {
+               fstrans_done(vp->v_mount);
                return (0);
-
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       }
 
        flags = ioflag & IO_SYNC ? B_SYNC : 0;
        async = vp->v_mount->mnt_flag & MNT_ASYNC;
        origoff = uio->uio_offset;
Index: sys/ufs/ffs/ffs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vfsops.c,v
retrieving revision 1.279
diff -p -u -4 -r1.279 ffs_vfsops.c
--- sys/ufs/ffs/ffs_vfsops.c    19 Oct 2012 17:09:08 -0000      1.279
+++ sys/ufs/ffs/ffs_vfsops.c    26 Nov 2012 13:51:40 -0000
@@ -1644,9 +1644,9 @@ ffs_sync(struct mount *mp, int waitfor, 
 
        /* Allocate a marker vnode. */
        mvp = vnalloc(mp);
 
-       fstrans_start(mp, FSTRANS_SHARED);
+       fstrans_start(mp, NULL, FSTRANS_SHARED);
        is_suspending = (fstrans_getstate(mp) == FSTRANS_SUSPENDING);
        /*
         * Write back each (modified) inode.
         */
@@ -2094,17 +2094,18 @@ ffs_suspendctl(struct mount *mp, int cmd
 {
        int error;
        struct lwp *l = curlwp;
 
-       switch (cmd) {
+       switch (cmd & (SUSPEND_SUSPEND | SUSPEND_RESUME)) {
        case SUSPEND_SUSPEND:
                if ((error = fstrans_setstate(mp, FSTRANS_SUSPENDING)) != 0)
                        return error;
-               error = ffs_sync(mp, MNT_WAIT, l->l_proc->p_cred);
+               if ((cmd & SUSPEND_SYNC) != 0)
+                       error = ffs_sync(mp, MNT_WAIT, l->l_proc->p_cred);
                if (error == 0)
                        error = fstrans_setstate(mp, FSTRANS_SUSPENDED);
 #ifdef WAPBL
-               if (error == 0 && mp->mnt_wapbl)
+               if ((cmd & SUSPEND_SYNC) != 0 && error == 0 && mp->mnt_wapbl)
                        error = wapbl_flush(mp->mnt_wapbl, 1);
 #endif
                if (error != 0) {
                        (void) fstrans_setstate(mp, FSTRANS_NORMAL);
Index: sys/ufs/ffs/ffs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vnops.c,v
retrieving revision 1.121
diff -p -u -4 -r1.121 ffs_vnops.c
--- sys/ufs/ffs/ffs_vnops.c     29 Apr 2012 22:54:00 -0000      1.121
+++ sys/ufs/ffs/ffs_vnops.c     26 Nov 2012 13:51:43 -0000
@@ -284,9 +284,12 @@ ffs_spec_fsync(void *v)
        uflags = UPDATE_CLOSE | ((flags & FSYNC_WAIT) ? UPDATE_WAIT : 0);
        vp = ap->a_vp;
        mp = vp->v_mount;
 
-       fstrans_start(mp, FSTRANS_LAZY);
+       error = fstrans_start(mp, vp, FSTRANS_LAZY);
+       if (error)
+               return error;
+       KASSERT(mp == vp->v_mount);
 
        error = spec_fsync(v);
        if (error)
                goto out;
@@ -341,9 +344,12 @@ ffs_fsync(void *v)
 
        vp = ap->a_vp;
        mp = vp->v_mount;
 
-       fstrans_start(mp, FSTRANS_LAZY);
+       error = fstrans_start(mp, vp, FSTRANS_LAZY);
+       if (error)
+               return error;
+       KASSERT(mp == vp->v_mount);
        if ((ap->a_offlo == 0 && ap->a_offhi == 0) || (vp->v_type != VREG)) {
                error = ffs_full_fsync(vp, ap->a_flags);
                goto out;
        }
@@ -549,15 +555,20 @@ ffs_reclaim(void *v)
                struct vnode *a_vp;
                struct lwp *a_l;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
+       struct inode *ip;
        struct mount *mp = vp->v_mount;
-       struct ufsmount *ump = ip->i_ump;
+       struct ufsmount *ump;
        void *data;
        int error;
 
-       fstrans_start(mp, FSTRANS_LAZY);
+       error = fstrans_start(mp, vp, FSTRANS_LAZY);
+       if (error)
+               return error;
+       KASSERT(mp == vp->v_mount);
+       ip = VTOI(vp);
+       ump = ip->i_ump;
        /*
         * The inode must be freed and updated before being removed
         * from its hash chain.  Other threads trying to gain a hold
         * on the inode will be stalled because it is locked (VI_XLOCK).
@@ -671,25 +682,31 @@ ffs_getextattr(void *v)
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
-       struct fs *fs = ip->i_fs;
+       struct inode *ip;
+       struct fs *fs;
+       int error;
+
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       ip = VTOI(vp);
+       fs = ip->i_fs;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               int error;
-
-               fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_getextattr(ap);
                fstrans_done(vp->v_mount);
                return error;
 #else
+               fstrans_done(vp->v_mount);
                return (EOPNOTSUPP);
 #endif
        }
 
        /* XXX Not implemented for UFS2 file systems. */
+       fstrans_done(vp->v_mount);
        return (EOPNOTSUPP);
 }
 
 int
@@ -703,25 +720,31 @@ ffs_setextattr(void *v)
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
-       struct fs *fs = ip->i_fs;
+       struct inode *ip;
+       struct fs *fs;
+       int error;
+
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       ip = VTOI(vp);
+       fs = ip->i_fs;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               int error;
-
-               fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_setextattr(ap);
                fstrans_done(vp->v_mount);
                return error;
 #else
+               fstrans_done(vp->v_mount);
                return (EOPNOTSUPP);
 #endif
        }
 
        /* XXX Not implemented for UFS2 file systems. */
+       fstrans_done(vp->v_mount);
        return (EOPNOTSUPP);
 }
 
 int
@@ -734,26 +757,32 @@ ffs_listextattr(void *v)
                size_t *a_size;
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
-       struct inode *ip = VTOI(ap->a_vp);
-       struct fs *fs = ip->i_fs;
+       struct vnode *vp = ap->a_vp;
+       struct inode *ip;
+       struct fs *fs;
+       int error;
+
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       ip = VTOI(vp);
+       fs = ip->i_fs;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               struct vnode *vp = ap->a_vp;
-               int error;
-
-               fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_listextattr(ap);
                fstrans_done(vp->v_mount);
                return error;
 #else
+               fstrans_done(vp->v_mount);
                return (EOPNOTSUPP);
 #endif
        }
 
        /* XXX Not implemented for UFS2 file systems. */
+       fstrans_done(vp->v_mount);
        return (EOPNOTSUPP);
 }
 
 int
@@ -765,23 +794,29 @@ ffs_deleteextattr(void *v)
                kauth_cred_t a_cred;
                struct proc *a_p;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
-       struct inode *ip = VTOI(vp);
-       struct fs *fs = ip->i_fs;
+       struct inode *ip;
+       struct fs *fs;
+       int error;
+
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       ip = VTOI(vp);
+       fs = ip->i_fs;
 
        if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-               int error;
-
-               fstrans_start(vp->v_mount, FSTRANS_SHARED);
                error = ufs_deleteextattr(ap);
                fstrans_done(vp->v_mount);
                return error;
 #else
+               fstrans_done(vp->v_mount);
                return (EOPNOTSUPP);
 #endif
        }
 
        /* XXX Not implemented for UFS2 file systems. */
+       fstrans_done(vp->v_mount);
        return (EOPNOTSUPP);
 }
Index: sys/ufs/ufs/ufs_bmap.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_bmap.c,v
retrieving revision 1.49
diff -p -u -4 -r1.49 ufs_bmap.c
--- sys/ufs/ufs/ufs_bmap.c      6 Mar 2011 17:08:39 -0000       1.49
+++ sys/ufs/ufs/ufs_bmap.c      26 Nov 2012 13:51:47 -0000
@@ -83,8 +83,11 @@ ufs_bmap(void *v)
                int *a_runp;
        } */ *ap = v;
        int error;
 
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
        /*
         * Check for underlying vnode requests and ensure that logical
         * to physical mapping is requested.
         */
@@ -92,9 +95,8 @@ ufs_bmap(void *v)
                *ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
        if (ap->a_bnp == NULL)
                return (0);
 
-       fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
        error = ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
            ap->a_runp, ufs_issequential);
        fstrans_done(ap->a_vp->v_mount);
        return error;
Index: sys/ufs/ufs/ufs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_inode.c,v
retrieving revision 1.88
diff -p -u -4 -r1.88 ufs_inode.c
--- sys/ufs/ufs/ufs_inode.c     20 Sep 2011 14:01:33 -0000      1.88
+++ sys/ufs/ufs/ufs_inode.c     26 Nov 2012 13:51:50 -0000
@@ -91,9 +91,10 @@ ufs_inactive(void *v)
 
        UFS_WAPBL_JUNLOCK_ASSERT(vp->v_mount);
 
        transmp = vp->v_mount;
-       fstrans_start(transmp, FSTRANS_LAZY);
+       error = fstrans_start(transmp, NULL, FSTRANS_LAZY);
+       KASSERT(error == 0);
        /*
         * Ignore inodes related to stale file handles.
         */
        if (ip->i_mode == 0)
Index: sys/ufs/ufs/ufs_lookup.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_lookup.c,v
retrieving revision 1.120
diff -p -u -4 -r1.120 ufs_lookup.c
--- sys/ufs/ufs/ufs_lookup.c    5 Nov 2012 17:27:40 -0000       1.120
+++ sys/ufs/ufs/ufs_lookup.c    26 Nov 2012 13:51:54 -0000
@@ -117,9 +117,9 @@ ufs_lookup(void *v)
                struct vnode **a_vpp;
                struct componentname *a_cnp;
        } */ *ap = v;
        struct vnode *vdp = ap->a_dvp;  /* vnode for directory being searched */
-       struct inode *dp = VTOI(vdp);   /* inode for directory being searched */
+       struct inode *dp;               /* inode for directory being searched */
        struct buf *bp;                 /* a buffer of directory entries */
        struct direct *ep;              /* the current directory entry */
        int entryoffsetinblock;         /* offset of ep in bp's buffer */
        enum {
@@ -147,15 +147,24 @@ ufs_lookup(void *v)
        struct componentname *cnp = ap->a_cnp;
        kauth_cred_t cred = cnp->cn_cred;
        int flags;
        int nameiop = cnp->cn_nameiop;
-       struct ufsmount *ump = dp->i_ump;
-       const int needswap = UFS_MPNEEDSWAP(ump);
-       int dirblksiz = ump->um_dirblksiz;
+       struct ufsmount *ump;
+       int needswap;
+       int dirblksiz;
        ino_t foundino;
        struct ufs_lookup_results *results;
        int iswhiteout;                 /* temp result from cache_lookup() */
 
+       error = fstrans_start(vdp->v_mount, vdp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
+       dp = VTOI(vdp);
+       ump = dp->i_ump;
+       needswap = UFS_MPNEEDSWAP(ump);
+       dirblksiz = ump->um_dirblksiz;
+
        flags = cnp->cn_flags;
 
        bp = NULL;
        slotoffset = -1;
@@ -172,14 +181,18 @@ ufs_lookup(void *v)
 
        /*
         * Check accessiblity of directory.
         */
-       if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
+       if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) {
+               fstrans_done(vdp->v_mount);
                return (error);
+       }
 
        if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
-           (nameiop == DELETE || nameiop == RENAME))
+           (nameiop == DELETE || nameiop == RENAME)) {
+               fstrans_done(vdp->v_mount);
                return (EROFS);
+       }
 
        /*
         * We now have a segment name to search for, and a directory to search.
         *
@@ -191,8 +204,9 @@ ufs_lookup(void *v)
                         cnp->cn_nameiop, cnp->cn_flags, &iswhiteout, vpp)) {
                if (iswhiteout) {
                        cnp->cn_flags |= ISWHITEOUT;
                }
+               fstrans_done(vdp->v_mount);
                return *vpp == NULLVP ? ENOENT : 0;
        }
        if (iswhiteout) {
                /*
@@ -207,10 +221,8 @@ ufs_lookup(void *v)
                 */
                cnp->cn_flags |= ISWHITEOUT;
        }
 
-       fstrans_start(vdp->v_mount, FSTRANS_SHARED);
-
        /*
         * Suppress search for slots unless creating
         * file and at end of pathname, in which case
         * we watch for a place to put the new file in
Index: sys/ufs/ufs/ufs_readwrite.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_readwrite.c,v
retrieving revision 1.104
diff -p -u -4 -r1.104 ufs_readwrite.c
--- sys/ufs/ufs/ufs_readwrite.c 29 Apr 2012 22:54:01 -0000      1.104
+++ sys/ufs/ufs/ufs_readwrite.c 26 Nov 2012 13:51:58 -0000
@@ -80,8 +80,12 @@ READ(void *v)
        long size, xfersize, blkoffset;
        int error, ioflag;
        bool usepc = false;
 
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
        vp = ap->a_vp;
        ip = VTOI(vp);
        ump = ip->i_ump;
        uio = ap->a_uio;
@@ -99,20 +103,25 @@ READ(void *v)
        } else if (vp->v_type != VREG && vp->v_type != VDIR)
                panic("%s: type %d", READ_S, vp->v_type);
 #endif
        fs = ip->I_FS;
-       if ((u_int64_t)uio->uio_offset > ump->um_maxfilesize)
+       if ((u_int64_t)uio->uio_offset > ump->um_maxfilesize) {
+               fstrans_done(vp->v_mount);
                return (EFBIG);
-       if (uio->uio_resid == 0)
+       }
+       if (uio->uio_resid == 0) {
+               fstrans_done(vp->v_mount);
                return (0);
+       }
 
 #ifndef LFS_READWRITE
-       if ((ip->i_flags & (SF_SNAPSHOT | SF_SNAPINVAL)) == SF_SNAPSHOT)
-               return ffs_snapshot_read(vp, uio, ioflag);
+       if ((ip->i_flags & (SF_SNAPSHOT | SF_SNAPINVAL)) == SF_SNAPSHOT) {
+               error = ffs_snapshot_read(vp, uio, ioflag);
+               fstrans_done(vp->v_mount);
+               return error;
+       }
 #endif /* !LFS_READWRITE */
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
-
        if (uio->uio_offset >= ip->i_size)
                goto out;
 
 #ifdef LFS_READWRITE
@@ -229,8 +238,12 @@ WRITE(void *v)
        bool need_unreserve = false;
 #endif
        struct ufsmount *ump;
 
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
        cred = ap->a_cred;
        ioflag = ap->a_ioflag;
        uio = ap->a_uio;
        vp = ap->a_vp;
@@ -246,10 +259,12 @@ WRITE(void *v)
        switch (vp->v_type) {
        case VREG:
                if (ioflag & IO_APPEND)
                        uio->uio_offset = ip->i_size;
-               if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size)
+               if ((ip->i_flags & APPEND) && uio->uio_offset != ip->i_size) {
+                       fstrans_done(vp->v_mount);
                        return (EPERM);
+               }
                /* FALLTHROUGH */
        case VLNK:
                break;
        case VDIR:
@@ -261,20 +276,24 @@ WRITE(void *v)
        }
 
        fs = ip->I_FS;
        if (uio->uio_offset < 0 ||
-           (u_int64_t)uio->uio_offset + uio->uio_resid > ump->um_maxfilesize)
+           (u_int64_t)uio->uio_offset + uio->uio_resid > ump->um_maxfilesize) {
+               fstrans_done(vp->v_mount);
                return (EFBIG);
+       }
 #ifdef LFS_READWRITE
        /* Disallow writes to the Ifile, even if noschg flag is removed */
        /* XXX can this go away when the Ifile is no longer in the namespace? */
-       if (vp == fs->lfs_ivnode)
+       if (vp == fs->lfs_ivnode) {
+               fstrans_done(vp->v_mount);
                return (EPERM);
+       }
 #endif
-       if (uio->uio_resid == 0)
+       if (uio->uio_resid == 0) {
+               fstrans_done(vp->v_mount);
                return (0);
-
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       }
 
        flags = ioflag & IO_SYNC ? B_SYNC : 0;
        async = vp->v_mount->mnt_flag & MNT_ASYNC;
        origoff = uio->uio_offset;
Index: sys/ufs/ufs/ufs_rename.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_rename.c,v
retrieving revision 1.5
diff -p -u -4 -r1.5 ufs_rename.c
--- sys/ufs/ufs/ufs_rename.c    4 Jun 2012 20:13:47 -0000       1.5
+++ sys/ufs/ufs/ufs_rename.c    26 Nov 2012 13:52:01 -0000
@@ -109,10 +109,26 @@ ufs_sane_rename(
  */
 int
 ufs_rename(void *v)
 {
+       struct vop_rename_args  /* {
+               struct vnode *a_fdvp;
+               struct vnode *a_fvp;
+               struct componentname *a_fcnp;
+               struct vnode *a_tdvp;
+               struct vnode *a_tvp;
+               struct componentname *a_tcnp;
+       } */ *ap = v;
+       struct mount *mp = ap->a_fdvp->v_mount;
+       int error;
 
-       return genfs_insane_rename(v, &ufs_sane_rename);
+       error = fstrans_start(mp, ap->a_fdvp, FSTRANS_SHARED);
+       if (error != 0)
+               return error;
+       error = genfs_insane_rename(v, &ufs_sane_rename);
+       fstrans_done(mp);
+
+       return error;
 }
 
 /*
  * ufs_gro_directory_empty_p: Return true if the directory vp is
@@ -333,9 +349,10 @@ ufs_gro_rename(struct mount *mp, kauth_c
        /*
         * Commence hacking of the data on disk.
         */
 
-       fstrans_start(mp, FSTRANS_SHARED);
+       error = fstrans_start(mp, NULL, FSTRANS_SHARED);
+       KASSERT(error == 0);
        error = UFS_WAPBL_BEGIN(mp);
        if (error)
                goto ihateyou;
 
@@ -785,9 +802,10 @@ ufs_gro_remove(struct mount *mp, kauth_c
        KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
        KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
        KASSERT(cnp->cn_nameiop == DELETE);
 
-       fstrans_start(mp, FSTRANS_SHARED);
+       error = fstrans_start(mp, NULL, FSTRANS_SHARED);
+       KASSERT(error == 0);
        error = UFS_WAPBL_BEGIN(mp);
        if (error)
                goto out0;
 
Index: sys/ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.210
diff -p -u -4 -r1.210 ufs_vnops.c
--- sys/ufs/ufs/ufs_vnops.c     4 Jun 2012 20:13:47 -0000       1.210
+++ sys/ufs/ufs/ufs_vnops.c     26 Nov 2012 13:52:05 -0000
@@ -143,17 +143,19 @@ ufs_create(void *v)
        int     error;
        struct vnode *dvp = ap->a_dvp;
        struct ufs_lookup_results *ulr;
 
+       error = fstrans_start(dvp->v_mount, dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
        /* XXX should handle this material another way */
        ulr = &VTOI(dvp)->i_crap;
        UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
 
        /*
         * UFS_WAPBL_BEGIN1(dvp->v_mount, dvp) performed by successful
         * ufs_makeinode
         */
-       fstrans_start(dvp->v_mount, FSTRANS_SHARED);
        error =
            ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
                          dvp, ulr, ap->a_vpp, ap->a_cnp);
        if (error) {
@@ -186,8 +188,12 @@ ufs_mknod(void *v)
        struct mount    *mp;
        ino_t           ino;
        struct ufs_lookup_results *ulr;
 
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
        vap = ap->a_vap;
        vpp = ap->a_vpp;
 
        /* XXX should handle this material another way */
@@ -197,9 +203,8 @@ ufs_mknod(void *v)
        /*
         * UFS_WAPBL_BEGIN1(dvp->v_mount, dvp) performed by successful
         * ufs_makeinode
         */
-       fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
        if ((error =
            ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
            ap->a_dvp, ulr, vpp, ap->a_cnp)) != 0)
                goto out;
@@ -280,12 +285,15 @@ ufs_close(void *v)
                kauth_cred_t    a_cred;
        } */ *ap = v;
        struct vnode    *vp;
        struct inode    *ip;
+       int error;
 
        vp = ap->a_vp;
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
        ip = VTOI(vp);
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
        if (vp->v_usecount > 1)
                UFS_ITIMES(vp, NULL, NULL, NULL);
        fstrans_done(vp->v_mount);
        return (0);
@@ -311,9 +319,10 @@ ufs_check_possible(struct vnode *vp, str
                case VREG:
                        if (vp->v_mount->mnt_flag & MNT_RDONLY)
                                return (EROFS);
 #if defined(QUOTA) || defined(QUOTA2)
-                       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+                       error = fstrans_start(vp->v_mount, NULL, 
FSTRANS_SHARED);
+                       KASSERT(error == 0);
                        error = chkdq(ip, 0, cred, 0);
                        fstrans_done(vp->v_mount);
                        if (error != 0)
                                return error;
@@ -387,13 +396,16 @@ ufs_getattr(void *v)
        } */ *ap = v;
        struct vnode    *vp;
        struct inode    *ip;
        struct vattr    *vap;
+       int error;
 
        vp = ap->a_vp;
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
        ip = VTOI(vp);
        vap = ap->a_vap;
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
        UFS_ITIMES(vp, NULL, NULL, NULL);
 
        /*
         * Copy from inode table
@@ -483,9 +495,11 @@ ufs_setattr(void *v)
            ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
                return (EINVAL);
        }
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
 
        if (vap->va_flags != VNOVAL) {
                if (vp->v_mount->mnt_flag & MNT_RDONLY) {
                        error = EROFS;
@@ -691,16 +705,20 @@ ufs_chmod(struct vnode *vp, int mode, ka
        int             error;
 
        UFS_WAPBL_JLOCK_ASSERT(vp->v_mount);
 
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
        ip = VTOI(vp);
 
        error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
            NULL, genfs_can_chmod(vp->v_type, cred, ip->i_uid, ip->i_gid, 
mode));
-       if (error)
+       if (error) {
+               fstrans_done(vp->v_mount);
                return (error);
+       }
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
        ip->i_mode &= ~ALLPERMS;
        ip->i_mode |= (mode & ALLPERMS);
        ip->i_flag |= IN_CHANGE;
        DIP_ASSIGN(ip, mode, ip->i_mode);
@@ -723,8 +741,12 @@ ufs_chown(struct vnode *vp, uid_t uid, g
        uid_t           ouid;
        gid_t           ogid;
        int64_t         change;
 #endif
+
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
        ip = VTOI(vp);
        error = 0;
 
        if (uid == (uid_t)VNOVAL)
@@ -733,12 +755,12 @@ ufs_chown(struct vnode *vp, uid_t uid, g
                gid = ip->i_gid;
 
        error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, vp,
            NULL, genfs_can_chown(cred, ip->i_uid, ip->i_gid, uid, gid));
-       if (error)
+       if (error) {
+               fstrans_done(vp->v_mount);
                return (error);
-
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       }
 #if defined(QUOTA) || defined(QUOTA2)
        ogid = ip->i_gid;
        ouid = ip->i_uid;
        change = DIP(ip, blocks);
@@ -786,15 +808,17 @@ ufs_remove(void *v)
        struct ufs_lookup_results *ulr;
 
        vp = ap->a_vp;
        dvp = ap->a_dvp;
+       error = fstrans_start(dvp->v_mount, dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
        ip = VTOI(vp);
 
        /* XXX should handle this material another way */
        ulr = &VTOI(dvp)->i_crap;
        UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
 
-       fstrans_start(dvp->v_mount, FSTRANS_SHARED);
        if (vp->v_type == VDIR || (ip->i_flags & (IMMUTABLE | APPEND)) ||
            (VTOI(dvp)->i_flags & APPEND))
                error = EPERM;
        else {
@@ -834,17 +858,20 @@ ufs_link(void *v)
        struct direct *newdir;
        int error;
        struct ufs_lookup_results *ulr;
 
+       error = fstrans_start(dvp->v_mount, dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
        KASSERT(dvp != vp);
        KASSERT(vp->v_type != VDIR);
        KASSERT(dvp->v_mount == vp->v_mount);
 
        /* XXX should handle this material another way */
        ulr = &VTOI(dvp)->i_crap;
        UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
 
-       fstrans_start(dvp->v_mount, FSTRANS_SHARED);
        error = vn_lock(vp, LK_EXCLUSIVE);
        if (error) {
                VOP_ABORTOP(dvp, cnp);
                goto out2;
@@ -906,11 +933,16 @@ ufs_whiteout(void *v)
        struct vnode            *dvp = ap->a_dvp;
        struct componentname    *cnp = ap->a_cnp;
        struct direct           *newdir;
        int                     error;
-       struct ufsmount         *ump = VFSTOUFS(dvp->v_mount);
+       struct ufsmount         *ump;
        struct ufs_lookup_results *ulr;
 
+       error = fstrans_start(dvp->v_mount, dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       ump = VFSTOUFS(dvp->v_mount);
+
        /* XXX should handle this material another way */
        ulr = &VTOI(dvp)->i_crap;
        UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
 
@@ -918,14 +950,15 @@ ufs_whiteout(void *v)
        switch (ap->a_flags) {
        case LOOKUP:
                /* 4.4 format directories support whiteout operations */
                if (ump->um_maxsymlinklen > 0)
-                       return (0);
-               return (EOPNOTSUPP);
+                       error = 0;
+               else
+                       error = EOPNOTSUPP;
+               break;
 
        case CREATE:
                /* create a new directory whiteout */
-               fstrans_start(dvp->v_mount, FSTRANS_SHARED);
                error = UFS_WAPBL_BEGIN(dvp->v_mount);
                if (error)
                        break;
 #ifdef DIAGNOSTIC
@@ -945,9 +978,8 @@ ufs_whiteout(void *v)
                break;
 
        case DELETE:
                /* remove an existing directory whiteout */
-               fstrans_start(dvp->v_mount, FSTRANS_SHARED);
                error = UFS_WAPBL_BEGIN(dvp->v_mount);
                if (error)
                        break;
 #ifdef DIAGNOSTIC
@@ -978,18 +1010,23 @@ ufs_mkdir(void *v)
        } */ *ap = v;
        struct vnode            *dvp = ap->a_dvp, *tvp;
        struct vattr            *vap = ap->a_vap;
        struct componentname    *cnp = ap->a_cnp;
-       struct inode            *ip, *dp = VTOI(dvp);
+       struct inode            *ip, *dp;
        struct buf              *bp;
        struct dirtemplate      dirtemplate;
        struct direct           *newdir;
        int                     error, dmode;
-       struct ufsmount         *ump = dp->i_ump;
-       int                     dirblksiz = ump->um_dirblksiz;
+       struct ufsmount         *ump;
+       int                     dirblksiz;
        struct ufs_lookup_results *ulr;
 
-       fstrans_start(dvp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(dvp->v_mount, dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dp = VTOI(dvp);
+       ump = dp->i_ump;
+       dirblksiz = ump->um_dirblksiz;
 
        /* XXX should handle this material another way */
        ulr = &dp->i_crap;
        UFS_CHECK_CRAPCOUNTER(dp);
@@ -1141,8 +1178,12 @@ ufs_rmdir(void *v)
        struct inode            *ip, *dp;
        int                     error;
        struct ufs_lookup_results *ulr;
 
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+
        vp = ap->a_vp;
        dvp = ap->a_dvp;
        cnp = ap->a_cnp;
        ip = VTOI(vp);
@@ -1163,10 +1204,8 @@ ufs_rmdir(void *v)
                vput(vp);
                return (EINVAL);
        }
 
-       fstrans_start(dvp->v_mount, FSTRANS_SHARED);
-
        /*
         * Do not remove a directory that is in the process of being renamed.
         * Verify that the directory is empty (and valid). (Rmdir ".." won't
         * be valid since ".." will contain a reference to the current
@@ -1246,8 +1285,11 @@ ufs_symlink(void *v)
        struct inode    *ip;
        int             len, error;
        struct ufs_lookup_results *ulr;
 
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
        vpp = ap->a_vpp;
 
        /* XXX should handle this material another way */
        ulr = &VTOI(ap->a_dvp)->i_crap;
@@ -1256,9 +1298,8 @@ ufs_symlink(void *v)
        /*
         * UFS_WAPBL_BEGIN1(dvp->v_mount, dvp) performed by successful
         * ufs_makeinode
         */
-       fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
        error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, ulr,
                              vpp, ap->a_cnp);
        if (error)
                goto out;
@@ -1567,14 +1608,22 @@ ufsspec_read(void *v)
                struct uio      *a_uio;
                int             a_ioflag;
                kauth_cred_t    a_cred;
        } */ *ap = v;
+       struct vnode *vp;
+       int error;
 
        /*
         * Set access flag.
         */
-       if ((ap->a_vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0)
-               VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
+       vp = ap->a_vp;
+       if ((vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0) {
+               error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+               if (error)
+                       return error;
+               VTOI(vp)->i_flag |= IN_ACCESS;
+               fstrans_done(vp->v_mount);
+       }
        return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
 }
 
 /*
@@ -1588,14 +1637,22 @@ ufsspec_write(void *v)
                struct uio      *a_uio;
                int             a_ioflag;
                kauth_cred_t    a_cred;
        } */ *ap = v;
+       struct vnode *vp;
+       int error;
 
        /*
         * Set update and change flags.
         */
-       if ((ap->a_vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0)
-               VTOI(ap->a_vp)->i_flag |= IN_MODIFY;
+       vp = ap->a_vp;
+       if ((vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0) {
+               error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+               if (error)
+                       return error;
+               VTOI(vp)->i_flag |= IN_MODIFY;
+               fstrans_done(vp->v_mount);
+       }
        return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
 }
 
 /*
@@ -1612,13 +1669,19 @@ ufsspec_close(void *v)
                kauth_cred_t    a_cred;
        } */ *ap = v;
        struct vnode    *vp;
        struct inode    *ip;
+       int error;
 
        vp = ap->a_vp;
-       ip = VTOI(vp);
-       if (vp->v_usecount > 1)
+       if (vp->v_usecount > 1) {
+               error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+               if (error)
+                       return error;
+               ip = VTOI(vp);
                UFS_ITIMES(vp, NULL, NULL, NULL);
+               fstrans_done(vp->v_mount);
+       }
        return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
 }
 
 /*
@@ -1632,13 +1695,20 @@ ufsfifo_read(void *v)
                struct uio      *a_uio;
                int             a_ioflag;
                kauth_cred_t    a_cred;
        } */ *ap = v;
+       struct vnode *vp;
+       int error;
 
        /*
         * Set access flag.
         */
-       VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
+       vp = ap->a_vp;
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       VTOI(vp)->i_flag |= IN_ACCESS;
+       fstrans_done(vp->v_mount);
        return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
 }
 
 /*
@@ -1652,13 +1722,20 @@ ufsfifo_write(void *v)
                struct uio      *a_uio;
                int             a_ioflag;
                kauth_cred_t    a_cred;
        } */ *ap = v;
+       struct vnode *vp;
+       int error;
 
        /*
         * Set update and change flags.
         */
-       VTOI(ap->a_vp)->i_flag |= IN_MODIFY;
+       vp = ap->a_vp;
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       VTOI(vp)->i_flag |= IN_MODIFY;
+       fstrans_done(vp->v_mount);
        return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
 }
 
 /*
@@ -1673,15 +1750,19 @@ ufsfifo_close(void *v)
                struct vnode    *a_vp;
                int             a_fflag;
                kauth_cred_t    a_cred;
        } */ *ap = v;
-       struct vnode    *vp;
-       struct inode    *ip;
+       struct vnode *vp;
+       int error;
 
        vp = ap->a_vp;
-       ip = VTOI(vp);
-       if (ap->a_vp->v_usecount > 1)
+       if (vp->v_usecount > 1) {
+               error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+               if (error)
+                       return error;
                UFS_ITIMES(vp, NULL, NULL, NULL);
+               fstrans_done(vp->v_mount);
+       }
        return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
 }
 
 /*
Index: sys/fs/msdosfs/msdosfs_denode.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_denode.c,v
retrieving revision 1.47
diff -p -u -4 -r1.47 msdosfs_denode.c
--- sys/fs/msdosfs/msdosfs_denode.c     4 Nov 2012 17:57:59 -0000       1.47
+++ sys/fs/msdosfs/msdosfs_denode.c     26 Nov 2012 13:52:09 -0000
@@ -707,18 +707,23 @@ msdosfs_inactive(void *v)
        struct vop_inactive_args /* {
                struct vnode *a_vp;
                bool *a_recycle;
        } */ *ap = v;
-       struct vnode *vp = ap->a_vp;
-       struct mount *mp = vp->v_mount;
-       struct denode *dep = VTODE(vp);
+       struct vnode *vp;
+       struct mount *mp;
+       struct denode *dep;
        int error = 0;
 
 #ifdef MSDOSFS_DEBUG
        printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, 
dep->de_Name[0]);
 #endif
 
-       fstrans_start(mp, FSTRANS_LAZY);
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_LAZY);
+       if (error)
+               return error;
+       vp = ap->a_vp;
+       mp = vp->v_mount;
+       dep = VTODE(vp);
        /*
         * Get rid of denodes related to stale file handles.
         */
        if (dep->de_Name[0] == SLOT_DELETED)
Index: sys/fs/msdosfs/msdosfs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_vfsops.c,v
retrieving revision 1.100
diff -p -u -4 -r1.100 msdosfs_vfsops.c
--- sys/fs/msdosfs/msdosfs_vfsops.c     4 Nov 2012 17:57:59 -0000       1.100
+++ sys/fs/msdosfs/msdosfs_vfsops.c     26 Nov 2012 13:52:13 -0000
@@ -958,9 +958,10 @@ msdosfs_sync(struct mount *mp, int waitf
                }
        }
        /* Allocate a marker vnode. */
        mvp = vnalloc(mp);
-       fstrans_start(mp, FSTRANS_SHARED);
+       error = fstrans_start(mp, NULL, FSTRANS_SHARED);
+       KASSERT(error == 0);
        /*
         * Write back each (modified) denode.
         */
        mutex_enter(&mntvnode_lock);
@@ -1079,13 +1080,14 @@ msdosfs_suspendctl(struct mount *mp, int
 {
        int error;
        struct lwp *l = curlwp;
 
-       switch (cmd) {
+       switch (cmd & (SUSPEND_SUSPEND | SUSPEND_RESUME)) {
        case SUSPEND_SUSPEND:
                if ((error = fstrans_setstate(mp, FSTRANS_SUSPENDING)) != 0)
                        return error;
-               error = msdosfs_sync(mp, MNT_WAIT, l->l_proc->p_cred);
+               if ((cmd & SUSPEND_SYNC) != 0)
+                       error = msdosfs_sync(mp, MNT_WAIT, l->l_proc->p_cred);
                if (error == 0)
                        error = fstrans_setstate(mp, FSTRANS_SUSPENDED);
                if (error != 0) {
                        (void) fstrans_setstate(mp, FSTRANS_NORMAL);
Index: sys/fs/msdosfs/msdosfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/msdosfs/msdosfs_vnops.c,v
retrieving revision 1.83
diff -p -u -4 -r1.83 msdosfs_vnops.c
--- sys/fs/msdosfs/msdosfs_vnops.c      29 Apr 2012 22:53:59 -0000      1.83
+++ sys/fs/msdosfs/msdosfs_vnops.c      26 Nov 2012 13:52:16 -0000
@@ -112,16 +112,19 @@ msdosfs_create(void *v)
        } */ *ap = v;
        struct componentname *cnp = ap->a_cnp;
        struct denode ndirent;
        struct denode *dep;
-       struct denode *pdep = VTODE(ap->a_dvp);
+       struct denode *pdep;
        int error;
 
 #ifdef MSDOSFS_DEBUG
        printf("msdosfs_create(cnp %p, vap %p\n", cnp, ap->a_vap);
 #endif
 
-       fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       pdep = VTODE(ap->a_dvp);
        /*
         * If this is the root directory and there is no space left we
         * can't do anything.  This is because the root directory can not
         * change size.
@@ -173,11 +176,15 @@ msdosfs_close(void *v)
                int a_fflag;
                kauth_cred_t a_cred;
        } */ *ap = v;
        struct vnode *vp = ap->a_vp;
-       struct denode *dep = VTODE(vp);
+       struct denode *dep;
+       int error;
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(vp);
        mutex_enter(vp->v_interlock);
        if (vp->v_usecount > 1)
                DETIMES(dep, NULL, NULL, NULL, dep->de_pmp->pm_gmtoff);
        mutex_exit(vp->v_interlock);
@@ -256,16 +263,24 @@ msdosfs_getattr(void *v)
                struct vnode *a_vp;
                struct vattr *a_vap;
                kauth_cred_t a_cred;
        } */ *ap = v;
-       struct denode *dep = VTODE(ap->a_vp);
-       struct msdosfsmount *pmp = dep->de_pmp;
-       struct vattr *vap = ap->a_vap;
+       struct denode *dep;
+       struct msdosfsmount *pmp;
+       struct vattr *vap;
        mode_t mode;
-       u_long dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
+       u_long dirsperblk;
        ino_t fileid;
+       int error;
+
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(ap->a_vp);
+       pmp = dep->de_pmp;
+       vap = ap->a_vap;
+       dirsperblk = pmp->pm_BytesPerSec / sizeof(struct direntry);
 
-       fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
        DETIMES(dep, NULL, NULL, NULL, pmp->pm_gmtoff);
        vap->va_fsid = dep->de_dev;
        /*
         * The following computation of the fileid must be the same as that
@@ -327,18 +342,23 @@ msdosfs_setattr(void *v)
                struct vattr *a_vap;
                kauth_cred_t a_cred;
        } */ *ap = v;
        int error = 0, de_changed = 0;
-       struct denode *dep = VTODE(ap->a_vp);
-       struct msdosfsmount *pmp = dep->de_pmp;
+       struct denode *dep;
+       struct msdosfsmount *pmp;
        struct vnode *vp  = ap->a_vp;
        struct vattr *vap = ap->a_vap;
        kauth_cred_t cred = ap->a_cred;
 
 #ifdef MSDOSFS_DEBUG
        printf("msdosfs_setattr(): vp %p, vap %p, cred %p\n",
            ap->a_vp, vap, cred);
 #endif
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(ap->a_vp);
+       pmp = dep->de_pmp;
        /*
         * Note we silently ignore uid or gid changes.
         */
        if ((vap->va_type != VNON) || (vap->va_nlink != (nlink_t)VNOVAL) ||
@@ -354,17 +374,19 @@ msdosfs_setattr(void *v)
                    (unsigned long long)vap->va_fileid);
                printf("    va_blocksize %lx, va_rdev %"PRIx64", va_bytes 
%"PRIx64", va_gen %lx\n",
                    vap->va_blocksize, vap->va_rdev, vap->va_bytes, 
vap->va_gen);
 #endif
+               fstrans_done(vp->v_mount);
                return (EINVAL);
        }
        /*
         * Silently ignore attributes modifications on directories.
         */
-       if (ap->a_vp->v_type == VDIR)
+       if (ap->a_vp->v_type == VDIR) {
+               fstrans_done(vp->v_mount);
                return 0;
+       }
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
        if (vap->va_size != VNOVAL) {
                if (vp->v_mount->mnt_flag & MNT_RDONLY) {
                        error = EROFS;
                        goto bad;
@@ -464,24 +486,30 @@ msdosfs_read(void *v)
        daddr_t lbn;
        vsize_t bytelen;
        struct buf *bp;
        struct vnode *vp = ap->a_vp;
-       struct denode *dep = VTODE(vp);
-       struct msdosfsmount *pmp = dep->de_pmp;
+       struct denode *dep;
+       struct msdosfsmount *pmp;
        struct uio *uio = ap->a_uio;
 
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(vp);
+       pmp = dep->de_pmp;
        /*
         * If they didn't ask for any data, then we are done.
         */
 
-       if (uio->uio_resid == 0)
+       if (uio->uio_resid == 0 || uio->uio_offset >= dep->de_FileSize) {
+               fstrans_done(vp->v_mount);
                return (0);
-       if (uio->uio_offset < 0)
+       }
+       if (uio->uio_offset < 0) {
+               fstrans_done(vp->v_mount);
                return (EINVAL);
-       if (uio->uio_offset >= dep->de_FileSize)
-               return (0);
+       }
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
        if (vp->v_type == VREG) {
                const int advice = IO_ADV_DECODE(ap->a_ioflag);
 
                while (uio->uio_resid > 0) {
@@ -563,13 +591,19 @@ msdosfs_write(void *v)
        off_t oldoff;
        size_t rem;
        struct uio *uio = ap->a_uio;
        struct vnode *vp = ap->a_vp;
-       struct denode *dep = VTODE(vp);
-       struct msdosfsmount *pmp = dep->de_pmp;
+       struct denode *dep;
+       struct msdosfsmount *pmp;
        kauth_cred_t cred = ap->a_cred;
        bool async;
 
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(vp);
+       pmp = dep->de_pmp;
+
 #ifdef MSDOSFS_DEBUG
        printf("msdosfs_write(vp %p, uio %p, ioflag %x, cred %p\n",
            vp, uio, ioflag, cred);
        printf("msdosfs_write(): diroff %lu, dirclust %lu, startcluster %lu\n",
@@ -581,24 +615,30 @@ msdosfs_write(void *v)
                if (ioflag & IO_APPEND)
                        uio->uio_offset = dep->de_FileSize;
                break;
        case VDIR:
+               fstrans_done(vp->v_mount);
                return EISDIR;
        default:
                panic("msdosfs_write(): bad file type");
        }
 
-       if (uio->uio_offset < 0)
+       if (uio->uio_offset < 0) {
+               fstrans_done(vp->v_mount);
                return (EINVAL);
+       }
 
-       if (uio->uio_resid == 0)
+       if (uio->uio_resid == 0) {
+               fstrans_done(vp->v_mount);
                return (0);
+       }
 
        /* Don't bother to try to write files larger than the fs limit */
-       if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX)
+       if (uio->uio_offset + uio->uio_resid > MSDOSFS_FILESIZE_MAX) {
+               fstrans_done(vp->v_mount);
                return (EFBIG);
+       }
 
-       fstrans_start(vp->v_mount, FSTRANS_SHARED);
        /*
         * If the offset we are starting the write at is beyond the end of
         * the file, then they've done a seek.  Unix filesystems allow
         * files with holes in them, DOS doesn't so we must fill the hole
@@ -733,13 +773,17 @@ msdosfs_remove(void *v)
                struct vnode *a_dvp;
                struct vnode *a_vp;
                struct componentname *a_cnp;
        } */ *ap = v;
-       struct denode *dep = VTODE(ap->a_vp);
-       struct denode *ddep = VTODE(ap->a_dvp);
+       struct denode *dep;
+       struct denode *ddep;
        int error;
 
-       fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(ap->a_vp);
+       ddep = VTODE(ap->a_dvp);
        if (ap->a_vp->v_type == VDIR)
                error = EPERM;
        else
                error = removede(ddep, dep);
@@ -843,9 +887,11 @@ msdosfs_rename(void *v)
        struct msdosfsmount *pmp;
        struct direntry *dotdotp;
        struct buf *bp;
 
-       pmp = VFSTOMSDOSFS(fdvp->v_mount);
+       error = fstrans_start(fdvp->v_mount, fdvp, FSTRANS_SHARED);
+       if (error)
+               return error;
 
        /*
         * Check for cross-device rename.
         */
@@ -862,8 +908,9 @@ abortit:
                        vput(tvp);
                VOP_ABORTOP(fdvp, fcnp);
                vrele(fdvp);
                vrele(fvp);
+               fstrans_done(fdvp->v_mount);
                return (error);
        }
 
        /*
@@ -879,8 +926,9 @@ abortit:
         * But I'm not going to fix it now.
         */
        if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
                goto abortit;
+       pmp = VFSTOMSDOSFS(fdvp->v_mount);
        dp = VTODE(fdvp);
        ip = VTODE(fvp);
 
        /*
@@ -906,9 +954,8 @@ abortit:
                doingdirectory++;
        }
        VN_KNOTE(fdvp, NOTE_WRITE);             /* XXXLUKEM/XXX: right place? */
 
-       fstrans_start(fdvp->v_mount, FSTRANS_SHARED);
        /*
         * When the target exists, both the directory
         * and target vnodes are returned locked.
         */
@@ -1177,19 +1224,24 @@ msdosfs_mkdir(void *v)
        } */ *ap = v;
        struct componentname *cnp = ap->a_cnp;
        struct denode ndirent;
        struct denode *dep;
-       struct denode *pdep = VTODE(ap->a_dvp);
+       struct denode *pdep;
        int error;
        int bn;
        u_long newcluster, pcl;
        daddr_t lbn;
        struct direntry *denp;
-       struct msdosfsmount *pmp = pdep->de_pmp;
+       struct msdosfsmount *pmp;
        struct buf *bp;
-       int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
+       int async;
 
-       fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       pdep = VTODE(ap->a_dvp);
+       pmp = pdep->de_pmp;
+       async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
        /*
         * If this is the root directory and there is no space left we
         * can't do anything.  This is because the root directory can not
         * change size.
@@ -1296,19 +1348,22 @@ msdosfs_rmdir(void *v)
        struct componentname *cnp = ap->a_cnp;
        struct denode *ip, *dp;
        int error;
 
+       error = fstrans_start(ap->a_dvp->v_mount, ap->a_dvp, FSTRANS_SHARED);
+       if (error)
+               return error;
        ip = VTODE(vp);
        dp = VTODE(dvp);
        /*
         * No rmdir "." please.
         */
        if (dp == ip) {
                vrele(dvp);
                vput(vp);
+               fstrans_done(ap->a_dvp->v_mount);
                return (EINVAL);
        }
-       fstrans_start(ap->a_dvp->v_mount, FSTRANS_SHARED);
        /*
         * Verify the directory is empty (and valid).
         * (Rmdir ".." won't be valid since
         *  ".." will contain a reference to
@@ -1376,10 +1431,10 @@ msdosfs_readdir(void *v)
        u_long dirsperblk;
        long bias = 0;
        daddr_t bn, lbn;
        struct buf *bp;
-       struct denode *dep = VTODE(ap->a_vp);
-       struct msdosfsmount *pmp = dep->de_pmp;
+       struct denode *dep;
+       struct msdosfsmount *pmp;
        struct direntry *dentp;
        struct dirent *dirbuf;
        struct uio *uio = ap->a_uio;
        off_t *cookies = NULL;
@@ -1390,17 +1445,25 @@ msdosfs_readdir(void *v)
 #ifdef MSDOSFS_DEBUG
        printf("msdosfs_readdir(): vp %p, uio %p, cred %p, eofflagp %p\n",
            ap->a_vp, uio, ap->a_cred, ap->a_eofflag);
 #endif
+       
+       error = fstrans_start(ap->a_vp->v_mount, ap->a_vp, FSTRANS_SHARED);
+       if (error)
+               return error;
+       dep = VTODE(ap->a_vp);
+       pmp = dep->de_pmp;
 
        /*
         * msdosfs_readdir() won't operate properly on regular files since
         * it does i/o only with the filesystem vnode, and hence can
         * retrieve the wrong block from the buffer cache for a plain file.
         * So, fail attempts to readdir() on a plain file.
         */
-       if ((dep->de_Attributes & ATTR_DIRECTORY) == 0)
+       if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
+               fstrans_done(ap->a_vp->v_mount);
                return (ENOTDIR);
+       }
 
        /*
         * If the user buffer is smaller than the size of one dos directory
         * entry or the file offset is not a multiple of the size of a
@@ -1408,15 +1471,15 @@ msdosfs_readdir(void *v)
         */
        count = uio->uio_resid & ~(sizeof(struct direntry) - 1);
        offset = uio->uio_offset;
        if (count < sizeof(struct direntry) ||
-           (offset & (sizeof(struct direntry) - 1)))
+           (offset & (sizeof(struct direntry) - 1))) {
+               fstrans_done(ap->a_vp->v_mount);
                return (EINVAL);
+       }
        lost = uio->uio_resid - count;
        uio->uio_resid = count;
        uio_off = uio->uio_offset;
-       
-       fstrans_start(ap->a_vp->v_mount, FSTRANS_SHARED);
 
        /* Allocate a temporary dirent buffer. */
        dirbuf = malloc(sizeof(struct dirent), M_MSDOSFSTMP, M_WAITOK | M_ZERO);
 
@@ -1806,9 +1869,11 @@ msdosfs_fsync(void *v)
        struct vnode *vp = ap->a_vp;
        int wait;
        int error;
 
-       fstrans_start(vp->v_mount, FSTRANS_LAZY);
+       error = fstrans_start(vp->v_mount, vp, FSTRANS_LAZY);
+       if (error)
+               return error;
        wait = (ap->a_flags & FSYNC_WAIT) != 0;
        error = vflushbuf(vp, ap->a_flags);
        if (error == 0 && (ap->a_flags & FSYNC_DATAONLY) == 0)
                error = msdosfs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0);


Home | Main Index | Thread Index | Old Index