Subject: File system suspension -- new API
To: None <tech-kern@netbsd.org>
From: Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
List: tech-kern
Date: 12/29/2006 16:34:45
--82I3+IH0IqGh5yIs
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline


Now that 4.0 has rebranched I'd like to commit the new file system suspension
API.  It replaces the current API (vn_start_write, vn_finished_write).  The
suspension helpers are now put into file system specific operations.  This means
every file system not supporting these helpers cannot be suspended and therefore
snapshots are no longer possible.

The attached diff implements this for file systems of type ffs.  The new API
is enabled on a kernel option NEWVNGATE.  This option will not be included in
any kernel config.

If noone objects I plan to commit around January 19, 2007.

Once it proves to work I will make the new API the default and remove all calls
to vn_start_write() and vn_finished_write().

A short tour:

To suspend a file system we have two functions:

  int
  vfs_suspend(struct mount *mp, int nowait)

	Serialize suspend requests so only one file system may suspend at
	a time.  Allowing multiple suspensions will lead to a can of
	deadlocks.
	If `nowait' and a suspension is in progress, return error.
	Stop syncer and call VFS method `VFS_SUSPENDCTL()' to do the work.

  void
  vfs_resume(struct mount *mp)

	Resume a currently suspended file system.


To help the VFS method `VFS_SUSPENDCTL()' to suspend a file system a new
lock is used.  One of:

  fstrans_shared	This lock will be granted if the file system
			is neither suspending or suspended.

  fstrans_lazy		This lock will be granted if the file system
			is not suspended.  It must be used on code paths
			that are involved in file system suspension.

	Both locks are recursive.  A thread already owning a shared or
	lazy lock will always get another lock.

  int
  fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type)

	Get a lock of this type.

  int
  fstrans_start_nowait(struct mount *mp, enum fstrans_lock_type lock_type)

	Like above.  Don't wait for the lock to become free.

  void
  fstrans_done(struct mount *mp)

	Release a lock.

  int
  fstrans_is_owner(struct mount *mp)

	True, if this thread is suspending this file system.


Every file system is always in one of these states:

  fstrans_normal	Normal operations
  fstrans_suspending	Preparing a suspension
  fstrans_suspended	Suspended

To query or change the current state we have:

  int
  fstrans_setstate(struct mount *mp, enum fstrans_state new_state)

	Move file system to a new state.  If entering `fstrans_suspending'
	take an exclusive `fstrans_shared' lock.  If entering
	`fstrans_suspended' take an exclusive `fstrans_lazy' lock.  If
	entering `fstrans_normal' release both locks.

  enum fstrans_state
  fstrans_getstate(struct mount *mp)

	Get the current state of this file system.

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

--82I3+IH0IqGh5yIs
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="fstrans.diff"

Index: sys/sys/fstrans.h
--- /dev/null	2006-12-29 11:10:57.000000000 +0100
+++ sys/sys/fstrans.h	2006-11-19 12:00:59.000000000 +0100
@@ -0,0 +1,77 @@
+/*	$NetBSD: Exp $	*/
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Juergen Hannken-Illjes.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the NetBSD
+ *	Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * File system transaction operations.
+ */
+
+#ifndef _SYS_FSTRANS_H_
+#define	_SYS_FSTRANS_H_
+
+#define SUSPEND_SUSPEND	0x0001		/* VFS_SUSPENDCTL: suspend */
+#define SUSPEND_RESUME	0x0002		/* VFS_SUSPENDCTL: resume */
+
+enum fstrans_lock_type {
+	fstrans_lazy = 1,		/* Granted while not suspended */
+	fstrans_shared = 2		/* Granted while not suspending */
+#ifdef _FSTRANS_API_PRIVATE
+	,
+	fstrans_excl = 3		/* Internal: exclusive lock */
+#endif /* _FSTRANS_API_PRIVATE */
+};
+
+enum fstrans_state {
+	fstrans_normal,
+	fstrans_suspending,
+	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);
+void	fstrans_done(struct mount *);
+int	fstrans_is_owner(struct mount *);
+
+int	fstrans_setstate(struct mount *, enum fstrans_state);
+enum fstrans_state fstrans_getstate(struct mount *);
+
+int	vfs_suspend(struct mount *, int);
+void	vfs_resume(struct mount *);
+
+#endif /* _SYS_FSTRANS_H_ */
Index: sys/sys/fstypes.h
===================================================================
RCS file: /cvsroot/src/sys/sys/fstypes.h,v
retrieving revision 1.17
diff -p -u -4 -r1.17 fstypes.h
--- sys/sys/fstypes.h	26 Dec 2006 12:39:01 -0000	1.17
+++ sys/sys/fstypes.h	29 Dec 2006 11:20:58 -0000
@@ -208,8 +208,9 @@ typedef struct fhandle	fhandle_t;
 #define	IMNT_SUSPEND	0x00000008	/* request upper write suspension */
 #define	IMNT_SUSPENDLOW	0x00000010	/* request lower write suspension */
 #define	IMNT_SUSPENDED	0x00000020	/* write operations are suspended */
 #define	IMNT_DTYPE	0x00000040	/* returns d_type fields */
+#define	IMNT_HAS_TRANS	0x00000080	/* supports transactions */
 
 #define __MNT_FLAGS \
 	__MNT_BASIC_FLAGS \
 	__MNT_EXPORTED_FLAGS \
@@ -252,8 +253,9 @@ typedef struct fhandle	fhandle_t;
 	"\01MNT_RDONLY"
 
 #define __IMNT_FLAG_BITS \
 	"\20" \
+	"\10IMNT_HAS_TRANS" \
 	"\07IMNT_DTYPE" \
 	"\06IMNT_SUSPENDED" \
 	"\05IMNT_SUSPENDLOW" \
 	"\04IMNT_SUSPEND" \
Index: sys/sys/mount.h
===================================================================
RCS file: /cvsroot/src/sys/sys/mount.h,v
retrieving revision 1.151
diff -p -u -4 -r1.151 mount.h
--- sys/sys/mount.h	17 Nov 2006 17:05:18 -0000	1.151
+++ sys/sys/mount.h	29 Dec 2006 11:20:58 -0000
@@ -236,8 +236,9 @@ struct vfsops {
 				    struct timespec *);
 	int	(*vfs_extattrctl) (struct mount *, int,
 				    struct vnode *, int, const char *,
 				    struct lwp *);
+	int	(*vfs_suspendctl) (struct mount *, int);
 	const struct vnodeopv_desc * const *vfs_opv_descs;
 	int	vfs_refcount;
 	LIST_ENTRY(vfsops) vfs_list;
 };
@@ -257,8 +258,9 @@ struct vfsops {
 #define	VFS_VPTOFH(VP, FIDP, FIDSZP)  (*(VP)->v_mount->mnt_op->vfs_vptofh)(VP, FIDP, FIDSZP)
 #define VFS_SNAPSHOT(MP, VP, TS)  (*(MP)->mnt_op->vfs_snapshot)(MP, VP, TS)
 #define	VFS_EXTATTRCTL(MP, C, VP, AS, AN, L) \
 	(*(MP)->mnt_op->vfs_extattrctl)(MP, C, VP, AS, AN, L)
+#define VFS_SUSPENDCTL(MP, C)     (*(MP)->mnt_op->vfs_suspendctl)(MP, C)
 
 struct vfs_hooks {
 	void	(*vh_unmount)(struct mount *);
 };
@@ -317,8 +319,9 @@ void	vfs_reinit(void);
 struct vfsops *vfs_getopsbyname(const char *);
 
 int	vfs_stdextattrctl(struct mount *, int, struct vnode *,
 	    int, const char *, struct lwp *);
+int	vfs_stdsuspendctl(struct mount *, int);
 
 extern	CIRCLEQ_HEAD(mntlist, mount) mountlist;	/* mounted filesystem list */
 extern	struct vfsops *vfssw[];			/* filesystem type table */
 extern	int nvfssw;
Index: sys/sys/vnode.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode.h,v
retrieving revision 1.163
diff -p -u -4 -r1.163 vnode.h
--- sys/sys/vnode.h	27 Dec 2006 12:22:14 -0000	1.163
+++ sys/sys/vnode.h	29 Dec 2006 11:20:58 -0000
@@ -588,10 +588,8 @@ int	getvnode(struct filedesc *, int, str
 
 /* see vfssubr(9) */
 void	vfs_getnewfsid(struct mount *);
 int	vfs_drainvnodes(long target, struct lwp *);
-void	vfs_write_resume(struct mount *);
-int	vfs_write_suspend(struct mount *, int, int);
 void	vfs_timestamp(struct timespec *);
 #ifdef DDB
 void	vfs_vnode_print(struct vnode *, int, void (*)(const char *, ...));
 void	vfs_mount_print(struct mount *, int, void (*)(const char *, ...));
Index: sys/kern/vfs_trans.c
--- /dev/null	2006-12-29 11:10:57.000000000 +0100
+++ sys/kern/vfs_trans.c	2006-11-19 12:35:04.000000000 +0100
@@ -0,0 +1,530 @@
+/*	$NetBSD: Exp $	*/
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Juergen Hannken-Illjes.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the NetBSD
+ *	Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: $");
+
+/*
+ * File system transaction operations.
+ */
+
+#include "opt_ddb.h"
+
+#if defined(DDB)
+#define _LWP_API_PRIVATE	/* Need _lwp_getspecific_by_lwp() */
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#define _FSTRANS_API_PRIVATE
+#include <sys/fstrans.h>
+
+#include <miscfs/syncfs/syncfs.h>
+
+struct fstrans_lwp_info {
+	struct fstrans_lwp_info *fli_succ;
+	struct mount *fli_mount;
+	int fli_count;
+	enum fstrans_lock_type fli_lock_type;
+};
+struct fstrans_mount_info {
+	enum fstrans_state fmi_state;
+	struct lock fmi_shared_lock;
+	struct lock fmi_lazy_lock;
+};
+
+static specificdata_key_t lwp_data_key;
+static specificdata_key_t mount_data_key;
+static struct lock vfs_suspend_lock =	/* Serialize suspensions. */
+    LOCK_INITIALIZER(PUSER, "suspwt1", 0, 0);
+
+POOL_INIT(fstrans_pl, sizeof(struct fstrans_lwp_info), 0, 0, 0,
+    "fstrans", NULL);
+
+static void fstrans_lwp_dtor(void *);
+static void fstrans_mount_dtor(void *);
+static struct fstrans_mount_info *fstrans_mount_init(struct mount *);
+
+/*
+ * Initialize
+ */
+void
+fstrans_init(void)
+{
+	int error;
+
+	error = lwp_specific_key_create(&lwp_data_key, fstrans_lwp_dtor);
+	KASSERT(error == 0);
+	error = mount_specific_key_create(&mount_data_key, fstrans_mount_dtor);
+	KASSERT(error == 0);
+}
+
+/*
+ * Deallocate lwp state
+ */
+static void
+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_count == 0);
+		fli_next = fli->fli_succ;
+		pool_put(&fstrans_pl, fli);
+	}
+}
+
+/*
+ * Deallocate mount state
+ */
+static void
+fstrans_mount_dtor(void *arg)
+{
+	struct fstrans_mount_info *fmi = arg;
+
+	KASSERT(fmi->fmi_state == fstrans_normal);
+	lockmgr(&fmi->fmi_lazy_lock, LK_DRAIN, NULL);
+	lockmgr(&fmi->fmi_shared_lock, LK_DRAIN, NULL);
+	free(fmi, M_MOUNT);
+}
+
+/*
+ * Create mount info for this mount
+ */
+static struct fstrans_mount_info *
+fstrans_mount_init(struct mount *mp)
+{
+	static struct lock lock = LOCK_INITIALIZER(PUSER, "ftminfo", 0, 0);
+	struct fstrans_mount_info *new;
+
+	lockmgr(&lock, LK_EXCLUSIVE, NULL);
+
+	if ((new = mount_getspecific(mp, mount_data_key)) != NULL) {
+		lockmgr(&lock, LK_RELEASE, NULL);
+		return new;
+	}
+
+	new = malloc(sizeof(*new), M_MOUNT, M_WAITOK);
+	new->fmi_state = fstrans_normal;
+	lockinit(&new->fmi_lazy_lock, PVFS, "suspfs", 0, 0);
+	lockinit(&new->fmi_shared_lock, PVFS, "suspfs", 0, 0);
+
+	mount_setspecific(mp, mount_data_key, new);
+
+	lockmgr(&lock, LK_RELEASE, NULL);
+
+	return new;
+}
+
+/*
+ * Start a transaction.  If this thread already has a transaction on this
+ * file system increment the reference counter.
+ * A thread with an exclusive transaction lock may get a shared or lazy one.
+ * 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)
+{
+	int error, lkflags;
+	struct fstrans_lwp_info *fli, *new_fli;
+	struct fstrans_mount_info *fmi;
+
+	ASSERT_SLEEPABLE(NULL, __func__);
+
+	lkflags = (lock_type == fstrans_excl ? LK_EXCLUSIVE : LK_SHARED);
+	if (!wait)
+		lkflags |= LK_NOWAIT;
+
+	if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
+		return 0;
+
+	new_fli = NULL;
+	for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
+		if (fli->fli_mount == NULL && new_fli == NULL)
+			new_fli = fli;
+		if (fli->fli_mount == mp) {
+			KASSERT(fli->fli_count > 0);
+			if (fli->fli_lock_type != fstrans_excl &&
+			    lock_type == fstrans_excl)
+				panic("fstrans_start: cannot upgrade lock");
+			fli->fli_count += 1;
+			return 0;
+		}
+	}
+
+	if (new_fli == NULL) {
+		new_fli = pool_get(&fstrans_pl, PR_WAITOK);
+		new_fli->fli_mount = NULL;
+		new_fli->fli_count = 0;
+		new_fli->fli_succ = lwp_getspecific(lwp_data_key);
+		lwp_setspecific(lwp_data_key, new_fli);
+	}
+
+	KASSERT(new_fli->fli_mount == NULL);
+	KASSERT(new_fli->fli_count == 0);
+
+	if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL)
+		fmi = fstrans_mount_init(mp);
+
+	if (lock_type == fstrans_lazy)
+		error = lockmgr(&fmi->fmi_lazy_lock, lkflags, NULL);
+	else
+		error = lockmgr(&fmi->fmi_shared_lock, lkflags, NULL);
+	if (error)
+		return error;
+
+	new_fli->fli_mount = mp;
+	new_fli->fli_count = 1;
+	new_fli->fli_lock_type = lock_type;
+
+	return 0;
+}
+
+/*
+ * Finish a transaction.
+ */
+void
+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)
+		return;
+
+	for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
+		if (fli->fli_mount == mp) {
+			fli->fli_count -= 1;
+			if (fli->fli_count > 0)
+				return;
+			break;
+		}
+	}
+
+	KASSERT(fli != NULL);
+	KASSERT(fli->fli_mount == mp);
+	KASSERT(fli->fli_count == 0);
+	fli->fli_mount = NULL;
+	fmi = mount_getspecific(mp, mount_data_key);
+	KASSERT(fmi != NULL);
+	if (fli->fli_lock_type == fstrans_lazy)
+		lockmgr(&fmi->fmi_lazy_lock, LK_RELEASE, NULL);
+	else
+		lockmgr(&fmi->fmi_shared_lock, LK_RELEASE, NULL);
+}
+
+/*
+ * Check if this thread has an exclusive lock.
+ */
+int
+fstrans_is_owner(struct mount *mp)
+{
+	struct fstrans_lwp_info *fli;
+
+	if (mp == NULL)
+		return 0;
+	if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
+		return 0;
+
+	for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ)
+		if (fli->fli_mount == mp)
+			break;
+
+	if (fli == NULL)
+		return 0;
+
+	KASSERT(fli->fli_mount == mp);
+	KASSERT(fli->fli_count > 0);
+	return (fli->fli_lock_type == fstrans_excl);
+}
+
+/*
+ * Set new file system state.
+ */
+int
+fstrans_setstate(struct mount *mp, enum fstrans_state new_state)
+{
+	int error;
+	struct fstrans_mount_info *fmi;
+
+	if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL)
+		fmi = fstrans_mount_init(mp);
+
+	switch (new_state) {
+	case fstrans_suspending:
+		KASSERT(fmi->fmi_state == fstrans_normal);
+		if ((error = fstrans_start(mp, fstrans_excl)) != 0)
+			return error;
+		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 &&
+		    (error = fstrans_start(mp, fstrans_excl)) != 0)
+			return error;
+		if ((error = lockmgr(&fmi->fmi_lazy_lock, LK_EXCLUSIVE, NULL))
+		    != 0)
+			return error;
+		fmi->fmi_state = fstrans_suspended;
+		break;
+
+	case fstrans_normal:
+		KASSERT(fmi->fmi_state == fstrans_normal ||
+			fstrans_is_owner(mp));
+		if (fmi->fmi_state == fstrans_suspended)
+			lockmgr(&fmi->fmi_lazy_lock, LK_RELEASE, NULL);
+		if (fmi->fmi_state == fstrans_suspending ||
+		    fmi->fmi_state == fstrans_suspended) {
+			fmi->fmi_state = fstrans_normal;
+			fstrans_done(mp);
+		}
+		break;
+
+	default:
+		panic("%s: illegal state %d", __func__, new_state);
+	}
+
+	return 0;
+}
+
+/*
+ * Get current file system state
+ */
+enum fstrans_state
+fstrans_getstate(struct mount *mp)
+{
+	struct fstrans_mount_info *fmi;
+
+	if ((fmi = mount_getspecific(mp, mount_data_key)) == NULL)
+		return fstrans_normal;
+
+	return fmi->fmi_state;
+}
+
+/*
+ * Request a filesystem to suspend all operations.
+ */
+int
+vfs_suspend(struct mount *mp, int nowait)
+{
+#ifndef NEWVNGATE
+	struct lwp *l = curlwp;	/* XXX */
+#endif /* NEWVNGATE */
+	int error, flags;
+
+	flags = LK_EXCLUSIVE;
+	if (nowait)
+		flags |= LK_NOWAIT;
+	if (lockmgr(&vfs_suspend_lock, flags, NULL) != 0)
+		return EWOULDBLOCK;
+
+#ifdef NEWVNGATE
+	lockmgr(&syncer_lock, LK_EXCLUSIVE, NULL);
+
+	if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND)) != 0) {
+		lockmgr(&syncer_lock, LK_RELEASE, NULL);
+		lockmgr(&vfs_suspend_lock, LK_RELEASE, NULL);
+	}
+
+	return error;
+#else /* NEWVNGATE */
+	KASSERT((mp->mnt_iflag &
+	    (IMNT_SUSPEND|IMNT_SUSPENDLOW|IMNT_SUSPENDED)) == 0);
+
+	mp->mnt_iflag |= IMNT_SUSPEND;
+
+	simple_lock(&mp->mnt_slock);
+	if (mp->mnt_writeopcountupper > 0)
+		ltsleep(&mp->mnt_writeopcountupper, PUSER - 1, "suspwt",
+			0, &mp->mnt_slock);
+	simple_unlock(&mp->mnt_slock);
+
+	error = VFS_SYNC(mp, MNT_WAIT, l->l_proc->p_cred, l);
+	if (error) {
+		vfs_resume(mp);
+		return error;
+	}
+	mp->mnt_iflag |= IMNT_SUSPENDLOW;
+
+	simple_lock(&mp->mnt_slock);
+	if (mp->mnt_writeopcountlower > 0)
+		ltsleep(&mp->mnt_writeopcountlower, PUSER - 1, "suspwt",
+			0, &mp->mnt_slock);
+	mp->mnt_iflag |= IMNT_SUSPENDED;
+	simple_unlock(&mp->mnt_slock);
+
+	return 0;
+#endif /* NEWVNGATE */
+}
+
+/*
+ * Request a filesystem to resume all operations.
+ */
+void
+vfs_resume(struct mount *mp)
+{
+
+#ifdef NEWVNGATE
+	VFS_SUSPENDCTL(mp, SUSPEND_RESUME);
+	lockmgr(&syncer_lock, LK_RELEASE, NULL);
+	lockmgr(&vfs_suspend_lock, LK_RELEASE, NULL);
+#else /* NEWVNGATE */
+	if ((mp->mnt_iflag & IMNT_SUSPEND) == 0)
+		return;
+	mp->mnt_iflag &= ~(IMNT_SUSPEND | IMNT_SUSPENDLOW | IMNT_SUSPENDED);
+	wakeup(&mp->mnt_flag);
+
+	lockmgr(&vfs_suspend_lock, LK_RELEASE, NULL);
+#endif /* NEWVNGATE */
+}
+
+/*
+ * Default vfs_suspendctl routine for file systems that do not support it.
+ */
+/*ARGSUSED*/
+int
+vfs_stdsuspendctl(struct mount *mp __unused, int mode __unused)
+{
+	return EOPNOTSUPP;
+}
+
+#if defined(DDB)
+void fstrans_dump(int);
+
+static void
+fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose)
+{
+	char prefix[9];
+	struct fstrans_lwp_info *fli;
+
+	snprintf(prefix, sizeof(prefix), "%d.%d", p->p_pid, l->l_lid);
+	for (fli = _lwp_getspecific_by_lwp(l, lwp_data_key);
+	     fli;
+	     fli = fli->fli_succ) {
+		if (!verbose && fli->fli_count == 0)
+			continue;
+		printf("%-8s", prefix);
+		if (verbose)
+			printf(" @%p", fli);
+		if (fli->fli_mount != NULL)
+			printf(" (%s)", fli->fli_mount->mnt_stat.f_mntonname);
+		else
+			printf(" NULL");
+		switch (fli->fli_lock_type) {
+		case fstrans_lazy:
+			printf(" lazy");
+			break;
+		case fstrans_shared:
+			printf(" shared");
+			break;
+		case fstrans_excl:
+			printf(" excl");
+			break;
+		default:
+			printf(" %#x", fli->fli_lock_type);
+			break;
+		}
+		printf(" %d\n", fli->fli_count);
+		prefix[0] = '\0';
+	}
+}
+
+static void
+fstrans_print_mount(struct mount *mp, int verbose)
+{
+	struct fstrans_mount_info *fmi;
+
+	fmi = mount_getspecific(mp, mount_data_key);
+	if (!verbose && (fmi == NULL || fmi->fmi_state == fstrans_normal))
+		return;
+
+	printf("%-16s ", mp->mnt_stat.f_mntonname);
+	if (fmi == NULL) {
+		printf("(null)\n");
+		return;
+	}
+	switch (fmi->fmi_state) {
+	case fstrans_normal:
+		printf("state normal\n");
+		break;
+	case fstrans_suspending:
+		printf("state suspending\n");
+		break;
+	case fstrans_suspended:
+		printf("state suspended\n");
+		break;
+	default:
+		printf("state %#x\n", fmi->fmi_state);
+		break;
+	}
+	printf("%16s", "lock_lazy:");
+	lockmgr_printinfo(&fmi->fmi_lazy_lock);
+	printf("\n");
+	printf("%16s", "lock_shared:");
+	lockmgr_printinfo(&fmi->fmi_shared_lock);
+	printf("\n");
+}
+
+void
+fstrans_dump(int full)
+{
+	const struct proclist_desc *pd;
+	struct proc *p;
+	struct lwp *l;
+	struct mount *mp;
+
+	printf("Fstrans locks by lwp:\n");
+	for (pd = proclists; pd->pd_list != NULL; pd++)
+		LIST_FOREACH(p, pd->pd_list, p_list)
+			LIST_FOREACH(l, &p->p_lwps, l_sibling)
+				fstrans_print_lwp(p, l, full == 1);
+
+	printf("Fstrans state by mount:\n");
+	CIRCLEQ_FOREACH(mp, &mountlist, mnt_list)
+		fstrans_print_mount(mp, full == 1);
+}
+#endif /* defined(DDB) */
Index: sys/miscfs/genfs/genfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/genfs/genfs_vnops.c,v
retrieving revision 1.142
diff -p -u -4 -r1.142 genfs_vnops.c
--- sys/miscfs/genfs/genfs_vnops.c	27 Dec 2006 12:10:09 -0000	1.142
+++ sys/miscfs/genfs/genfs_vnops.c	29 Dec 2006 11:20:57 -0000
@@ -45,8 +45,9 @@ __KERNEL_RCSID(0, "$NetBSD: genfs_vnops.
 #include <sys/poll.h>
 #include <sys/mman.h>
 #include <sys/file.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/genfs/genfs.h>
 #include <miscfs/genfs/genfs_node.h>
 #include <miscfs/specfs/specdev.h>
@@ -436,8 +437,9 @@ genfs_getpages(void *v)
 	kauth_cred_t cred = curlwp->l_cred;		/* XXXUBC curlwp */
 	boolean_t async = (flags & PGO_SYNCIO) == 0;
 	boolean_t write = (ap->a_access_type & VM_PROT_WRITE) != 0;
 	boolean_t sawhole = FALSE;
+	boolean_t has_trans = FALSE;
 	boolean_t overwrite = (flags & PGO_OVERWRITE) != 0;
 	boolean_t blockalloc = write && (flags & PGO_NOBLOCKALLOC) == 0;
 	voff_t origvsize;
 	UVMHIST_FUNC("genfs_getpages"); UVMHIST_CALLED(ubchist);
@@ -452,8 +454,11 @@ genfs_getpages(void *v)
 	if (*ap->a_count > MAX_READ_PAGES) {
 		panic("genfs_getpages: too many pages");
 	}
 
+	pgs = pgs_onstack;
+	pgs_size = sizeof(pgs_onstack);
+
 startover:
 	error = 0;
 	origvsize = vp->v_size;
 	origoffset = ap->a_offset;
@@ -479,9 +484,10 @@ startover:
 			simple_unlock(&uobj->vmobjlock);
 		}
 		UVMHIST_LOG(ubchist, "off 0x%x count %d goes past EOF 0x%x",
 		    origoffset, *ap->a_count, memeof,0);
-		return (EINVAL);
+		error = EINVAL;
+		goto out_err;
 	}
 
 	/* uobj is locked */
 
@@ -528,9 +534,10 @@ startover:
 		nfound = uvn_findpages(uobj, origoffset, &npages,
 		    ap->a_m, UFP_NOWAIT|UFP_NOALLOC|(write ? UFP_NORDONLY : 0));
 		KASSERT(npages == *ap->a_count);
 		if (nfound == 0) {
-			return EBUSY;
+			error = EBUSY;
+			goto out_err;
 		}
 		if (lockmgr(&gp->g_glock, LK_SHARED | LK_NOWAIT, NULL)) {
 			genfs_rel_pages(ap->a_m, npages);
 
@@ -547,9 +554,10 @@ startover:
 			}
 		} else {
 			lockmgr(&gp->g_glock, LK_RELEASE, NULL);
 		}
-		return (ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0);
+		error = (ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0);
+		goto out_err;
 	}
 	simple_unlock(&uobj->vmobjlock);
 
 	/*
@@ -579,17 +587,25 @@ startover:
 	    ((endoffset - startoffset) >> PAGE_SHIFT);
 	if (pgs_size > sizeof(pgs_onstack)) {
 		pgs = kmem_zalloc(pgs_size, async ? KM_NOSLEEP : KM_SLEEP);
 		if (pgs == NULL) {
-			return (ENOMEM);
+			pgs = pgs_onstack;
+			error = ENOMEM;
+			goto out_err;
 		}
 	} else {
-		pgs = pgs_onstack;
+		/* pgs == pgs_onstack */
 		memset(pgs, 0, pgs_size);
 	}
 	UVMHIST_LOG(ubchist, "ridx %d npages %d startoff %ld endoff %ld",
 	    ridx, npages, startoffset, endoffset);
 
+	if (!has_trans &&
+	    (error = fstrans_start(vp->v_mount, fstrans_shared)) != 0) {
+		goto out_err;
+	}
+	has_trans = TRUE;
+
 	/*
 	 * hold g_glock to prevent a race with truncate.
 	 *
 	 * check if our idea of v_size is still valid.
@@ -613,11 +629,10 @@ startover:
 		lockmgr(&gp->g_glock, LK_RELEASE, NULL);
 		KASSERT(async != 0);
 		genfs_rel_pages(&pgs[ridx], orignpages);
 		simple_unlock(&uobj->vmobjlock);
-		if (pgs != pgs_onstack)
-			kmem_free(pgs, pgs_size);
-		return (EBUSY);
+		error = EBUSY;
+		goto out_err;
 	}
 
 	/*
 	 * if the pages are already resident, just return them.
@@ -681,11 +696,10 @@ startover:
 			lockmgr(&gp->g_glock, LK_RELEASE, NULL);
 			KASSERT(async != 0);
 			genfs_rel_pages(pgs, npages);
 			simple_unlock(&uobj->vmobjlock);
-			if (pgs != pgs_onstack)
-				kmem_free(pgs, pgs_size);
-			return (EBUSY);
+			error = EBUSY;
+			goto out_err;
 		}
 	}
 	simple_unlock(&uobj->vmobjlock);
 
@@ -853,11 +867,10 @@ loopdone:
 	nestiobuf_done(mbp, skipbytes, error);
 	if (async) {
 		UVMHIST_LOG(ubchist, "returning 0 (async)",0,0,0,0);
 		lockmgr(&gp->g_glock, LK_RELEASE, NULL);
-		if (pgs != pgs_onstack)
-			kmem_free(pgs, pgs_size);
-		return (0);
+		error = 0;
+		goto out_err;
 	}
 	if (bp != NULL) {
 		error = biowait(mbp);
 	}
@@ -913,15 +926,14 @@ loopdone:
 		uvm_page_unbusy(pgs, npages);
 		uvm_unlock_pageq();
 		simple_unlock(&uobj->vmobjlock);
 		UVMHIST_LOG(ubchist, "returning error %d", error,0,0,0);
-		if (pgs != pgs_onstack)
-			kmem_free(pgs, pgs_size);
-		return (error);
+		goto out_err;
 	}
 
 out:
 	UVMHIST_LOG(ubchist, "succeeding, npages %d", npages,0,0,0);
+	error = 0;
 	uvm_lock_pageq();
 	for (i = 0; i < npages; i++) {
 		pg = pgs[i];
 		if (pg == NULL) {
@@ -958,11 +970,15 @@ out:
 	if (ap->a_m != NULL) {
 		memcpy(ap->a_m, &pgs[ridx],
 		    orignpages * sizeof(struct vm_page *));
 	}
+
+out_err:
 	if (pgs != pgs_onstack)
 		kmem_free(pgs, pgs_size);
-	return (0);
+	if (has_trans)
+		fstrans_done(vp->v_mount);
+	return (error);
 }
 
 /*
  * generic VM putpages routine.
@@ -1039,8 +1055,9 @@ genfs_putpages(void *v)
 	struct lwp *l = curlwp ? curlwp : &lwp0;
 	struct genfs_node *gp = VTOG(vp);
 	int dirtygen;
 	boolean_t modified = FALSE;
+	boolean_t has_trans = FALSE;
 	boolean_t cleanall;
 
 	UVMHIST_FUNC("genfs_putpages"); UVMHIST_CALLED(ubchist);
 
@@ -1068,8 +1085,20 @@ genfs_putpages(void *v)
 	/*
 	 * the vnode has pages, set up to process the request.
 	 */
 
+	if ((flags & PGO_CLEANIT) != 0) {
+		simple_unlock(slock);
+		if (pagedaemon)
+			error = fstrans_start_nowait(vp->v_mount, fstrans_lazy);
+		else
+			error = fstrans_start(vp->v_mount, fstrans_lazy);
+		if (error)
+			return error;
+		has_trans = TRUE;
+		simple_lock(slock);
+	}
+
 	error = 0;
 	s = splbio();
 	simple_lock(&global_v_numoutput_slock);
 	wasclean = (vp->v_numoutput == 0);
@@ -1423,8 +1452,12 @@ skip_scan:
 		}
 		splx(s);
 	}
 	simple_unlock(slock);
+
+	if (has_trans)
+		fstrans_done(vp->v_mount);
+
 	return (error);
 }
 
 int
Index: sys/ufs/ufs/ufs_bmap.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_bmap.c,v
retrieving revision 1.40
diff -p -u -4 -r1.40 ufs_bmap.c
--- sys/ufs/ufs/ufs_bmap.c	4 Apr 2006 17:12:57 -0000	1.40
+++ sys/ufs/ufs/ufs_bmap.c	29 Dec 2006 11:21:00 -0000
@@ -46,8 +46,9 @@ __KERNEL_RCSID(0, "$NetBSD: ufs_bmap.c,v
 #include <sys/vnode.h>
 #include <sys/mount.h>
 #include <sys/resourcevar.h>
 #include <sys/trace.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/specfs/specdev.h>
 
 #include <ufs/ufs/inode.h>
@@ -80,8 +81,10 @@ ufs_bmap(void *v)
 		struct vnode **a_vpp;
 		daddr_t *a_bnp;
 		int *a_runp;
 	} */ *ap = v;
+	int error;
+
 	/*
 	 * Check for underlying vnode requests and ensure that logical
 	 * to physical mapping is requested.
 	 */
@@ -89,10 +92,14 @@ ufs_bmap(void *v)
 		*ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
 	if (ap->a_bnp == NULL)
 		return (0);
 
-	return (ufs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
-	    ap->a_runp, ufs_issequential));
+	if ((error = fstrans_start(ap->a_vp->v_mount, fstrans_shared)) != 0)
+		return error;
+	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;
 }
 
 /*
  * Indirect blocks are now on the vnode for the file.  They are given negative
Index: sys/ufs/ufs/ufs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_extern.h,v
retrieving revision 1.49
diff -p -u -4 -r1.49 ufs_extern.h
--- sys/ufs/ufs/ufs_extern.h	14 May 2006 21:33:39 -0000	1.49
+++ sys/ufs/ufs/ufs_extern.h	29 Dec 2006 11:21:00 -0000
@@ -65,12 +65,12 @@ int	ufs_create(void *);
 int	ufs_getattr(void *);
 int	ufs_inactive(void *);
 #define	ufs_fcntl	genfs_fcntl
 #define	ufs_ioctl	genfs_enoioctl
-#define	ufs_islocked	genfs_islocked
+int	ufs_islocked(void *);
 #define	ufs_lease_check genfs_lease_check
 int	ufs_link(void *);
-#define ufs_lock	genfs_lock
+int	ufs_lock(void *);
 int	ufs_lookup(void *);
 int	ufs_mkdir(void *);
 int	ufs_mknod(void *);
 #define	ufs_mmap	genfs_mmap
@@ -87,9 +87,9 @@ int	ufs_rmdir(void *);
 #define	ufs_poll	genfs_poll
 int	ufs_setattr(void *);
 int	ufs_strategy(void *);
 int	ufs_symlink(void *);
-#define ufs_unlock	genfs_unlock
+int	ufs_unlock(void *);
 int	ufs_whiteout(void *);
 int	ufsspec_close(void *);
 int	ufsspec_read(void *);
 int	ufsspec_write(void *);
Index: sys/ufs/ufs/ufs_inode.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_inode.c,v
retrieving revision 1.62
diff -p -u -4 -r1.62 ufs_inode.c
--- sys/ufs/ufs/ufs_inode.c	16 Nov 2006 01:33:53 -0000	1.62
+++ sys/ufs/ufs/ufs_inode.c	29 Dec 2006 11:21:00 -0000
@@ -51,8 +51,9 @@ __KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,
 #include <sys/mount.h>
 #include <sys/kernel.h>
 #include <sys/namei.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <ufs/ufs/quota.h>
 #include <ufs/ufs/inode.h>
 #include <ufs/ufs/ufsmount.h>
@@ -79,16 +80,19 @@ ufs_inactive(void *v)
 		struct lwp *a_l;
 	} */ *ap = v;
 	struct vnode *vp = ap->a_vp;
 	struct inode *ip = VTOI(vp);
-	struct mount *mp;
+	struct mount *mp, *transmp;
 	struct lwp *l = ap->a_l;
 	mode_t mode;
 	int error = 0;
 
 	if (prtactive && vp->v_usecount != 0)
 		vprint("ufs_inactive: pushing active", vp);
 
+	transmp = vp->v_mount;
+	if ((error = fstrans_start(transmp, fstrans_shared)) != 0)
+		return error;
 	/*
 	 * Ignore inodes related to stale file handles.
 	 */
 	if (ip->i_mode == 0)
@@ -141,8 +145,9 @@ out:
 	 */
 
 	if (ip->i_mode == 0)
 		vrecycle(vp, NULL, l);
+	fstrans_done(transmp);
 	return (error);
 }
 
 /*
Index: sys/ufs/ufs/ufs_lookup.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_lookup.c,v
retrieving revision 1.81
diff -p -u -4 -r1.81 ufs_lookup.c
--- sys/ufs/ufs/ufs_lookup.c	9 Dec 2006 16:11:52 -0000	1.81
+++ sys/ufs/ufs/ufs_lookup.c	29 Dec 2006 11:21:00 -0000
@@ -52,8 +52,9 @@ __KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c
 #include <sys/mount.h>
 #include <sys/vnode.h>
 #include <sys/kernel.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <ufs/ufs/inode.h>
 #include <ufs/ufs/dir.h>
 #ifdef UFS_DIRHASH
@@ -168,8 +169,11 @@ ufs_lookup(void *v)
 	if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) {
 		return (error);
 	}
 
+	if ((error = fstrans_start(vdp->v_mount, fstrans_shared)) != 0)
+		return error;
+
 	/*
 	 * 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
@@ -243,9 +247,9 @@ ufs_lookup(void *v)
 	} else {
 		dp->i_offset = dp->i_diroff;
 		if ((entryoffsetinblock = dp->i_offset & bmask) &&
 		    (error = ufs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp)))
-			return (error);
+			goto out;
 		numdirpasses = 2;
 		nchstats.ncs_2passes++;
 	}
 	prevoff = dp->i_offset;
@@ -264,9 +268,9 @@ searchloop:
 				brelse(bp);
 			error = ufs_blkatoff(vdp, (off_t)dp->i_offset, NULL,
 			    &bp);
 			if (error)
-				return (error);
+				goto out;
 			entryoffsetinblock = 0;
 		}
 		/*
 		 * If still looking for a slot, and at a DIRBLKSIZE
@@ -421,9 +425,9 @@ notfound:
 		 * creation of files in the directory.
 		 */
 		error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
 		if (error)
-			return (error);
+			goto out;
 		/*
 		 * Return an indication of where the new directory
 		 * entry should be put.  If we didn't find a slot,
 		 * then set dp->i_count to 0 indicating
@@ -465,16 +469,18 @@ notfound:
 		 * NB - if the directory is unlocked, then this
 		 * information cannot be used.
 		 */
 		cnp->cn_flags |= SAVENAME;
-		return (EJUSTRETURN);
+		error = EJUSTRETURN;
+		goto out;
 	}
 	/*
 	 * Insert name into cache (as non-existent) if appropriate.
 	 */
 	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
 		cache_enter(vdp, *vpp, cnp);
-	return (ENOENT);
+	error = ENOENT;
+	goto out;
 
 found:
 	if (numdirpasses == 2)
 		nchstats.ncs_pass2++;
@@ -508,9 +514,9 @@ found:
 		 * Write access to directory required to delete files.
 		 */
 		error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
 		if (error)
-			return (error);
+			goto out;
 		/*
 		 * Return pointer to current entry in dp->i_offset,
 		 * and distance past previous entry (if there
 		 * is a previous entry in this block) in dp->i_count.
@@ -522,17 +528,18 @@ found:
 			dp->i_count = dp->i_offset - prevoff;
 		if (dp->i_number == foundino) {
 			VREF(vdp);
 			*vpp = vdp;
-			return (0);
+			error = 0;
+			goto out;
 		}
 		if (flags & ISDOTDOT)
 			VOP_UNLOCK(vdp, 0); /* race to get the inode */
 		error = VFS_VGET(vdp->v_mount, foundino, &tdp);
 		if (flags & ISDOTDOT)
 			vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
 		if (error)
-			return (error);
+			goto out;
 		/*
 		 * If directory is "sticky", then user must own
 		 * the directory, or the file in it, else she
 		 * may not delete it (unless she's root). This
@@ -542,12 +549,14 @@ found:
 		    kauth_cred_geteuid(cred) != 0 &&
 		    kauth_cred_geteuid(cred) != dp->i_uid &&
 		    VTOI(tdp)->i_uid != kauth_cred_geteuid(cred)) {
 			vput(tdp);
-			return (EPERM);
+			error = EPERM;
+			goto out;
 		}
 		*vpp = tdp;
-		return (0);
+		error = 0;
+		goto out;
 	}
 
 	/*
 	 * If rewriting (RENAME), return the inode and the
@@ -557,25 +566,28 @@ found:
 	 */
 	if (nameiop == RENAME && (flags & ISLASTCN)) {
 		error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
 		if (error)
-			return (error);
+			goto out;
 		/*
 		 * Careful about locking second inode.
 		 * This can only occur if the target is ".".
 		 */
-		if (dp->i_number == foundino)
-			return (EISDIR);
+		if (dp->i_number == foundino) {
+			error = EISDIR;
+			goto out;
+		}
 		if (flags & ISDOTDOT)
 			VOP_UNLOCK(vdp, 0); /* race to get the inode */
 		error = VFS_VGET(vdp->v_mount, foundino, &tdp);
 		if (flags & ISDOTDOT)
 			vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
 		if (error)
-			return (error);
+			goto out;
 		*vpp = tdp;
 		cnp->cn_flags |= SAVENAME;
-		return (0);
+		error = 0;
+		goto out;
 	}
 
 	/*
 	 * Step through the translation in the name.  We do not `vput' the
@@ -601,27 +613,31 @@ found:
 		VOP_UNLOCK(pdp, 0);	/* race to get the inode */
 		error = VFS_VGET(vdp->v_mount, foundino, &tdp);
 		vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY);
 		if (error) {
-			return error;
+			goto out;
 		}
 		*vpp = tdp;
 	} else if (dp->i_number == foundino) {
 		VREF(vdp);	/* we want ourself, ie "." */
 		*vpp = vdp;
 	} else {
 		error = VFS_VGET(vdp->v_mount, foundino, &tdp);
 		if (error)
-			return (error);
+			goto out;
 		*vpp = tdp;
 	}
 
 	/*
 	 * Insert name into cache if appropriate.
 	 */
 	if (cnp->cn_flags & MAKEENTRY)
 		cache_enter(vdp, *vpp, cnp);
-	return (0);
+	error = 0;
+
+out:
+	fstrans_done(vdp->v_mount);
+	return error;
 }
 
 void
 ufs_dirbad(struct inode *ip, doff_t offset, const char *how)
Index: sys/ufs/ufs/ufs_readwrite.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_readwrite.c,v
retrieving revision 1.71
diff -p -u -4 -r1.71 ufs_readwrite.c
--- sys/ufs/ufs/ufs_readwrite.c	14 Oct 2006 09:17:26 -0000	1.71
+++ sys/ufs/ufs/ufs_readwrite.c	29 Dec 2006 11:21:00 -0000
@@ -103,8 +103,12 @@ READ(void *v)
 	if ((u_int64_t)uio->uio_offset > ump->um_maxfilesize)
 		return (EFBIG);
 	if (uio->uio_resid == 0)
 		return (0);
+
+	if ((error = fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+		return error;
+
 	if (uio->uio_offset >= ip->i_size)
 		goto out;
 
 #ifdef LFS_READWRITE
@@ -182,8 +186,10 @@ READ(void *v)
 		ip->i_flag |= IN_ACCESS;
 		if ((ap->a_ioflag & IO_SYNC) == IO_SYNC)
 			error = UFS_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
 	}
+
+	fstrans_done(vp->v_mount);
 	return (error);
 }
 
 /*
@@ -273,8 +279,11 @@ WRITE(void *v)
 	}
 	if (uio->uio_resid == 0)
 		return (0);
 
+	if ((error = fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+		return error;
+
 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
 	async = vp->v_mount->mnt_flag & MNT_ASYNC;
 	origoff = uio->uio_offset;
 	resid = uio->uio_resid;
@@ -507,6 +516,9 @@ out:
 		uio->uio_resid = resid;
 	} else if (resid > uio->uio_resid && (ioflag & IO_SYNC) == IO_SYNC)
 		error = UFS_UPDATE(vp, NULL, NULL, UPDATE_WAIT);
 	KASSERT(vp->v_size == ip->i_size);
+
+	fstrans_done(vp->v_mount);
+
 	return (error);
 }
Index: sys/ufs/ufs/ufs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_vnops.c,v
retrieving revision 1.145
diff -p -u -4 -r1.145 ufs_vnops.c
--- sys/ufs/ufs/ufs_vnops.c	26 Dec 2006 14:50:08 -0000	1.145
+++ sys/ufs/ufs/ufs_vnops.c	29 Dec 2006 11:21:01 -0000
@@ -59,8 +59,9 @@ __KERNEL_RCSID(0, "$NetBSD: ufs_vnops.c,
 #include <sys/malloc.h>
 #include <sys/dirent.h>
 #include <sys/lockf.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/specfs/specdev.h>
 #include <miscfs/fifofs/fifo.h>
 
@@ -104,11 +105,14 @@ ufs_create(void *v)
 		struct vattr		*a_vap;
 	} */ *ap = v;
 	int	error;
 
+	if ((error = fstrans_start(ap->a_dvp->v_mount, fstrans_shared)) != 0)
+		return error;
 	error =
 	    ufs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
 			  ap->a_dvp, ap->a_vpp, ap->a_cnp);
+	fstrans_done(ap->a_dvp->v_mount);
 	if (error)
 		return (error);
 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
 	return (0);
@@ -135,12 +139,14 @@ ufs_mknod(void *v)
 	ino_t		ino;
 
 	vap = ap->a_vap;
 	vpp = ap->a_vpp;
+	if ((error = fstrans_start(ap->a_dvp->v_mount, fstrans_shared)) != 0)
+		return error;
 	if ((error =
 	    ufs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
 	    ap->a_dvp, vpp, ap->a_cnp)) != 0)
-		return (error);
+		goto out;
 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
 	ip = VTOI(*vpp);
 	mp  = (*vpp)->v_mount;
 	ino = ip->i_number;
@@ -166,8 +172,10 @@ ufs_mknod(void *v)
 	vput(*vpp);
 	(*vpp)->v_type = VNON;
 	vgone(*vpp);
 	error = VFS_VGET(mp, ino, vpp);
+out:
+	fstrans_done(ap->a_dvp->v_mount);
 	if (error != 0) {
 		*vpp = NULL;
 		return (error);
 	}
@@ -257,10 +265,15 @@ ufs_access(void *v)
 		case VREG:
 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
 				return (EROFS);
 #ifdef QUOTA
-			if ((error = getinoquota(ip)) != 0)
-				return (error);
+			if ((error =
+			    fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+				return error;
+			error = getinoquota(ip);
+			fstrans_done(vp->v_mount);
+			if (error != 0)
+				return error;
 #endif
 			break;
 		case VBAD:
 		case VBLK:
@@ -383,51 +396,71 @@ ufs_setattr(void *v)
 	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
 	    ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
 		return (EINVAL);
 	}
+
+	if ((error = fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+		return error;
+
 	if (vap->va_flags != VNOVAL) {
-		if (vp->v_mount->mnt_flag & MNT_RDONLY)
-			return (EROFS);
+		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+			error = EROFS;
+			goto out;
+		}
 		if (kauth_cred_geteuid(cred) != ip->i_uid &&
 		    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
 		    &l->l_acflag)))
-			return (error);
+			goto out;
 		if (kauth_cred_geteuid(cred) == 0) {
 			if ((ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) &&
-			    securelevel > 0)
-				return (EPERM);
+			    securelevel > 0) {
+				error =  EPERM;
+				goto out;
+			}
 			/* Snapshot flag cannot be set or cleared */
 			if ((vap->va_flags & SF_SNAPSHOT) !=
-			    (ip->i_flags & SF_SNAPSHOT))
-				return (EPERM);
+			    (ip->i_flags & SF_SNAPSHOT)) {
+				error = EPERM;
+				goto out;
+			}
 			ip->i_flags = vap->va_flags;
 			DIP_ASSIGN(ip, flags, ip->i_flags);
 		} else {
 			if ((ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) ||
-			    (vap->va_flags & UF_SETTABLE) != vap->va_flags)
-				return (EPERM);
+			    (vap->va_flags & UF_SETTABLE) != vap->va_flags) {
+				error = EPERM;
+				goto out;
+			}
 			if ((ip->i_flags & SF_SETTABLE) !=
-			    (vap->va_flags & SF_SETTABLE))
-				return (EPERM);
+			    (vap->va_flags & SF_SETTABLE)) {
+				error = EPERM;
+				goto out;
+			}
 			ip->i_flags &= SF_SETTABLE;
 			ip->i_flags |= (vap->va_flags & UF_SETTABLE);
 			DIP_ASSIGN(ip, flags, ip->i_flags);
 		}
 		ip->i_flag |= IN_CHANGE;
-		if (vap->va_flags & (IMMUTABLE | APPEND))
-			return (0);
+		if (vap->va_flags & (IMMUTABLE | APPEND)) {
+			error = 0;
+			goto out;
+		}
+	}
+	if (ip->i_flags & (IMMUTABLE | APPEND)) {
+		error = EPERM;
+		goto out;
 	}
-	if (ip->i_flags & (IMMUTABLE | APPEND))
-		return (EPERM);
 	/*
 	 * Go through the fields and update iff not VNOVAL.
 	 */
 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
-		if (vp->v_mount->mnt_flag & MNT_RDONLY)
-			return (EROFS);
+		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+			error = EROFS;
+			goto out;
+		}
 		error = ufs_chown(vp, vap->va_uid, vap->va_gid, cred, l);
 		if (error)
-			return (error);
+			goto out;
 	}
 	if (vap->va_size != VNOVAL) {
 		/*
 		 * Disallow write attempts on read-only file systems;
@@ -435,39 +468,49 @@ ufs_setattr(void *v)
 		 * character device resident on the file system.
 		 */
 		switch (vp->v_type) {
 		case VDIR:
-			return (EISDIR);
+			error = EISDIR;
+			goto out;
 		case VCHR:
 		case VBLK:
 		case VFIFO:
 			break;
 		case VREG:
-			if (vp->v_mount->mnt_flag & MNT_RDONLY)
-				 return (EROFS);
-			if ((ip->i_flags & SF_SNAPSHOT) != 0)
-				return (EPERM);
+			if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+				 error = EROFS;
+				 goto out;
+			}
+			if ((ip->i_flags & SF_SNAPSHOT) != 0) {
+				error = EPERM;
+				goto out;
+			}
 			error = UFS_TRUNCATE(vp, vap->va_size, 0, cred, l);
 			if (error)
-				return (error);
+				goto out;
 			break;
 		default:
-			return (EOPNOTSUPP);
+			error = EOPNOTSUPP;
+			goto out;
 		}
 	}
 	ip = VTOI(vp);
 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
 	    vap->va_birthtime.tv_sec != VNOVAL) {
-		if (vp->v_mount->mnt_flag & MNT_RDONLY)
-			return (EROFS);
-		if ((ip->i_flags & SF_SNAPSHOT) != 0)
-			return (EPERM);
+		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+			error = EROFS;
+			goto out;
+		}
+		if ((ip->i_flags & SF_SNAPSHOT) != 0) {
+			error = EPERM;
+			goto out;
+		}
 		if (kauth_cred_geteuid(cred) != ip->i_uid &&
 		    (error = kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
 		    &l->l_acflag)) &&
 		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
 		    (error = VOP_ACCESS(vp, VWRITE, cred, l))))
-			return (error);
+			goto out;
 		if (vap->va_atime.tv_sec != VNOVAL)
 			if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
 				ip->i_flag |= IN_ACCESS;
 		if (vap->va_mtime.tv_sec != VNOVAL)
@@ -478,21 +521,27 @@ ufs_setattr(void *v)
 			ip->i_ffs2_birthnsec = vap->va_birthtime.tv_nsec;
 		}
 		error = UFS_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 0);
 		if (error)
-			return (error);
+			goto out;
 	}
 	error = 0;
 	if (vap->va_mode != (mode_t)VNOVAL) {
-		if (vp->v_mount->mnt_flag & MNT_RDONLY)
-			return (EROFS);
+		if (vp->v_mount->mnt_flag & MNT_RDONLY) {
+			error = EROFS;
+			goto out;
+		}
 		if ((ip->i_flags & SF_SNAPSHOT) != 0 &&
 		    (vap->va_mode & (S_IXUSR | S_IWUSR | S_IXGRP | S_IWGRP |
-		     S_IXOTH | S_IWOTH)))
-			return (EPERM);
+		     S_IXOTH | S_IWOTH))) {
+			error = EPERM;
+			goto out;
+		}
 		error = ufs_chmod(vp, (int)vap->va_mode, cred, l);
 	}
 	VN_KNOTE(vp, NOTE_ATTRIB);
+out:
+	fstrans_done(vp->v_mount);
 	return (error);
 }
 
 /*
@@ -648,8 +697,10 @@ ufs_remove(void *v)
 
 	vp = ap->a_vp;
 	dvp = ap->a_dvp;
 	ip = VTOI(vp);
+	if ((error = fstrans_start(dvp->v_mount, fstrans_shared)) != 0)
+		return error;
 	if (vp->v_type == VDIR || (ip->i_flags & (IMMUTABLE | APPEND)) ||
 	    (VTOI(dvp)->i_flags & APPEND))
 		error = EPERM;
 	else
@@ -660,8 +711,9 @@ ufs_remove(void *v)
 		vrele(vp);
 	else
 		vput(vp);
 	vput(dvp);
+	fstrans_done(dvp->v_mount);
 	return (error);
 }
 
 /*
@@ -687,8 +739,10 @@ ufs_link(void *v)
 #ifdef DIAGNOSTIC
 	if ((cnp->cn_flags & HASBUF) == 0)
 		panic("ufs_link: no name");
 #endif
+	if ((error = fstrans_start(dvp->v_mount, fstrans_shared)) != 0)
+		return error;
 	if (vp->v_type == VDIR) {
 		VOP_ABORTOP(dvp, cnp);
 		error = EPERM;
 		goto out2;
@@ -741,8 +795,9 @@ ufs_link(void *v)
  out2:
 	VN_KNOTE(vp, NOTE_LINK);
 	VN_KNOTE(dvp, NOTE_WRITE);
 	vput(dvp);
+	fstrans_done(dvp->v_mount);
 	return (error);
 }
 
 /*
@@ -771,8 +826,10 @@ ufs_whiteout(void *v)
 		return (EOPNOTSUPP);
 
 	case CREATE:
 		/* create a new directory whiteout */
+		if ((error = fstrans_start(dvp->v_mount, fstrans_shared)) != 0)
+			return error;
 #ifdef DIAGNOSTIC
 		if ((cnp->cn_flags & SAVENAME) == 0)
 			panic("ufs_whiteout: missing name");
 		if (ump->um_maxsymlinklen <= 0)
@@ -791,8 +848,10 @@ ufs_whiteout(void *v)
 		break;
 
 	case DELETE:
 		/* remove an existing directory whiteout */
+		if ((error = fstrans_start(dvp->v_mount, fstrans_shared)) != 0)
+			return error;
 #ifdef DIAGNOSTIC
 		if (ump->um_maxsymlinklen <= 0)
 			panic("ufs_whiteout: old format filesystem");
 #endif
@@ -807,8 +866,9 @@ ufs_whiteout(void *v)
 	if (cnp->cn_flags & HASBUF) {
 		PNBUF_PUT(cnp->cn_pnbuf);
 		cnp->cn_flags &= ~HASBUF;
 	}
+	fstrans_done(dvp->v_mount);
 	return (error);
 }
 
 
@@ -849,8 +909,9 @@ ufs_rename(void *v)
 	} */ *ap = v;
 	struct vnode		*tvp, *tdvp, *fvp, *fdvp;
 	struct componentname	*tcnp, *fcnp;
 	struct inode		*ip, *xp, *dp;
+	struct mount		*mp;
 	struct direct		*newdir;
 	int			doingdirectory, oldparent, newparent, error;
 
 	tvp = ap->a_tvp;
@@ -959,8 +1020,12 @@ ufs_rename(void *v)
 	xp = NULL;
 	if (tvp)
 		xp = VTOI(tvp);
 
+	mp = fdvp->v_mount;
+	if ((error = fstrans_start(mp, fstrans_shared)) != 0)
+		return error;
+
 	/*
 	 * 1) Bump link count while we're moving stuff
 	 *    around.  If we crash somewhere before
 	 *    completing our work, the link count
@@ -1162,9 +1227,9 @@ ufs_rename(void *v)
 	vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
 	if ((error = relookup(fdvp, &fvp, fcnp))) {
 		vput(fdvp);
 		vrele(ap->a_fvp);
-		return (error);
+		goto out2;
 	}
 	if (fvp != NULL) {
 		xp = VTOI(fvp);
 		dp = VTOI(fdvp);
@@ -1174,9 +1239,10 @@ ufs_rename(void *v)
 		 */
 		if (doingdirectory)
 			panic("rename: lost dir entry");
 		vrele(ap->a_fvp);
-		return (0);
+		error = 0;
+		goto out2;
 	}
 	/*
 	 * Ensure that the directory entry still exists and has not
 	 * changed while the new name has been entered. If the source is
@@ -1210,9 +1276,9 @@ ufs_rename(void *v)
 		vput(fdvp);
 	if (xp)
 		vput(fvp);
 	vrele(ap->a_fvp);
-	return (error);
+	goto out2;
 
 	/* exit routines from steps 1 & 2 */
  bad:
 	if (xp)
@@ -1232,8 +1298,12 @@ ufs_rename(void *v)
 		vput(fvp);
 	} else
 		vrele(fvp);
 	vrele(fdvp);
+
+	/* exit routines from step 3 */
+ out2:
+	fstrans_done(mp);
 	return (error);
 }
 
 /*
@@ -1258,8 +1328,11 @@ ufs_mkdir(void *v)
 	int			error, dmode, blkoff;
 	struct ufsmount		*ump = dp->i_ump;
 	int			dirblksiz = ump->um_dirblksiz;
 
+	if ((error = fstrans_start(dvp->v_mount, fstrans_shared)) != 0)
+		return error;
+
 #ifdef DIAGNOSTIC
 	if ((cnp->cn_flags & HASBUF) == 0)
 		panic("ufs_mkdir: no name");
 #endif
@@ -1286,8 +1359,9 @@ ufs_mkdir(void *v)
 	if ((error = getinoquota(ip)) ||
 	    (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
 		PNBUF_PUT(cnp->cn_pnbuf);
 		UFS_VFREE(tvp, ip->i_number, dmode);
+		fstrans_done(dvp->v_mount);
 		vput(tvp);
 		vput(dvp);
 		return (error);
 	}
@@ -1415,8 +1489,9 @@ ufs_mkdir(void *v)
 		vput(tvp);
 	}
  out:
 	PNBUF_PUT(cnp->cn_pnbuf);
+	fstrans_done(dvp->v_mount);
 	vput(dvp);
 	return (error);
 }
 
@@ -1451,8 +1526,12 @@ ufs_rmdir(void *v)
 			vput(vp);
 		vput(vp);
 		return (EINVAL);
 	}
+
+	if ((error = fstrans_start(dvp->v_mount, fstrans_shared)) != 0)
+		return error;
+
 	/*
 	 * 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
@@ -1521,8 +1600,9 @@ ufs_rmdir(void *v)
 		ufsdirhash_free(ip);
 #endif
  out:
 	VN_KNOTE(vp, NOTE_DELETE);
+	fstrans_done(dvp->v_mount);
 	vput(dvp);
 	vput(vp);
 	return (error);
 }
@@ -1544,12 +1624,14 @@ ufs_symlink(void *v)
 	struct inode	*ip;
 	int		len, error;
 
 	vpp = ap->a_vpp;
+	if ((error = fstrans_start(ap->a_dvp->v_mount, fstrans_shared)) != 0)
+		return error;
 	error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
 			      vpp, ap->a_cnp);
 	if (error)
-		return (error);
+		goto out;
 	VN_KNOTE(ap->a_dvp, NOTE_WRITE);
 	vp = *vpp;
 	len = strlen(ap->a_target);
 	ip = VTOI(vp);
@@ -1564,8 +1646,10 @@ ufs_symlink(void *v)
 		    UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL,
 		    NULL);
 	if (error)
 		vput(vp);
+out:
+	fstrans_done(ap->a_dvp->v_mount);
 	return (error);
 }
 
 /*
@@ -2241,4 +2325,71 @@ ufs_gop_markupdate(struct vnode *vp, int
 
 		ip->i_flag |= mask;
 	}
 }
+
+/*
+ * Lock the node.
+ */
+int
+ufs_lock(void *v)
+{
+	struct vop_lock_args /* {
+		struct vnode *a_vp;
+		int a_flags;
+	} */ *ap = v;
+	struct vnode *vp = ap->a_vp;
+	struct mount *mp = vp->v_mount;
+
+	/*
+	 * Fake lock during file system suspension.
+	 */
+	if ((vp->v_type == VREG || vp->v_type == VDIR) &&
+	    fstrans_is_owner(mp) &&
+	    fstrans_getstate(mp) == fstrans_suspending) {
+		if ((ap->a_flags & LK_INTERLOCK) != 0)
+			simple_unlock(&vp->v_interlock);
+		return 0;
+	}
+	return (lockmgr(vp->v_vnlock, ap->a_flags, &vp->v_interlock));
+}
+
+/*
+ * Unlock the node.
+ */
+int
+ufs_unlock(void *v)
+{
+	struct vop_unlock_args /* {
+		struct vnode *a_vp;
+		int a_flags;
+	} */ *ap = v;
+	struct vnode *vp = ap->a_vp;
+	struct mount *mp = vp->v_mount;
+
+	/*
+	 * Fake unlock during file system suspension.
+	 */
+	if ((vp->v_type == VREG || vp->v_type == VDIR) &&
+	    fstrans_is_owner(mp) &&
+	    fstrans_getstate(mp) == fstrans_suspending) {
+		if ((ap->a_flags & LK_INTERLOCK) != 0)
+			simple_unlock(&vp->v_interlock);
+		return 0;
+	}
+	return (lockmgr(vp->v_vnlock, ap->a_flags | LK_RELEASE,
+	    &vp->v_interlock));
+}
+
+/*
+ * Return whether or not the node is locked.
+ */
+int
+ufs_islocked(void *v)
+{
+	struct vop_islocked_args /* {
+		struct vnode *a_vp;
+	} */ *ap = v;
+	struct vnode *vp = ap->a_vp;
+
+	return (lockstatus(vp->v_vnlock));
+}
Index: sys/ufs/ffs/ffs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vfsops.c,v
retrieving revision 1.190
diff -p -u -4 -r1.190 ffs_vfsops.c
--- sys/ufs/ffs/ffs_vfsops.c	16 Nov 2006 01:33:53 -0000	1.190
+++ sys/ufs/ffs/ffs_vfsops.c	29 Dec 2006 11:20:59 -0000
@@ -60,8 +60,9 @@ __KERNEL_RCSID(0, "$NetBSD: ffs_vfsops.c
 #include <sys/lock.h>
 #include <sys/sysctl.h>
 #include <sys/conf.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/specfs/specdev.h>
 
 #include <ufs/ufs/quota.h>
@@ -107,8 +108,9 @@ struct vfsops ffs_vfsops = {
 	ffs_done,
 	ffs_mountroot,
 	ffs_snapshot,
 	ffs_extattrctl,
+	ffs_suspendctl,
 	ffs_vnodeopv_descs,
 	0,
 	{ NULL, NULL },
 };
@@ -972,8 +974,11 @@ ffs_mountfs(struct vnode *devvp, struct 
 	}
 	mp->mnt_fs_bshift = fs->fs_bshift;
 	mp->mnt_dev_bshift = DEV_BSHIFT;	/* XXX */
 	mp->mnt_flag |= MNT_LOCAL;
+#ifdef NEWVNGATE
+	mp->mnt_iflag |= IMNT_HAS_TRANS;
+#endif
 #ifdef FFS_EI
 	if (needswap)
 		ump->um_flags |= UFS_NEEDSWAP;
 #endif
@@ -1312,8 +1317,10 @@ ffs_sync(struct mount *mp, int waitfor, 
 	if (fs->fs_fmod != 0 && fs->fs_ronly != 0) {		/* XXX */
 		printf("fs = %s\n", fs->fs_fsmnt);
 		panic("update: rofs mod");
 	}
+	if ((error = fstrans_start(mp, fstrans_shared)) != 0)
+		return error;
 	/*
 	 * Write back each (modified) inode.
 	 */
 	simple_lock(&mntvnode_slock);
@@ -1340,8 +1347,13 @@ loop:
 		{
 			simple_unlock(&vp->v_interlock);
 			continue;
 		}
+		if (vp->v_type == VBLK &&
+		    fstrans_getstate(mp) == fstrans_suspending) {
+			simple_unlock(&vp->v_interlock);
+			continue;
+		}
 		simple_unlock(&mntvnode_slock);
 		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK);
 		if (error) {
 			simple_lock(&mntvnode_slock);
@@ -1395,8 +1407,9 @@ loop:
 		fs->fs_time = time_second;
 		if ((error = ffs_cgupdate(ump, waitfor)))
 			allerror = error;
 	}
+	fstrans_done(mp);
 	return (allerror);
 }
 
 /*
@@ -1759,4 +1772,31 @@ ffs_extattrctl(struct mount *mp, int cmd
 				       l));
 #endif
 	return (vfs_stdextattrctl(mp, cmd, vp, attrnamespace, attrname, l));
 }
+
+int
+ffs_suspendctl(struct mount *mp, int cmd)
+{
+	int error;
+	struct lwp *l = curlwp;
+
+	switch (cmd) {
+	case SUSPEND_SUSPEND:
+		if ((error = fstrans_setstate(mp, fstrans_suspending)) != 0)
+			return error;
+		error = ffs_sync(mp, MNT_WAIT, l->l_proc->p_cred, l);
+		if (error == 0)
+			error = fstrans_setstate(mp, fstrans_suspended);
+		if (error != 0) {
+			(void) fstrans_setstate(mp, fstrans_normal);
+			return error;
+		}
+		return 0;
+
+	case SUSPEND_RESUME:
+		return fstrans_setstate(mp, fstrans_normal);
+
+	default:
+		return EINVAL;
+	}
+}
Index: sys/ufs/ffs/ffs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_extern.h,v
retrieving revision 1.54
diff -p -u -4 -r1.54 ffs_extern.h
--- sys/ufs/ffs/ffs_extern.h	13 Jul 2006 12:00:26 -0000	1.54
+++ sys/ufs/ffs/ffs_extern.h	29 Dec 2006 11:20:59 -0000
@@ -124,8 +124,9 @@ int	ffs_vget(struct mount *, ino_t, stru
 int	ffs_fhtovp(struct mount *, struct fid *, struct vnode **);
 int	ffs_vptofh(struct vnode *, struct fid *, size_t *);
 int	ffs_extattrctl(struct mount *, int, struct vnode *, int,
 		       const char *, struct lwp *);
+int	ffs_suspendctl(struct mount *, int);
 int	ffs_sbupdate(struct ufsmount *, int);
 int	ffs_cgupdate(struct ufsmount *, int);
 
 /* ffs_vnops.c */
Index: sys/ufs/ffs/ffs_snapshot.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_snapshot.c,v
retrieving revision 1.38
diff -p -u -4 -r1.38 ffs_snapshot.c
--- sys/ufs/ffs/ffs_snapshot.c	2 Dec 2006 17:21:11 -0000	1.38
+++ sys/ufs/ffs/ffs_snapshot.c	29 Dec 2006 11:20:59 -0000
@@ -59,8 +59,9 @@ __KERNEL_RCSID(0, "$NetBSD: ffs_snapshot
 #include <sys/resource.h>
 #include <sys/resourcevar.h>
 #include <sys/vnode.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/specfs/specdev.h>
 
 #include <ufs/ufs/quota.h>
@@ -290,9 +291,9 @@ ffs_snapshot(struct mount *mp, struct vn
 	 * All allocations are done, so we can now snapshot the system.
 	 *
 	 * Suspend operation on filesystem.
 	 */
-	if ((error = vfs_write_suspend(vp->v_mount, PUSER|PCATCH, 0)) != 0) {
+	if ((error = vfs_suspend(vp->v_mount, 0)) != 0) {
 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
 		goto out;
 	}
 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
@@ -388,25 +389,33 @@ loop:
 			VI_UNLOCK(xvp);
 			MNT_ILOCK(mp);
 			continue;
 		}
+#ifndef NEWVNGATE
 		if (vn_lock(xvp, LK_EXCLUSIVE | LK_INTERLOCK) != 0) {
 			MNT_ILOCK(mp);
 			goto loop;
 		}
+#else /* NEWVNGATE */
+		VI_UNLOCK(xvp);
+#endif /* NEWVNGATE */
 #ifdef DEBUG
 		if (snapdebug)
 			vprint("ffs_snapshot: busy vnode", xvp);
 #endif
 		if (VOP_GETATTR(xvp, &vat, l->l_cred, l) == 0 &&
 		    vat.va_nlink > 0) {
+#ifndef NEWVNGATE
 			VOP_UNLOCK(xvp, 0);
+#endif /* NEWVNGATE */
 			MNT_ILOCK(mp);
 			continue;
 		}
 		xp = VTOI(xvp);
 		if (ffs_checkfreefile(copy_fs, vp, xp->i_number)) {
+#ifndef NEWVNGATE
 			VOP_UNLOCK(xvp, 0);
+#endif /* NEWVNGATE */
 			MNT_ILOCK(mp);
 			continue;
 		}
 		/*
@@ -434,9 +443,11 @@ loop:
 			db_assign(xp, loc, blkno);
 		if (!error)
 			error = ffs_freefile(copy_fs, vp, xp->i_number,
 			    xp->i_mode);
+#ifndef NEWVNGATE
 		VOP_UNLOCK(xvp, 0);
+#endif /* NEWVNGATE */
 		if (error) {
 			free(copy_fs->fs_csp, M_UFSMNT);
 			goto out1;
 		}
@@ -517,9 +528,9 @@ loop:
 out1:
 	/*
 	 * Resume operation on filesystem.
 	 */
-	vfs_write_resume(vp->v_mount);
+	vfs_resume(vp->v_mount);
 	/*
 	 * Set the mtime to the time the snapshot has been taken.
 	 */
 	TIMEVAL_TO_TIMESPEC(&starttime, &ts);
Index: sys/ufs/ffs/ffs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/ffs/ffs_vnops.c,v
retrieving revision 1.83
diff -p -u -4 -r1.83 ffs_vnops.c
--- sys/ufs/ffs/ffs_vnops.c	16 Nov 2006 01:33:53 -0000	1.83
+++ sys/ufs/ffs/ffs_vnops.c	29 Dec 2006 11:20:59 -0000
@@ -47,8 +47,9 @@ __KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,
 #include <sys/vnode.h>
 #include <sys/pool.h>
 #include <sys/signalvar.h>
 #include <sys/kauth.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/fifofs/fifo.h>
 #include <miscfs/genfs/genfs.h>
 #include <miscfs/specfs/specdev.h>
@@ -250,18 +251,22 @@ ffs_fsync(void *v)
 	int bsize;
 	daddr_t blk_high;
 	struct vnode *vp;
 
+	vp = ap->a_vp;
+
+	if ((error = fstrans_start(vp->v_mount, fstrans_lazy)) != 0)
+		return error;
 	/*
 	 * XXX no easy way to sync a range in a file with softdep.
 	 */
-	if ((ap->a_offlo == 0 && ap->a_offhi == 0) || DOINGSOFTDEP(ap->a_vp) ||
-			(ap->a_vp->v_type != VREG))
-		return ffs_full_fsync(v);
-
-	vp = ap->a_vp;
+	if ((ap->a_offlo == 0 && ap->a_offhi == 0) || DOINGSOFTDEP(vp) ||
+	    (vp->v_type != VREG)) {
+		error = ffs_full_fsync(v);
+		goto out;
+	}
 
-	bsize = ap->a_vp->v_mount->mnt_stat.f_iosize;
+	bsize = vp->v_mount->mnt_stat.f_iosize;
 	blk_high = ap->a_offhi / bsize;
 	if (ap->a_offhi % bsize != 0)
 		blk_high++;
 
@@ -273,9 +278,9 @@ ffs_fsync(void *v)
 	error = VOP_PUTPAGES(vp, trunc_page(ap->a_offlo),
 	    round_page(ap->a_offhi), PGO_CLEANIT |
 	    ((ap->a_flags & FSYNC_WAIT) ? PGO_SYNCIO : 0));
 	if (error) {
-		return error;
+		goto out;
 	}
 
 	/*
 	 * Then, flush indirect blocks.
@@ -285,9 +290,9 @@ ffs_fsync(void *v)
 	if (blk_high >= NDADDR) {
 		error = ufs_getlbns(vp, blk_high, ia, &num);
 		if (error) {
 			splx(s);
-			return error;
+			goto out;
 		}
 		for (i = 0; i < num; i++) {
 			bp = incore(vp, ia[i].in_lbn);
 			if (bp != NULL) {
@@ -325,8 +330,10 @@ ffs_fsync(void *v)
 		VOP_IOCTL(VTOI(vp)->i_devvp, DIOCCACHESYNC, &l, FWRITE,
 			ap->a_l->l_cred, ap->a_l);
 	}
 
+out:
+	fstrans_done(vp->v_mount);
 	return error;
 }
 
 /*
@@ -362,9 +369,11 @@ ffs_full_fsync(void *v)
 
 	if (vp->v_type == VREG || vp->v_type == VBLK) {
 		simple_lock(&vp->v_interlock);
 		error = VOP_PUTPAGES(vp, 0, 0, PGO_ALLPAGES | PGO_CLEANIT |
-		    ((ap->a_flags & FSYNC_WAIT) ? PGO_SYNCIO : 0));
+		    ((ap->a_flags & FSYNC_WAIT) ? PGO_SYNCIO : 0) |
+		    (fstrans_getstate(vp->v_mount) == fstrans_suspending ?
+			PGO_FREE : 0));
 		if (error) {
 			return error;
 		}
 	}
@@ -479,13 +488,18 @@ ffs_reclaim(void *v)
 		struct lwp *a_l;
 	} */ *ap = v;
 	struct vnode *vp = ap->a_vp;
 	struct inode *ip = VTOI(vp);
+	struct mount *mp = vp->v_mount;
 	struct ufsmount *ump = ip->i_ump;
 	int error;
 
-	if ((error = ufs_reclaim(vp, ap->a_l)) != 0)
+	if ((error = fstrans_start(mp, fstrans_lazy)) != 0)
+		return error;
+	if ((error = ufs_reclaim(vp, ap->a_l)) != 0) {
+		fstrans_done(mp);
 		return (error);
+	}
 	if (ip->i_din.ffs1_din != NULL) {
 		if (ump->um_fstype == UFS1)
 			pool_put(&ffs_dinode1_pool, ip->i_din.ffs1_din);
 		else
@@ -496,8 +510,9 @@ ffs_reclaim(void *v)
 	 * XXX a separate pool for MFS inodes?
 	 */
 	pool_put(&ffs_inode_pool, vp->v_data);
 	vp->v_data = NULL;
+	fstrans_done(mp);
 	return (0);
 }
 
 int
@@ -606,14 +621,21 @@ ffs_getextattr(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 vnode *vp = ap->a_vp;
+	struct inode *ip = VTOI(vp);
 	struct fs *fs = ip->i_fs;
 
 	if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-		return (ufs_getextattr(ap));
+		int error;
+
+		if ((error = fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+			return error;
+		error = ufs_getextattr(ap);
+		fstrans_done(vp->v_mount);
+		return error;
 #else
 		return (EOPNOTSUPP);
 #endif
 	}
@@ -632,14 +654,21 @@ ffs_setextattr(void *v)
 		struct uio *a_uio;
 		kauth_cred_t a_cred;
 		struct proc *a_p;
 	} */ *ap = v;
-	struct inode *ip = VTOI(ap->a_vp);
+	struct vnode *vp = ap->a_vp;
+	struct inode *ip = VTOI(vp);
 	struct fs *fs = ip->i_fs;
 
 	if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-		return (ufs_setextattr(ap));
+		int error;
+
+		if ((error = fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+			return error;
+		error = ufs_setextattr(ap);
+		fstrans_done(vp->v_mount);
+		return error;
 #else
 		return (EOPNOTSUPP);
 #endif
 	}
@@ -678,14 +707,21 @@ ffs_deleteextattr(void *v)
 		int a_attrnamespace;
 		kauth_cred_t a_cred;
 		struct proc *a_p;
 	} */ *ap = v;
-	struct inode *ip = VTOI(ap->a_vp);
+	struct vnode *vp = ap->a_vp;
+	struct inode *ip = VTOI(vp);
 	struct fs *fs = ip->i_fs;
 
 	if (fs->fs_magic == FS_UFS1_MAGIC) {
 #ifdef UFS_EXTATTR
-		return (ufs_deleteextattr(ap));
+		int error;
+
+		if ((error = fstrans_start(vp->v_mount, fstrans_shared)) != 0)
+			return error;
+		error = ufs_deleteextattr(ap);
+		fstrans_done(vp->v_mount);
+		return error;
 #else
 		return (EOPNOTSUPP);
 #endif
 	}
Index: sys/ufs/lfs/lfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/ufs/lfs/lfs_vnops.c,v
retrieving revision 1.194
diff -p -u -4 -r1.194 lfs_vnops.c
--- sys/ufs/lfs/lfs_vnops.c	9 Dec 2006 16:11:52 -0000	1.194
+++ sys/ufs/lfs/lfs_vnops.c	29 Dec 2006 11:21:00 -0000
@@ -87,8 +87,9 @@ __KERNEL_RCSID(0, "$NetBSD: lfs_vnops.c,
 #include <sys/pool.h>
 #include <sys/signalvar.h>
 #include <sys/kauth.h>
 #include <sys/syslog.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/fifofs/fifo.h>
 #include <miscfs/genfs/genfs.h>
 #include <miscfs/specfs/specdev.h>
Index: sys/kern/init_main.c
===================================================================
RCS file: /cvsroot/src/sys/kern/init_main.c,v
retrieving revision 1.286
diff -p -u -4 -r1.286 init_main.c
--- sys/kern/init_main.c	21 Dec 2006 15:55:25 -0000	1.286
+++ sys/kern/init_main.c	29 Dec 2006 11:20:56 -0000
@@ -101,8 +101,9 @@ __KERNEL_RCSID(0, "$NetBSD: init_main.c,
 #include <sys/resourcevar.h>
 #include <sys/signalvar.h>
 #include <sys/systm.h>
 #include <sys/vnode.h>
+#include <sys/fstrans.h>
 #include <sys/tty.h>
 #include <sys/conf.h>
 #include <sys/disklabel.h>
 #include <sys/buf.h>
@@ -312,8 +313,10 @@ main(void)
 		desiredvnodes = usevnodes;
 #endif
 	vfsinit();
 
+	/* Initialize fstrans. */
+	fstrans_init();
 
 #ifdef __HAVE_TIMECOUNTER
 	inittimecounter();
 	ntp_init();
Index: sys/kern/vfs_subr.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_subr.c,v
retrieving revision 1.277
diff -p -u -4 -r1.277 vfs_subr.c
--- sys/kern/vfs_subr.c	27 Dec 2006 12:22:14 -0000	1.277
+++ sys/kern/vfs_subr.c	29 Dec 2006 11:20:56 -0000
@@ -2447,62 +2447,8 @@ vfs_reinit(void)
 		}
 	}
 }
 
-/*
- * Request a filesystem to suspend write operations.
- */
-int
-vfs_write_suspend(struct mount *mp, int slpflag, int slptimeo)
-{
-	struct lwp *l = curlwp;	/* XXX */
-	int error;
-
-	while ((mp->mnt_iflag & IMNT_SUSPEND)) {
-		if (slptimeo < 0)
-			return EWOULDBLOCK;
-		error = tsleep(&mp->mnt_flag, slpflag, "suspwt1", slptimeo);
-		if (error)
-			return error;
-	}
-	mp->mnt_iflag |= IMNT_SUSPEND;
-
-	simple_lock(&mp->mnt_slock);
-	if (mp->mnt_writeopcountupper > 0)
-		ltsleep(&mp->mnt_writeopcountupper, PUSER - 1, "suspwt",
-			0, &mp->mnt_slock);
-	simple_unlock(&mp->mnt_slock);
-
-	error = VFS_SYNC(mp, MNT_WAIT, l->l_cred, l);
-	if (error) {
-		vfs_write_resume(mp);
-		return error;
-	}
-	mp->mnt_iflag |= IMNT_SUSPENDLOW;
-
-	simple_lock(&mp->mnt_slock);
-	if (mp->mnt_writeopcountlower > 0)
-		ltsleep(&mp->mnt_writeopcountlower, PUSER - 1, "suspwt",
-			0, &mp->mnt_slock);
-	mp->mnt_iflag |= IMNT_SUSPENDED;
-	simple_unlock(&mp->mnt_slock);
-
-	return 0;
-}
-
-/*
- * Request a filesystem to resume write operations.
- */
-void
-vfs_write_resume(struct mount *mp)
-{
-
-	if ((mp->mnt_iflag & IMNT_SUSPEND) == 0)
-		return;
-	mp->mnt_iflag &= ~(IMNT_SUSPEND | IMNT_SUSPENDLOW | IMNT_SUSPENDED);
-	wakeup(&mp->mnt_flag);
-}
-
 void
 copy_statvfs_info(struct statvfs *sbp, const struct mount *mp)
 {
 	const struct statvfs *mbp;
Index: sys/kern/vfs_syscalls.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.287
diff -p -u -4 -r1.287 vfs_syscalls.c
--- sys/kern/vfs_syscalls.c	27 Dec 2006 08:55:35 -0000	1.287
+++ sys/kern/vfs_syscalls.c	29 Dec 2006 11:20:57 -0000
@@ -695,17 +695,17 @@ dounmount(struct mount *mp, int flags, s
 		simple_unlock(&mp->mnt_slock);
 		return (error);
 	}
 	CIRCLEQ_REMOVE(&mountlist, mp, mnt_list);
-	if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) {
+	if ((coveredvp = mp->mnt_vnodecovered) != NULLVP)
 		coveredvp->v_mountedhere = NULL;
-		vrele(coveredvp);
-	}
 	mp->mnt_op->vfs_refcount--;
 	if (TAILQ_FIRST(&mp->mnt_vnodelist) != NULL)
 		panic("unmount: dangling vnode");
 	mp->mnt_iflag |= IMNT_GONE;
 	lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock);
+	if (coveredvp != NULLVP)
+		vrele(coveredvp);
 	mount_finispecific(mp);
 	if (used_syncer)
 		lockmgr(&syncer_lock, LK_RELEASE, NULL);
 	simple_lock(&mp->mnt_slock);
Index: sys/kern/vfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_vnops.c,v
retrieving revision 1.129
diff -p -u -4 -r1.129 vfs_vnops.c
--- sys/kern/vfs_vnops.c	30 Nov 2006 01:09:47 -0000	1.129
+++ sys/kern/vfs_vnops.c	29 Dec 2006 11:20:57 -0000
@@ -994,8 +994,10 @@ vn_start_write(struct vnode *vp, struct 
 	}
 	if ((mp = *mpp) == NULL)
 		return (0);
 	mp = mp->mnt_leaf;
+	if ((mp->mnt_iflag & IMNT_HAS_TRANS) != 0)
+		return 0;
 	/*
 	 * Check on status of suspension.
 	 */
 	prio = PUSER - 1;
@@ -1035,8 +1037,10 @@ vn_finished_write(struct mount *mp, int 
 {
 	if (mp == NULL)
 		return;
 	mp = mp->mnt_leaf;
+	if ((mp->mnt_iflag & IMNT_HAS_TRANS) != 0)
+		return;
 	simple_lock(&mp->mnt_slock);
 	if ((flags & V_LOWER) == 0) {
 		mp->mnt_writeopcountupper--;
 		if (mp->mnt_writeopcountupper < 0)
Index: sys/conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.821
diff -p -u -4 -r1.821 files
--- sys/conf/files	17 Dec 2006 22:57:15 -0000	1.821
+++ sys/conf/files	29 Dec 2006 11:20:54 -0000
@@ -1369,8 +1369,9 @@ file	kern/vfs_init.c
 file	kern/vfs_lockf.c
 file	kern/vfs_lookup.c
 file	kern/vfs_subr.c
 file	kern/vfs_syscalls.c
+file	kern/vfs_trans.c
 file	kern/vfs_vnops.c
 file	kern/vfs_xattr.c
 file	kern/vnode_if.c
 file	miscfs/deadfs/dead_vnops.c
Index: sys/dev/fss.c
===================================================================
RCS file: /cvsroot/src/sys/dev/fss.c,v
retrieving revision 1.29
diff -p -u -4 -r1.29 fss.c
--- sys/dev/fss.c	16 Nov 2006 01:32:45 -0000	1.29
+++ sys/dev/fss.c	29 Dec 2006 11:20:54 -0000
@@ -64,8 +64,9 @@ __KERNEL_RCSID(0, "$NetBSD: fss.c,v 1.29
 #include <sys/file.h>
 #include <sys/uio.h>
 #include <sys/conf.h>
 #include <sys/kthread.h>
+#include <sys/fstrans.h>
 
 #include <miscfs/specfs/specdev.h>
 
 #include <dev/fssvar.h>
@@ -740,9 +741,9 @@ fss_create_snapshot(struct fss_softc *sc
 	/*
 	 * Activate the snapshot.
 	 */
 
-	if ((error = vfs_write_suspend(sc->sc_mount, PUSER|PCATCH, 0)) != 0)
+	if ((error = vfs_suspend(sc->sc_mount, 0)) != 0)
 		goto bad;
 
 	microtime(&sc->sc_time);
 
@@ -751,9 +752,9 @@ fss_create_snapshot(struct fss_softc *sc
 		    fss_copy_on_write, sc);
 	if (error == 0)
 		sc->sc_flags |= FSS_ACTIVE;
 
-	vfs_write_resume(sc->sc_mount);
+	vfs_resume(sc->sc_mount);
 
 	if (error != 0)
 		goto bad;
 
@@ -1084,9 +1085,11 @@ fss_bs_thread(void *arg)
 			if (error) {
 				bp->b_error = error;
 				bp->b_flags |= B_ERROR;
 				bp->b_resid = bp->b_bcount;
-			}
+			} else
+				bp->b_resid = 0;
+
 			biodone(bp);
 
 			continue;
 		}
Index: sys/fs/sysvbfs/sysvbfs.c
===================================================================
RCS file: /cvsroot/src/sys/fs/sysvbfs/sysvbfs.c,v
retrieving revision 1.3
diff -p -u -4 -r1.3 sysvbfs.c
--- sys/fs/sysvbfs/sysvbfs.c	9 Dec 2006 16:11:51 -0000	1.3
+++ sys/fs/sysvbfs/sysvbfs.c	29 Dec 2006 11:20:55 -0000
@@ -131,8 +131,9 @@ struct vfsops sysvbfs_vfsops = {
 	NULL,			/* vfs_mountroot */
 	(int (*)(struct mount *, struct vnode *, struct timespec *))
 	    eopnotsupp,		/* snapshot */
 	vfs_stdextattrctl,
+	vfs_stdsuspendctl,
 	sysvbfs_vnodeopv_descs,
 	0,
 	{ NULL, NULL }
 };
====
The same diff (addition of vfs_stdsuspendctl) applies to:
  sys/ufs/ext2fs/ext2fs_vfsops.c
  sys/ufs/lfs/lfs_vfsops.c
  sys/ufs/mfs/mfs_vfsops.c
  sys/coda/coda_vfsops.c
  sys/fs/adosfs/advfsops.c
  sys/fs/cd9660/cd9660_vfsops.c
  sys/fs/filecorefs/filecore_vfsops.c
  sys/fs/msdosfs/msdosfs_vfsops.c
  sys/fs/ntfs/ntfs_vfsops.c
  sys/fs/ptyfs/ptyfs_vfsops.c
  sys/fs/smbfs/smbfs_vfsops.c
  sys/fs/tmpfs/tmpfs_vfsops.c
  sys/fs/udf/udf_vfsops.c
  sys/fs/union/union_vfsops.c
  sys/miscfs/fdesc/fdesc_vfsops.c
  sys/miscfs/kernfs/kernfs_vfsops.c
  sys/miscfs/nullfs/null_vfsops.c
  sys/miscfs/overlay/overlay_vfsops.c
  sys/miscfs/portal/portal_vfsops.c
  sys/miscfs/procfs/procfs_vfsops.c
  sys/miscfs/umapfs/umap_vfsops.c
  sys/nfs/nfs_vfsops.c

--82I3+IH0IqGh5yIs--