*BSD News Article 79623


Return to BSD News archive

Newsgroups: comp.bugs.2bsd
Path: euryale.cc.adfa.oz.au!newshost.carno.net.au!harbinger.cc.monash.edu.au!nntp.coast.net!howland.erols.net!cs.utexas.edu!news.sprintlink.net!news-peer.sprintlink.net!news.mathworks.com!uunet!in3.uu.net!news.new-york.net!wlbr!moe.2bsd.com!sms
From: sms@moe.2bsd.com (Steven M. Schultz)
Subject: 'mount -o async' kernel support (finally) implemented (#331)
Organization: 2BSD, Simi Valley CA USA
Message-ID: <DyKxIp.LLB@moe.2bsd.com>
Date: Tue, 1 Oct 1996 04:23:13 GMT
Lines: 1847

	NOTE:  Apply #332 before recompiling the kernel.  A bug was fixed
	       after this kit had already been created.

Subject: 'mount -o async' kernel support (finally) implemented (#331)
Index:	sys/sys/<many> 2.11BSD

Description:
	The 'async' option of mount(8) was a NOP.  While the option would
	be parsed and the flag passed to the kernel there was no code in
	the kernel which paid attention to it.

	Specifying the O_FSYNC flag to open(2) did not have any effect.

Repeat-By:
	Observation.  Mention was made earlier (in the updates which dealt
	with fstab(5), mount(8)) that the 'MNT_ASYNC' (-o async) flag was
	set/cleared but not used.

	Another method is to time the creation and removal of a directory
	tree.  With 'async' updates enabled the elapsed time for operations
	such as copying, removing or creating directory hierarchies is
	dramatically reduced.

Fix:
	NOTE:  the "-o async" option to mount(8) should *NEVER* be used
	       on any filesystem you are not prepared to reload if the
	       system should crash or suffer a power failure.  Filesystems
	       using async updates *may* be damaged/corrupted beyond
	       fsck(8)'s ability to repair if critical filesystem updates
	       are pending when the power goes out.

	Async updates do not permanently defer inode/superblock updates.  The
	updates are only delayed until the next sync(2) system call is done
	(or the filesystem is unmounted).  Update(8) issues a sync(2) every
	30 seconds.

	When would 'async' updates be useful?  /tmp being on a partition of
	its own is one case.   This is what the fstab(5) entry would look
	like:

/dev/ra0e	/tmp	ufs	rw,async	1	3

	Another *real good* use of '-o async' is when copying or removing
	large directory hierarchies.  The common method of doing this is
	with a tar pipeline:

		(cd /foo; tar -cf - .) | (cd /bar; tar -xpf -)

	In this case you would want to turn on the 'async' flag before
	doing the copy and then turn off the 'async' flag when the copy
	is done.  This is possible using the 'update' option to mount(8):

		mount -u -o async /bar
		(cd /foo; tar -cf - .) | (cd /bar; tar -xpf -)
		mount -u -o noasync /bar

	Removing deep directory trees is another good time to use async
	updates.  If, for example you have a scratch copy of /usr stored
	in /bar as /bar/usr and now it is time to delete /bar/usr:

		mount -u -o async /bar
		rm -r /bar/usr
		mount -u -o noasync /bar

	The changes to the kernel are moderately large.  Many of the
	modifications are small but there are numerous places that needed
	changing.  A couple kernel functions take a different number of
	arguments now,  itrunc() takes 3 args rather than two for example.
	
	A couple kernel function names have changed.  The function which
	allocates a block of diskspace is now called 'balloc' rather than
	simply 'alloc'.  This is, I believe more meaningful as it describes
	what is being allocated.

	The largest single change was in the 'sync' handling.   The 
	kernel function which handles the sync call is now called 'sync()'
	rather than 'update()'.  It always struck me as silly to call
	the routine update() but then put in a comment saying it's effectively
	'sync()'.
	
	The inner loop from sync() was moved to a new function 'ufs_sync' 
	so that a single filesystem can be sync'd.   The 'unmount' processing
	now calls 'ufs_sync()' rather than 'update()' - this way only the
	filesystem being unmounted gets sync'd rather than all filesystems.

	The O_FSYNC flag to open(2) now turns on the IO_SYNC flag which
	is passed to the lower level block I/O routines.  Applications can
	now request synchronous I/O on a per file basis.

	NOTE:  If the system administrator turns on the 'async' mount flag
	       that overrides O_FSYNC.

	How effective are 'async updates'?  That depends in part on the
	type of disk drive being used.  Older, slower drives (such as the
	RD54) show a *dramatic* decrease (50% in some cases) in elapsed time
	for removing many files from directories.  Newer, faster drives
	(which often include a cache in the drive) show a less dramatic
	results but the decrease in elapsed time is still in the realm  of
	20%.

	There were two simple tests used to demonstrate the effectiveness
	of async updates.  The first creates a large directory hierarchy
	using "mkdir -p".

	find /usr -type d -print | sed -e "s,^,mkdir -p /mnt," > /tmp/dir

	First we mount the scratch filesystem normally (no async updates):

	mount /dev/ra8g /mnt

	Then run the two timed tests:

	time sh /tmp/dir
	time rm -r /mnt/usr

	Using an RD54 the result was:

	sh /tmp/dir: 33.8u  149.4s  6:24  47%  1539+4462io
	     rm -r:   1.7u   53.8s  3:17  28%  797+2270io

	Next update the mount flags, turning on the 'async update' mode:

	mount -u -o async /mnt

	time sh /tmp/dir
	time rm -r /mnt/usr

	sh /tmp/dir: 33.3u  105.5s  2:51  81%  300+2148io
	     rm -r:   2.1u   48.7s  1:21  62%  788+801io

	Quite a difference, eh?  From 6min24sec to 2min51sec when creating
	the directory hierarchy and from 3min17sec to 1min21sec when removing
	it.

	How does performing 'async updates' cause such a marked improvement?
	When a directory is updated the kernel normally writes out the modified
	block of the directory synchronously.  If multiple files/directories
	are being created then the modified directory block(s) get written
	out for each file created - if a directory is populated with 10 files
	then the directory is updated 10 times.  By performing the filesystem
	updates 'asynchronously' (letting 'update' do the "sync" once every
	30 seconds) the modified directory block(s) are left in the buffer
	cache and _not_ written out multiple times.

	Async updates also defer writing out modified inodes until a 'sync'
	is performed.  Thus if a file is accessed repeatedly (causing the
	timestamps in the inode to be updated) the inode is not written out
	to disk multiple times.

	Oh, for more modern disks which have a lower seek time as well as
	an onboard cache the decrease in elapsed times is closer to 20%
	rather than 50 or 60%.

	For an HP3724S (1.2GB, 9.5ms seek, 512kb cache) the numbers from 
	the tests above are:

	no async
	--------
	sh /tmp/dir:  33.0u  112.6s  3:04  78%  719+4450io
	     rm -r:    2.4u   52.8s  1:24  65%  790+2258io

	async
	-----
	sh /tmp/dir:  33.8u  104.9s  2:35  89%  315+2140io
	     rm -r:    2.7u   48.6s  1:08  75%  790+804io

	And with out further ado here is how to install the update.

	NOTE: you may or may not have to adjust the kernel overlay 
	      configuration.  The changes in object sizes are slight but
	      may be enough, as they were in the case of the GENERIC kernel,
	      to cause overlays or the base segment to be too large.

	      The patch below adjusts the GENERIC Makefile.  Other kernel 
	      config files may need to be edited manually.

	Cut where indicated, saving to a file (/tmp/331).  Then:

		cd /tmp
		patch -p0 < 331
		cd /sys/YOUR_KERNEL
		make clean
		make
		mv /unix /ounix
		mv /netnix /onetnix
		mv unix netnix /
		chmod 744 /unix /netnix
		reboot

	It is a good idea to have a GENERIC kernel around.  I keep a copy
	in /genunix.

		cd /sys/GENERIC
		make clean
		make 
		mv unix /genunix
		make clean

	This and previous updates, as always, are available via anonyous
	FTP to either FTP.IIPO.GTEGSC.COM or MOE.2BSD.COM in the directory
	/put/2.11BSD.

----------------------------cut here------------------------
*** /usr/src/sys/h/buf.h.old	Sun Jun  9 20:07:10 1996
--- /usr/src/sys/h/buf.h	Sat Sep 14 20:38:35 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)buf.h	1.3 (2.11BSD GTE) 1996/6/9
   */
  
  /*
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)buf.h	1.4 (2.11BSD GTE) 1996/9/13
   */
  
  /*
***************
*** 68,73 ****
--- 68,77 ----
  #define	BQ_AGE		2		/* rubbish */
  #define	BQ_EMPTY	3		/* buffer headers with no memory */
  
+ /* Flags to low-level allocation routines. */
+ #define B_CLRBUF	0x01	/* Request allocated buffer be cleared. */
+ #define B_SYNC		0x02	/* Do all allocations synchronously. */
+ 
  #define	bawrite(bp)	{(bp)->b_flags |= B_ASYNC; bwrite(bp);}
  #define	bfree(bp)	(bp)->b_bcount = 0
  #define	bftopaddr(bp)	((u_int)(bp)->b_un.b_addr >> 6 | (bp)->b_xmem << 10)
***************
*** 80,86 ****
  extern struct	bufhd bufhash[];	/* heads of hash lists */
  extern struct	buf bfreelist[];	/* heads of available lists */
  
! struct	buf *alloc();
  struct	buf *getblk();
  struct	buf *geteblk();
  struct	buf *getnewbuf();
--- 84,90 ----
  extern struct	bufhd bufhash[];	/* heads of hash lists */
  extern struct	buf bfreelist[];	/* heads of available lists */
  
! struct	buf *balloc();
  struct	buf *getblk();
  struct	buf *geteblk();
  struct	buf *getnewbuf();
*** /usr/src/sys/pdp/machdep2.c.old	Sun Dec 24 12:39:33 1995
--- /usr/src/sys/pdp/machdep2.c	Sat Sep 14 21:28:49 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)machdep2.c	2.6 (2.11BSD GTE) 1995/12/24
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)machdep2.c	2.7 (2.11BSD GTE) 1996/9/14
   */
  
  #include "param.h"
***************
*** 398,404 ****
  		 * Release inodes held by texts before update.
  		 */
  		xumount(NODEV);
! 		update();
  		{ register struct buf *bp;
  		  int iter, nbusy;
  
--- 398,404 ----
  		 * Release inodes held by texts before update.
  		 */
  		xumount(NODEV);
! 		sync();
  		{ register struct buf *bp;
  		  int iter, nbusy;
  
*** /usr/src/sys/sys/kern_acct.c.old	Sat Nov 26 23:08:26 1994
--- /usr/src/sys/sys/kern_acct.c	Mon Sep 16 21:09:07 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)kern_acct.c	2.2 (2.11BSD) 11/26/94
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)kern_acct.c	2.3 (2.11BSD) 1996/9/13
   */
  
  #include "param.h"
***************
*** 136,142 ****
  	u.u_error = rdwri(UIO_WRITE, ip, ap, sizeof(acctbuf), siz,
  			UIO_SYSSPACE, IO_UNIT|IO_APPEND, (int *)0);
  	if (u.u_error)
! 		itrunc(ip, (u_long)siz);
  	iunlock(ip);
  }
  
--- 136,142 ----
  	u.u_error = rdwri(UIO_WRITE, ip, ap, sizeof(acctbuf), siz,
  			UIO_SYSSPACE, IO_UNIT|IO_APPEND, (int *)0);
  	if (u.u_error)
! 		itrunc(ip, (u_long)siz, 0);
  	iunlock(ip);
  }
  
*** /usr/src/sys/sys/kern_sig.c.old	Sat Nov 26 23:16:37 1994
--- /usr/src/sys/sys/kern_sig.c	Mon Sep 16 21:06:27 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)kern_sig.c	1.5 (2.11BSD GTE) 11/26/94
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)kern_sig.c	1.6 (2.11BSD GTE) 1996/9/13
   */
  
  #include "param.h"
***************
*** 838,844 ****
  		u.u_error = EFAULT;
  		goto out;
  	}
! 	itrunc(ip, (u_long)0);
  	u.u_acflag |= ACORE;
  	u.u_error = rdwri(UIO_WRITE, ip, &u, ctob(USIZE), (off_t)0,
  			UIO_SYSSPACE, IO_UNIT, (int *)0);
--- 838,844 ----
  		u.u_error = EFAULT;
  		goto out;
  	}
! 	itrunc(ip, (u_long)0, 0);
  	u.u_acflag |= ACORE;
  	u.u_error = rdwri(UIO_WRITE, ip, &u, ctob(USIZE), (off_t)0,
  			UIO_SYSSPACE, IO_UNIT, (int *)0);
*** /usr/src/sys/sys/sys_inode.c.old	Sat Mar  2 00:45:38 1996
--- /usr/src/sys/sys/sys_inode.c	Thu Sep 19 20:57:44 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)sys_inode.c	1.7 (2.11BSD GTE) 1996/3/2
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)sys_inode.c	1.8 (2.11BSD GTE) 1996/9/19
   */
  
  #include "param.h"
***************
*** 56,61 ****
--- 56,64 ----
  			ioflag |= IO_APPEND;
  		if	(fp->f_flag & FNONBLOCK)
  			ioflag |= IO_NDELAY;
+ 		if	(fp->f_flag & FFSYNC ||
+ 			 (ip->i_fs->fs_flags & MNT_SYNCHRONOUS))
+ 			ioflag |= IO_SYNC;
  		error = rwip(ip, uio, ioflag);
  		if	(ioflag & IO_APPEND)
  			fp->f_offset = uio->uio_offset;
***************
*** 109,114 ****
--- 112,118 ----
  	daddr_t lbn, bn;
  	int n, on, type, resid;
  	int error = 0;
+ 	int flags;
  
  #ifdef	DIAGNOSTIC
  	if (uio->uio_rw != UIO_READ && uio->uio_rw != UIO_WRITE)
***************
*** 152,157 ****
--- 156,176 ----
  		}
  	   }
  
+ /*
+  * The IO_SYNC flag is turned off here if the 'async' mount flag is on.
+  * Otherwise directory I/O (which is done by the kernel) would still 
+  * synchronous (because the kernel carefully passes IO_SYNC for all directory
+  * I/O) even if the fs was mounted with "-o async".  
+  *
+  * A side effect of this is that if the system administrator mounts a filesystem
+  * 'async' then the O_FSYNC flag to open() is ignored.
+  *
+  * This behaviour should probably be selectable via "sysctl fs.async.dirs" and
+  * "fs.async.ofsync".  A project for a rainy day.
+ */
+ 	if (type == IFREG  || type == IFDIR && (ip->i_fs->fs_flags & MNT_ASYNC))
+ 		ioflag &= ~IO_SYNC;
+ 
  	if (type == IFCHR) {
  		if (uio->uio_rw == UIO_READ)
  			error = (*cdevsw[major(dev)].d_read)(dev, uio, ioflag);
***************
*** 194,199 ****
--- 213,220 ----
  	resid = uio->uio_resid;
  	osize = ip->i_size;
  
+ 	flags = ioflag & IO_SYNC ? B_SYNC : 0;
+ 
  	do {
  		lbn = lblkno(uio->uio_offset);
  		on = blkoff(uio->uio_offset);
***************
*** 205,214 ****
  					return (0);
  				if (diff < n)
  					n = diff;
! 			bn = bmap(ip, lbn, B_READ, 0);
  			}
  			else
! 				bn = bmap(ip,lbn,B_WRITE,n == DEV_BSIZE ? 0: 1);
  			if (u.u_error || uio->uio_rw == UIO_WRITE && (long)bn<0)
  				return (u.u_error);
  			if (uio->uio_rw == UIO_WRITE && uio->uio_offset + n > ip->i_size &&
--- 226,236 ----
  					return (0);
  				if (diff < n)
  					n = diff;
! 			bn = bmap(ip, lbn, B_READ, flags);
  			}
  			else
! 				bn = bmap(ip,lbn,B_WRITE,
! 				       n == DEV_BSIZE ? flags : flags|B_CLRBUF);
  			if (u.u_error || uio->uio_rw == UIO_WRITE && (long)bn<0)
  				return (u.u_error);
  			if (uio->uio_rw == UIO_WRITE && uio->uio_offset + n > ip->i_size &&
***************
*** 260,265 ****
--- 282,294 ----
  		} else {
  			if (ioflag & IO_SYNC)
  				bwrite(bp);
+ /*
+  * The check below interacts _very_ badly with virtual memory tmp files
+  * such as those used by 'ld'.   These files tend to be small and repeatedly
+  * rewritten in 1kb chunks.  The check below causes the device driver to be
+  * called (and I/O initiated)  constantly.  Not sure what to do about this yet
+  * but this comment is being placed here as a reminder.
+ */
  			else if (n + on == DEV_BSIZE && !(ip->i_flag & IPIPE)) {
  				bp->b_flags |= B_AGE;
  				bawrite(bp);
***************
*** 274,280 ****
  		error = u.u_error;		/* XXX */
  	if (error && (uio->uio_rw == UIO_WRITE) && (ioflag & IO_UNIT) && 
  		(type != IFBLK)) {
! 		itrunc(ip, osize);
  		uio->uio_offset -= (resid - uio->uio_resid);
  		uio->uio_resid = resid;
  /*
--- 303,309 ----
  		error = u.u_error;		/* XXX */
  	if (error && (uio->uio_rw == UIO_WRITE) && (ioflag & IO_UNIT) && 
  		(type != IFBLK)) {
! 		itrunc(ip, osize, ioflag & IO_SYNC);
  		uio->uio_offset -= (resid - uio->uio_resid);
  		uio->uio_resid = resid;
  /*
*** /usr/src/sys/sys/ufs_alloc.c.old	Wed Jan 11 20:07:49 1995
--- /usr/src/sys/sys/ufs_alloc.c	Thu Sep 19 21:49:24 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_alloc.c	1.2 (2.11BSD GTE) 1/11/94
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_alloc.c	1.3 (2.11BSD GTE) 1996/9/19
   */
  
  #include "param.h"
***************
*** 15,20 ****
--- 15,21 ----
  #include "buf.h"
  #include "user.h"
  #include "kernel.h"
+ #include "mount.h"
  #ifdef QUOTA
  #include "quota.h"
  #endif
***************
*** 30,44 ****
   * obtain NICFREE more...
   */
  struct buf *
! alloc(ip, clrflg)
  	struct inode *ip;
! 	int clrflg;
  {
  	register struct fs *fs;
  	register struct buf *bp;
  	daddr_t bno;
  
  	fs = ip->i_fs;
  	while (fs->fs_flock)
  		sleep((caddr_t)&fs->fs_flock, PINOD);
  	do {
--- 31,48 ----
   * obtain NICFREE more...
   */
  struct buf *
! balloc(ip, flags)
  	struct inode *ip;
! 	int flags;
  {
  	register struct fs *fs;
  	register struct buf *bp;
+ 	int	async;
  	daddr_t bno;
  
  	fs = ip->i_fs;
+ 	async = fs->fs_flags & MNT_ASYNC;
+ 
  	while (fs->fs_flock)
  		sleep((caddr_t)&fs->fs_flock, PINOD);
  	do {
***************
*** 64,70 ****
  		}
  		brelse(bp);
  		/*
! 		 * Write the superblock back, synchronously,
  		 * so that the free list pointer won't point at garbage.
  		 * We can still end up with dups in free if we then
  		 * use some of the blocks in this freeblock, then crash
--- 68,74 ----
  		}
  		brelse(bp);
  		/*
! 		 * Write the superblock back, synchronously if requested,
  		 * so that the free list pointer won't point at garbage.
  		 * We can still end up with dups in free if we then
  		 * use some of the blocks in this freeblock, then crash
***************
*** 80,86 ****
  			*fps = *fs;
  		}
  		mapout(bp);
! 		bwrite(bp);
  		fs->fs_flock = 0;
  		wakeup((caddr_t)&fs->fs_flock);
  		if (fs->fs_nfree <=0)
--- 84,93 ----
  			*fps = *fs;
  		}
  		mapout(bp);
! 		if (!async)
! 			bwrite(bp);
! 		else
! 			bdwrite(bp);
  		fs->fs_flock = 0;
  		wakeup((caddr_t)&fs->fs_flock);
  		if (fs->fs_nfree <=0)
***************
*** 88,94 ****
  	}
  	bp = getblk(ip->i_dev, bno);
  	bp->b_resid = 0;
! 	if (clrflg)
  		clrbuf(bp);
  	fs->fs_fmod = 1;
  	fs->fs_tfree--;
--- 95,101 ----
  	}
  	bp = getblk(ip->i_dev, bno);
  	bp->b_resid = 0;
! 	if (flags & B_CLRBUF)
  		clrbuf(bp);
  	fs->fs_fmod = 1;
  	fs->fs_tfree--;
***************
*** 220,226 ****
  	if (fs->fs_ninode > 0)
  		goto loop;
  	fserr(fs, emsg);
! 	uprintf("\n%s: create/symlink failed, %s\n", fs->fs_fsmnt, emsg);
  	u.u_error = ENOSPC;
  	return(NULL);
  }
--- 227,233 ----
  	if (fs->fs_ninode > 0)
  		goto loop;
  	fserr(fs, emsg);
! 	uprintf("\n%s: %s\n", fs->fs_fsmnt, emsg);
  	u.u_error = ENOSPC;
  	return(NULL);
  }
***************
*** 257,263 ****
  		*fbp = *((FBLKP)&fs->fs_nfree);
  		mapout(bp);
  		fs->fs_nfree = 0;
! 		bwrite(bp);
  		fs->fs_flock = 0;
  		wakeup((caddr_t)&fs->fs_flock);
  	}
--- 264,273 ----
  		*fbp = *((FBLKP)&fs->fs_nfree);
  		mapout(bp);
  		fs->fs_nfree = 0;
! 		if (fs->fs_flags & MNT_ASYNC)
! 			bdwrite(bp);
! 		else
! 			bwrite(bp);
  		fs->fs_flock = 0;
  		wakeup((caddr_t)&fs->fs_flock);
  	}
*** /usr/src/sys/sys/ufs_bio.c.old	Sun Jan  3 00:45:09 1993
--- /usr/src/sys/sys/ufs_bio.c	Thu Sep 19 19:59:36 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_bio.c	2.1 (2.11BSD) 12/26/92
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_bio.c	2.2 (2.11BSD) 1996/9/13
   */
  
  #include "param.h"
***************
*** 325,332 ****
  	bp = dp->av_forw;
  	notavail(bp);
  	if (bp->b_flags & B_DELWRI) {
! 		bp->b_flags |= B_ASYNC;
! 		bwrite(bp);
  		goto loop;
  	}
  	if(bp->b_flags & (B_RAMREMAP|B_PHYS)) {
--- 325,331 ----
  	bp = dp->av_forw;
  	notavail(bp);
  	if (bp->b_flags & B_DELWRI) {
! 		bawrite(bp);
  		goto loop;
  	}
  	if(bp->b_flags & (B_RAMREMAP|B_PHYS)) {
***************
*** 421,430 ****
  }
  
  /*
!  * Make sure all write-behind blocks
!  * on dev (or NODEV for all)
!  * are flushed out.
!  * (from umount and update)
   */
  bflush(dev)
  	register dev_t dev;
--- 420,427 ----
  }
  
  /*
!  * Make sure all write-behind blocks on dev are flushed out.
!  * (from umount and sync)
   */
  bflush(dev)
  	register dev_t dev;
***************
*** 439,445 ****
  	for (bp = flist->av_forw; bp != flist; bp = bp->av_forw) {
  		if ((bp->b_flags & B_DELWRI) == 0)
  			continue;
! 		if (dev == NODEV || dev == bp->b_dev) {
  			bp->b_flags |= B_ASYNC;
  			notavail(bp);
  			bwrite(bp);
--- 436,442 ----
  	for (bp = flist->av_forw; bp != flist; bp = bp->av_forw) {
  		if ((bp->b_flags & B_DELWRI) == 0)
  			continue;
! 		if (dev == bp->b_dev) {
  			bp->b_flags |= B_ASYNC;
  			notavail(bp);
  			bwrite(bp);
*** /usr/src/sys/sys/ufs_bmap.c.old	Sun Apr  8 03:03:57 1990
--- /usr/src/sys/sys/ufs_bmap.c	Thu Sep 19 21:51:20 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_bmap.c	1.1 (2.10BSD Berkeley) 12/1/86
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_bmap.c	1.2 (2.11BSD) 1996/9/19
   */
  
  #include "param.h"
***************
*** 16,21 ****
--- 16,22 ----
  #include "user.h"
  #include "buf.h"
  #include "fs.h"
+ #include "mount.h"
  #include "uio.h"
  
  /*
***************
*** 27,36 ****
   * for use in read-ahead.
   */
  daddr_t
! bmap(ip, bn, rwflg, clrflg)
  	register struct inode *ip;
  	daddr_t bn;
! 	int rwflg, clrflg;
  {
  	register int i;
  	register struct buf *bp;
--- 28,37 ----
   * for use in read-ahead.
   */
  daddr_t
! bmap(ip, bn, rwflg, flags)
  	register struct inode *ip;
  	daddr_t bn;
! 	int rwflg, flags;
  {
  	register int i;
  	register struct buf *bp;
***************
*** 37,42 ****
--- 38,44 ----
  	struct buf *nbp;
  	int j, sh;
  	daddr_t nb, *bap, ra;
+ 	int async = ip->i_fs->fs_flags & MNT_ASYNC;
  
  	if (bn < 0) {
  		u.u_error = EFBIG;
***************
*** 51,65 ****
  		i = bn;
  		nb = ip->i_addr[i];
  		if (nb == 0) {
! 			if (rwflg == B_READ || (bp = alloc(ip, clrflg)) == NULL)
  				return((daddr_t)-1);
  			nb = dbtofsb(bp->b_blkno);
! 			if ((ip->i_mode&IFMT) == IFDIR)
! 				/*
! 				 * Write directory blocks synchronously
! 				 * so they never appear with garbage in
! 				 * them on the disk.
! 				 */
  				bwrite(bp);
  			else
  				bdwrite(bp);
--- 53,67 ----
  		i = bn;
  		nb = ip->i_addr[i];
  		if (nb == 0) {
! 			if (rwflg == B_READ || (bp = balloc(ip, flags)) == NULL)
  				return((daddr_t)-1);
  			nb = dbtofsb(bp->b_blkno);
! /*
!  * directory blocks are usually the only thing written synchronously at this
!  * point (so they never appear with garbage in them on the disk).  This is
!  * overridden if the filesystem was mounted 'async'.
! */
! 			if (flags & B_SYNC)
  				bwrite(bp);
  			else
  				bdwrite(bp);
***************
*** 97,110 ****
  	 */
  	nb = ip->i_addr[NADDR-j];
  	if (nb == 0) {
! 		if (rwflg == B_READ || (bp = alloc(ip, 1)) == NULL)
  			return((daddr_t) -1);
  		nb = dbtofsb(bp->b_blkno);
  		/*
! 		 * Write synchronously so that indirect blocks
  		 * never point at garbage.
  		 */
! 		bwrite(bp);
  		ip->i_addr[NADDR-j] = nb;
  		ip->i_flag |= IUPD|ICHG;
  	}
--- 99,115 ----
  	 */
  	nb = ip->i_addr[NADDR-j];
  	if (nb == 0) {
! 		if (rwflg == B_READ || (bp = balloc(ip, flags | B_CLRBUF)) == NULL)
  			return((daddr_t) -1);
  		nb = dbtofsb(bp->b_blkno);
  		/*
! 		 * Write synchronously if requested so that indirect blocks
  		 * never point at garbage.
  		 */
! 		if (async)
! 			bdwrite(bp);
! 		else
! 			bwrite(bp);
  		ip->i_addr[NADDR-j] = nb;
  		ip->i_flag |= IUPD|ICHG;
  	}
***************
*** 129,145 ****
  			ra = bap[i+1];
  		mapout(bp);
  		if (nb == 0) {
! 			if (rwflg == B_READ || (nbp = alloc(ip, 1)) == NULL) {
  				brelse(bp);
  				return((daddr_t) -1);
  			}
  			nb = dbtofsb(nbp->b_blkno);
! 			if (j < 3 || (ip->i_mode&IFMT) == IFDIR)
! 				/*
! 				 * Write synchronously so indirect blocks
! 				 * never point at garbage and blocks
! 				 * in directories never contain garbage.
! 				 */
  				bwrite(nbp);
  			else
  				bdwrite(nbp);
--- 134,151 ----
  			ra = bap[i+1];
  		mapout(bp);
  		if (nb == 0) {
! 			if (rwflg == B_READ || (nbp = balloc(ip, flags | B_CLRBUF)) == NULL) {
  				brelse(bp);
  				return((daddr_t) -1);
  			}
  			nb = dbtofsb(nbp->b_blkno);
! /*
!  * Write synchronously so indirect blocks never point at garbage and blocks
!  * in directories never contain garbage.  This check used to be based on the
!  * type of inode, if it was a directory then 'sync' writes were done.  See the
!  * comments earlier about filesystems being mounted 'async'.
! */
! 			if (!async && (j < 3 || (flags & B_SYNC)))
  				bwrite(nbp);
  			else
  				bdwrite(nbp);
*** /usr/src/sys/sys/ufs_fio.c.old	Thu Dec 29 17:31:03 1994
--- /usr/src/sys/sys/ufs_fio.c	Mon Sep 16 21:09:30 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_fio.c	1.3 (2.11BSD GTE) 12/29/94
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_fio.c	1.4 (2.11BSD GTE) 1996/9/13
   */
  
  #include "param.h"
***************
*** 162,168 ****
  		{
  		if	((ip->i_mode & IFMT) == IFDIR)
  			return(EISDIR);
! 		itrunc(ip, vap->va_size);
  		if	(u.u_error)
  			return(u.u_error);
  		}
--- 162,168 ----
  		{
  		if	((ip->i_mode & IFMT) == IFDIR)
  			return(EISDIR);
! 		itrunc(ip, vap->va_size, 0);
  		if	(u.u_error)
  			return(u.u_error);
  		}
*** /usr/src/sys/sys/ufs_inode.c.old	Mon Nov 28 20:33:32 1994
--- /usr/src/sys/sys/ufs_inode.c	Thu Sep 19 21:23:28 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_inode.c	1.4 (2.11BSD GTE) 11/26/94
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_inode.c	1.5 (2.11BSD GTE) 1996/9/19
   */
  
  #include "param.h"
***************
*** 312,322 ****
  	if (ip->i_count == 1) {
  		ip->i_flag |= ILOCKED;
  		if (ip->i_nlink <= 0 && ip->i_fs->fs_ronly == 0) {
- 			itrunc(ip, (u_long)0);
- 			ip->i_mode = 0;
- 			ip->i_rdev = 0;
- 			ip->i_flag |= IUPD|ICHG;
- 			ifree(ip, ip->i_number);
  #ifdef QUOTA
  			QUOTAMAP();
  			(void) chkiq(ip->i_dev, ip, ip->i_uid, 0);
--- 312,317 ----
***************
*** 324,329 ****
--- 319,329 ----
  			ix_dquot[ip - inode] = NODQUOT;
  			QUOTAUNMAP();
  #endif
+ 			itrunc(ip, (u_long)0, 0);
+ 			ip->i_mode = 0;
+ 			ip->i_rdev = 0;
+ 			ip->i_flag |= IUPD|ICHG;
+ 			ifree(ip, ip->i_number);
  		}
  		IUPDAT(ip, &time, &time, 0);
  		IUNLOCK(ip);
***************
*** 411,417 ****
  #endif
  	bcopy(ip->i_addr, dp->di_addr, NADDR * sizeof (daddr_t));
  	mapout(bp);
! 	if (waitfor)
  		bwrite(bp);
  	else
  		bdwrite(bp);
--- 411,417 ----
  #endif
  	bcopy(ip->i_addr, dp->di_addr, NADDR * sizeof (daddr_t));
  	mapout(bp);
! 	if (waitfor && ((ip->i_fs->fs_flags & MNT_ASYNC) == 0))
  		bwrite(bp);
  	else
  		bdwrite(bp);
***************
*** 428,436 ****
   *
   * NB: triple indirect blocks are untested.
   */
! itrunc(oip,length)
  	register struct inode *oip;
  	u_long length;
  {
  	daddr_t lastblock;
  	register int i;
--- 428,437 ----
   *
   * NB: triple indirect blocks are untested.
   */
! itrunc(oip,length, ioflags)
  	register struct inode *oip;
  	u_long length;
+ 	int	ioflags;
  {
  	daddr_t lastblock;
  	register int i;
***************
*** 439,448 ****
--- 440,454 ----
  	struct buf *bp;
  	int offset, level;
  	struct inode tip;
+ 	int aflags;
  #ifdef QUOTA
  	long bytesreleased;
  #endif
  
+ 	aflags = B_CLRBUF;
+ 	if (ioflags & IO_SYNC)
+ 		aflags |= B_SYNC;
+ 
  	/*
  	 * special hack for pipes, since size for them isn't the size of
  	 * the file, it's the amount currently waiting for transfer.  It's
***************
*** 452,462 ****
  	 */
  	if (oip->i_flag & IPIPE)
  		oip->i_size = MAXPIPSIZ;
! 	else if (oip->i_size <= length) {
! 		oip->i_flag |= ICHG|IUPD;
! 		iupdat(oip, &time, &time, 1);
! 		return;
  	}
  	/*
  	 * Calculate index into inode's block list of
  	 * last direct and indirect blocks (if any)
--- 458,483 ----
  	 */
  	if (oip->i_flag & IPIPE)
  		oip->i_size = MAXPIPSIZ;
! 	else if (oip->i_size == length)
! 		goto updret;
! 
! 	/*
! 	 * Lengthen the size of the file. We must ensure that the
! 	 * last byte of the file is allocated. Since the smallest
! 	 * value of osize is 0, length will be at least 1.
! 	 */
! 	if (oip->i_size < length) {
! 		bn = bmap(oip, lblkno(length - 1), B_WRITE, aflags);
! 		if (u.u_error || bn < 0)
! 			return;
! #ifdef	QUOTA
! 		bytesreleased = oip->i_size - length;
! #endif
! 		oip->i_size = length;
! 		bdwrite(bp);
! 		goto doquotaupd;
  	}
+ 
  	/*
  	 * Calculate index into inode's block list of
  	 * last direct and indirect blocks (if any)
***************
*** 476,482 ****
  	 */
  	offset = blkoff(length);
  	if (offset) {
! 		bn = bmap(oip, lblkno(length), B_WRITE, 1);
  		if (u.u_error || bn < 0)
  			return;
  		bp = bread(oip->i_dev, bn);
--- 497,503 ----
  	 */
  	offset = blkoff(length);
  	if (offset) {
! 		bn = bmap(oip, lblkno(length), B_WRITE, aflags);
  		if (u.u_error || bn < 0)
  			return;
  		bp = bread(oip->i_dev, bn);
***************
*** 509,516 ****
  		}
  	for (i = NDADDR - 1; i > lastblock; i--)
  		oip->i_db[i] = 0;
- 	oip->i_flag |= ICHG|IUPD;
- 	iupdat(oip, &time, &time, 1);
  
  	/*
  	 * Indirect blocks first.
--- 530,535 ----
***************
*** 519,525 ****
  	for (level = TRIPLE; level >= SINGLE; level--) {
  		bn = ip->i_ib[level];
  		if (bn != 0) {
! 			indirtrunc(ip, bn, lastiblock[level], level);
  			if (lastiblock[level] < 0) {
  				ip->i_ib[level] = 0;
  				free(ip, bn);
--- 538,544 ----
  	for (level = TRIPLE; level >= SINGLE; level--) {
  		bn = ip->i_ib[level];
  		if (bn != 0) {
! 			indirtrunc(ip, bn, lastiblock[level], level, aflags);
  			if (lastiblock[level] < 0) {
  				ip->i_ib[level] = 0;
  				free(ip, bn);
***************
*** 553,564 ****
  			panic("itrunc2");
  /* END PARANOIA */
  #endif
! 	oip->i_flag |= ICHG;
  #ifdef QUOTA
  	QUOTAMAP();
  	(void)chkdq(oip, -bytesreleased, 0);
  	QUOTAUNMAP();
  #endif
  }
  
  /*
--- 572,587 ----
  			panic("itrunc2");
  /* END PARANOIA */
  #endif
! 
! doquotaupd:
  #ifdef QUOTA
  	QUOTAMAP();
  	(void)chkdq(oip, -bytesreleased, 0);
  	QUOTAUNMAP();
  #endif
+ updret:
+ 	oip->i_flag |= ICHG|IUPD;
+ 	iupdat(oip, &time, &time, 1);
  }
  
  /*
***************
*** 571,580 ****
   *
   * NB: triple indirect blocks are untested.
   */
! indirtrunc(ip, bn, lastbn, level)
  	struct inode *ip;
  	daddr_t bn, lastbn;
  	int level;
  {
  	register struct buf *bp;
  	daddr_t nb, last;
--- 594,604 ----
   *
   * NB: triple indirect blocks are untested.
   */
! indirtrunc(ip, bn, lastbn, level, aflags)
  	struct inode *ip;
  	daddr_t bn, lastbn;
  	int level;
+ 	int aflags;
  {
  	register struct buf *bp;
  	daddr_t nb, last;
***************
*** 619,625 ****
  		bzero((caddr_t)&bap[last + 1],
  		    (u_int)(NINDIR - (last + 1)) * sizeof(daddr_t));
  		mapout(bp);
! 		bwrite(bp);
  		bp = cpy;
  	}
  
--- 643,652 ----
  		bzero((caddr_t)&bap[last + 1],
  		    (u_int)(NINDIR - (last + 1)) * sizeof(daddr_t));
  		mapout(bp);
! 		if (aflags & B_SYNC)
! 			bwrite(bp);
! 		else
! 			bawrite(bp);
  		bp = cpy;
  	}
  
***************
*** 632,638 ****
  	 * and that doesn't work well with recursion.
  	 */
  	if (level == SINGLE)
! 		trsingle(ip, bp, last);
  	else {
  		register daddr_t *bstart, *bstop;
  
--- 659,665 ----
  	 * and that doesn't work well with recursion.
  	 */
  	if (level == SINGLE)
! 		trsingle(ip, bp, last, aflags);
  	else {
  		register daddr_t *bstart, *bstop;
  
***************
*** 646,652 ****
  			nb = *bstart;
  			if (nb) {
  				mapout(bp);
! 				indirtrunc(ip, nb, (daddr_t)-1, level - 1);
  				free(ip, nb);
  				mapin(bp);
  			}
--- 673,679 ----
  			nb = *bstart;
  			if (nb) {
  				mapout(bp);
! 				indirtrunc(ip,nb,(daddr_t)-1, level-1, aflags);
  				free(ip, nb);
  				mapin(bp);
  			}
***************
*** 663,669 ****
  			mapout(bp);
  			last = lastbn % factor;
  			if (nb != 0)
! 				indirtrunc(ip, nb, last, level - 1);
  		}
  	}
  	brelse(bp);
--- 690,696 ----
  			mapout(bp);
  			last = lastbn % factor;
  			if (nb != 0)
! 				indirtrunc(ip, nb, last, level - 1, aflags);
  		}
  	}
  	brelse(bp);
***************
*** 670,679 ****
  }
  
  static
! trsingle(ip, bp,last)
  	register struct inode *ip;
  	caddr_t bp;
  	daddr_t last;
  {
  	register daddr_t *bstart, *bstop;
  	daddr_t blarray[NINDIR];
--- 697,707 ----
  }
  
  static
! trsingle(ip, bp,last, aflags)
  	register struct inode *ip;
  	caddr_t bp;
  	daddr_t last;
+ 	int aflags;
  {
  	register daddr_t *bstart, *bstop;
  	daddr_t blarray[NINDIR];
*** /usr/src/sys/sys/ufs_mount.c.old	Sat Apr 20 21:56:21 1996
--- /usr/src/sys/sys/ufs_mount.c	Mon Sep 16 21:24:08 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_mount.c	1.8 (2.11BSD GTE) 1996/4/20
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_mount.c	1.9 (2.11BSD GTE) 1996/9/13
   */
  
  #include "param.h"
***************
*** 270,275 ****
--- 270,276 ----
  	register struct mount *mp;
  	register struct inode *ip;
  	register int error;
+ 	int aflag;
  
  	error = getmdev(&dev, fname);
  	if (error)
***************
*** 281,293 ****
  found:
  	xumount(dev);	/* remove unused sticky files from text table */
  	nchinval(dev);	/* flush the name cache */
! 	update();
  #ifdef QUOTA
  	if (iflush(dev, mp->m_qinod) < 0)
  #else
  	if (iflush(dev) < 0)
  #endif
  		return (EBUSY);
  #ifdef QUOTA
  	QUOTAMAP();
  	closedq(mp);
--- 282,300 ----
  found:
  	xumount(dev);	/* remove unused sticky files from text table */
  	nchinval(dev);	/* flush the name cache */
! 	aflag = mp->m_flags & MNT_ASYNC;
! 	mp->m_flags &= ~MNT_ASYNC;	/* Don't want async when unmounting */
! 	ufs_sync(mp);
! 
  #ifdef QUOTA
  	if (iflush(dev, mp->m_qinod) < 0)
  #else
  	if (iflush(dev) < 0)
  #endif
+ 		{
+ 		mp->m_flags |= aflag;
  		return (EBUSY);
+ 		}
  #ifdef QUOTA
  	QUOTAMAP();
  	closedq(mp);
*** /usr/src/sys/sys/ufs_namei.c.old	Sat Nov 26 23:26:42 1994
--- /usr/src/sys/sys/ufs_namei.c	Mon Sep 16 21:08:35 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_namei.c	1.3 (2.11BSD GTE) 11/26/94
   */
  #include "param.h"
  #include "../machine/seg.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_namei.c	1.4 (2.11BSD GTE) 1996/9/13
   */
  #include "param.h"
  #include "../machine/seg.h"
***************
*** 984,990 ****
  	bwrite(bp);
  	dp->i_flag |= IUPD|ICHG;
  	if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
! 		itrunc(dp, (u_long)ndp->ni_endoff);
  	iput(dp);
  	return (error);
  }
--- 984,990 ----
  	bwrite(bp);
  	dp->i_flag |= IUPD|ICHG;
  	if (ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
! 		itrunc(dp, (u_long)ndp->ni_endoff, 0);
  	iput(dp);
  	return (error);
  }
*** /usr/src/sys/sys/ufs_subr.c.old	Wed Feb 16 19:39:05 1994
--- /usr/src/sys/sys/ufs_subr.c	Mon Sep 16 21:08:51 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_subr.c	1.4 (2.11BSD GTE) 2/15/94
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_subr.c	1.5 (2.11BSD GTE) 1996/9/13
   */
  
  #include "param.h"
***************
*** 19,89 ****
  #include "systm.h"
  
  /*
!  * Update is the internal name of 'sync'.  It goes through the disk
!  * queues to initiate sandbagged IO; goes through the inodes to write
!  * modified nodes; and it goes through the mount table to initiate
!  * the writing of the modified super blocks.
   */
! update()
  {
  	register struct mount *mp;
! 	register struct buf *bp;
  
! 	if (updlock)
  		return;
  	updlock++;
! 	/*
! 	 * Write back modified superblocks.
! 	 * Consistency check that the superblock
! 	 * of each file system is still in the buffer cache.
! 	 */
! 	for (mp = &mount[0]; mp < &mount[NMOUNT]; mp++) {
! 		register struct fs *fs;
! 
! 		if (mp->m_inodp == NULL || mp->m_dev == NODEV)
  			continue;
  		fs = &mp->m_filsys;
! 		if (fs->fs_fmod == 0)
  			continue;
! 		if (fs->fs_ronly != 0) {		/* XXX */
! 			printf("fs = %s\n", fs->fs_fsmnt);
! 			panic("update: rofs mod");
  		}
! 		if (fs->fs_ilock || fs->fs_flock)
! 			continue;
! 		bp = getblk(mp->m_dev, SUPERB);
! 		if (bp->b_flags & B_ERROR)
! 			continue;
! 		fs->fs_fmod = 0;
! 		fs->fs_time = time.tv_sec;
! 		bcopy(fs, mapin(bp), sizeof (struct fs));
! 		mapout(bp);
! 		bwrite(bp);
  	}
- 	/*
- 	 * Write back each (modified) inode.
- 	 */
- 	{
- 		register struct inode *ip;
  
- 		for (ip = inode; ip < inodeNINODE; ip++) {
- 			if ((ip->i_flag & ILOCKED) != 0 || ip->i_count == 0 ||
- 			    (ip->i_flag & (IMOD|IACC|IUPD|ICHG)) == 0)
- 				continue;
- 			ip->i_flag |= ILOCKED;
- 			ip->i_count++;
- 			iupdat(ip, &time, &time, 0);
- 			iput(ip);
- 		}
- 	}
- 	updlock = 0;
- 	/*
- 	 * Force stale buffer cache information to be flushed,
- 	 * for all devices.
- 	 */
- 	bflush(NODEV);
- }
- 
  /*
   * Flush all the blocks associated with an inode.
   * There are two strategies based on the size of the file;
--- 19,52 ----
  #include "systm.h"
  
  /*
!  * Go through the mount table looking for filesystems which have been modified.
!  * For each "dirty" filesystem call 'ufs_sync' to flush changed inodes, data
!  * blocks and the superblock to disc.
   */
! sync()
  {
  	register struct mount *mp;
! 	register struct fs *fs;
! 	int async;
  
! 	if	(updlock)
  		return;
  	updlock++;
! 	for	(mp = &mount[0]; mp < &mount[NMOUNT]; mp++)
! 		{
! 		if	(mp->m_inodp == NULL || mp->m_dev == NODEV)
  			continue;
  		fs = &mp->m_filsys;
! 		if	(fs->fs_fmod == 0 || fs->fs_ilock || fs->fs_flock)
  			continue;
! 		async = mp->m_flags & MNT_ASYNC;
! 		mp->m_flags &= ~MNT_ASYNC;
! 		ufs_sync(mp);
! 		mp->m_flags |= async;
  		}
! 	updlock = 0;
  	}
  
  /*
   * Flush all the blocks associated with an inode.
   * There are two strategies based on the size of the file;
***************
*** 207,239 ****
  		if (mp->m_dev == dev)
  			return (mp - &mount[0]);
  	return (-1);
- }
- #endif
- 
- #if (!defined(vax) && !defined(tahoe) && !defined(pdp11)) || defined(VAX630)
- scanc(size, cp, table, mask)
- 	u_int size;
- 	register u_char *cp, table[];
- 	register u_char mask;
- {
- 	register u_char *end = &cp[size];
- 
- 	while (cp < end && (table[*cp] & mask) == 0)
- 		cp++;
- 	return (end - cp);
- }
- #endif
- 
- #if !defined(vax) && !defined(tahoe) && !defined(pdp11)
- locc(mask, size, cp)
- 	register u_char mask;
- 	u_int size;
- 	register u_char *cp;
- {
- 	register u_char *end = &cp[size];
- 
- 	while (cp < end && *cp != mask)
- 		cp++;
- 	return (end - cp);
  }
  #endif
--- 170,174 ----
*** /usr/src/sys/sys/ufs_syscalls.c.old	Sun Dec 24 15:30:35 1995
--- /usr/src/sys/sys/ufs_syscalls.c	Mon Sep 16 21:10:02 1996
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_syscalls.c	1.6 (2.11BSD GTE) 1995/12/24
   */
  
  #include "param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_syscalls.c	1.7 (2.11BSD GTE) 1996/9/13
   */
  
  #include "param.h"
***************
*** 198,204 ****
  		}
  	}
  	if (mode & O_TRUNC)
! 		itrunc(ip, (u_long)0);
  	iunlock(ip);
  	fp->f_flag = mode&FMASK;
  	fp->f_type = DTYPE_INODE;
--- 198,204 ----
  		}
  	}
  	if (mode & O_TRUNC)
! 		itrunc(ip, (u_long)0, mode & O_FSYNC ? IO_SYNC : 0);
  	iunlock(ip);
  	fp->f_flag = mode&FMASK;
  	fp->f_type = DTYPE_INODE;
***************
*** 832,846 ****
  }
  
  /*
-  * Flush any pending I/O.
-  */
- sync()
- {
- 
- 	update();
- }
- 
- /*
   * Truncate a file given its path name.
   */
  truncate()
--- 832,837 ----
***************
*** 1144,1150 ****
  		if (doingdirectory) {
  			if (--xp->i_nlink != 0)
  				panic("rename: lnk dir");
! 			itrunc(xp, (u_long)0);
  		}
  		xp->i_flag |= ICHG;
  		iput(xp);
--- 1135,1141 ----
  		if (doingdirectory) {
  			if (--xp->i_nlink != 0)
  				panic("rename: lnk dir");
! 			itrunc(xp, (u_long)0, 0);	/* IO_SYNC? */
  		}
  		xp->i_flag |= ICHG;
  		iput(xp);
***************
*** 1494,1500 ****
  	 * worry about them later.
  	 */
  	ip->i_nlink -= 2;
! 	itrunc(ip, (u_long)0);
  	cacheinval(ip);
  out:
  	if (dp)
--- 1485,1491 ----
  	 * worry about them later.
  	 */
  	ip->i_nlink -= 2;
! 	itrunc(ip, (u_long)0, 0);	/* IO_SYNC? */
  	cacheinval(ip);
  out:
  	if (dp)
*** /usr/src/sys/sys/ufs_syscalls2.c.old	Sun Dec 31 12:12:18 1995
--- /usr/src/sys/sys/ufs_syscalls2.c	Sat Sep 14 20:50:21 1996
***************
*** 1,5 ****
  /*
!  * 	@(#) 	ufs_syscalls2.c	  1.2 (2.11BSD) 1995/12/31
   *
   * ufs_syscalls was getting too large.  New UFS related system calls are
   * placed in this file.
--- 1,5 ----
  /*
!  * 	@(#) 	ufs_syscalls2.c	  1.3 (2.11BSD) 1996/9/13
   *
   * ufs_syscalls was getting too large.  New UFS related system calls are
   * placed in this file.
***************
*** 9,17 ****
--- 9,19 ----
  #include "../machine/seg.h"
  #include "user.h"
  #include "inode.h"
+ #include "buf.h"
  #include "fs.h"
  #include "namei.h"
  #include "mount.h"
+ #include "kernel.h"
  
  statfs()
  	{
***************
*** 113,116 ****
--- 115,193 ----
  	else
  		u.u_r.r_val1 = count;
  	return(0);
+ 	}
+ 
+ /*
+  * 'ufs_sync' is the routine which syncs a single filesystem.  This was
+  * created to replace 'update' which 'unmount' called.  It seemed silly to
+  * sync _every_ filesystem when unmounting just one filesystem.
+ */
+ 
+ ufs_sync(mp)
+ 	register struct mount *mp;
+ 	{
+ 	register struct fs *fs;
+ 	struct	buf *bp;
+ 	int	error = 0;
+ 
+ 	fs = &mp->m_filsys;
+ 	if	(fs->fs_fmod && (mp->m_flags & MNT_RDONLY))
+ 		{
+ 		printf("fs = %s\n", fs->fs_fsmnt);
+ 		panic("sync: rofs");
+ 		}
+ 	syncinodes(fs);		/* sync the inodes for this filesystem */
+ 	bflush(mp->m_dev);	/* flush dirty data blocks */
+ #ifdef	QUOTA
+ 	qsync(mp->m_dev);	/* sync the quotas */
+ #endif
+ 
+ /*
+  * And lastly the superblock, if the filesystem was modified.
+  * Write back modified superblocks. Consistency check that the superblock
+  * of each file system is still in the buffer cache.
+ */
+ 	if	(fs->fs_fmod)
+ 		{
+ 		bp =  getblk(mp->m_dev, SUPERB);
+ 		fs->fs_fmod = 0;
+ 		fs->fs_time = time.tv_sec;
+ 		bcopy(fs, mapin(bp), sizeof (struct fs));
+ 		mapout(bp);
+ 		bwrite(bp);
+ 		error = geterror(bp);
+ 		}
+ 	return(error);
+ 	}
+ 
+ /*
+  * This is somewhat inefficient in that the inode table is scanned for each
+  * filesystem but it didn't seem worth a page or two of code on something
+  * which only happens every 30 seconds.
+ */
+ 
+ syncinodes(fs)
+ 	struct	fs *fs;
+ 	{
+ 	register struct inode *ip;
+ 
+ 	/*
+ 	 * Write back each (modified) inode.
+ 	 */
+ 	for	(ip = inode; ip < inodeNINODE; ip++)
+ 		{
+ /*
+  * Attempt to reduce the overhead by short circuiting the scan if the
+  * inode is not for the filesystem being processed.
+ */
+ 		if	(ip->i_fs != fs)
+ 			continue;
+ 		if ((ip->i_flag & ILOCKED) != 0 || ip->i_count == 0 ||
+ 			   (ip->i_flag & (IMOD|IACC|IUPD|ICHG)) == 0)
+ 			continue;
+ 		ip->i_flag |= ILOCKED;
+ 		ip->i_count++;
+ 		iupdat(ip, &time, &time, 0);
+ 		iput(ip);
+ 		}
  	}
*** /usr/src/sys/GENERIC/Makefile.old	Mon Jun 17 20:04:04 1996
--- /usr/src/sys/GENERIC/Makefile	Sat Sep 21 22:33:57 1996
***************
*** 10,16 ****
  # software without specific prior written permission. This software
  # is provided ``as is'' without express or implied warranty.
  #
! #	2.7 (2.11BSD GTE) 1996/6/8
  #
  #########################################################
  # Non-network, but separate I/D kernel			#
--- 10,16 ----
  # software without specific prior written permission. This software
  # is provided ``as is'' without express or implied warranty.
  #
! #	2.7 (2.11BSD GTE) 1996/9/21
  #
  #########################################################
  # Non-network, but separate I/D kernel			#
***************
*** 52,58 ****
  	subr_rmap.o subr_xxx.o sys_inode.o sys_pipe.o trap.o tty.o \
  	tty_conf.o tty_subr.o tty_tb.o tty_tty.o ufs_alloc.o ufs_bio.o \
  	ufs_bmap.o ufs_dsort.o ufs_fio.o ufs_inode.o ufs_namei.o \
! 	ufs_subr.o xp.o
  OV1=	sys_generic.o ufs_syscalls.o
  OV2=	kern_acct.o kern_exec.o kern_exit.o kern_fork.o kern_resource.o
  OV3=	clock.o cons.o kern_time.o \
--- 52,58 ----
  	subr_rmap.o subr_xxx.o sys_inode.o sys_pipe.o trap.o tty.o \
  	tty_conf.o tty_subr.o tty_tb.o tty_tty.o ufs_alloc.o ufs_bio.o \
  	ufs_bmap.o ufs_dsort.o ufs_fio.o ufs_inode.o ufs_namei.o \
! 	xp.o
  OV1=	sys_generic.o ufs_syscalls.o
  OV2=	kern_acct.o kern_exec.o kern_exit.o kern_fork.o kern_resource.o
  OV3=	clock.o cons.o kern_time.o \
***************
*** 64,70 ****
  OV6=	tmscp.o tmscpdump.o
  OV7=	rl.o mch_fpsim.o ingreslock.o ufs_disksubr.o
  OV8=	rx.o kern_sysctl.o vm_sched.o vm_text.o
! OV9=	kern_pdp.o kern_xxx.o ufs_syscalls2.o mem.o
  
  KERNOBJ=${CONF} ${BASE} ${OV1} ${OV2} ${OV3} ${OV4} ${OV5} \
  	${OV6} ${OV7} ${OV8} ${OV9} ${OV10} ${OV11} ${OV12} \
--- 64,70 ----
  OV6=	tmscp.o tmscpdump.o
  OV7=	rl.o mch_fpsim.o ingreslock.o ufs_disksubr.o
  OV8=	rx.o kern_sysctl.o vm_sched.o vm_text.o
! OV9=	kern_pdp.o kern_xxx.o ufs_syscalls2.o mem.o ufs_subr.o
  
  KERNOBJ=${CONF} ${BASE} ${OV1} ${OV2} ${OV3} ${OV4} ${OV5} \
  	${OV6} ${OV7} ${OV8} ${OV9} ${OV10} ${OV11} ${OV12} \
*** /usr/src/sys/conf/Make.sunix.old	Sun Jun  9 13:55:53 1996
--- /usr/src/sys/conf/Make.sunix	Sat Sep 21 22:44:08 1996
***************
*** 9,15 ****
  # software without specific prior written permission. This software
  # is provided ``as is'' without express or implied warranty.
  #
! #	2.7 (2.11BSD GTE) 1996/6/8
  #
  #########################################################
  # Non-network, but separate I/D kernel			#
--- 9,15 ----
  # software without specific prior written permission. This software
  # is provided ``as is'' without express or implied warranty.
  #
! #	2.7 (2.11BSD GTE) 1996/9/21
  #
  #########################################################
  # Non-network, but separate I/D kernel			#
***************
*** 51,57 ****
  	subr_rmap.o subr_xxx.o sys_inode.o sys_pipe.o trap.o tty.o \
  	tty_conf.o tty_subr.o tty_tb.o tty_tty.o ufs_alloc.o ufs_bio.o \
  	ufs_bmap.o ufs_dsort.o ufs_fio.o ufs_inode.o ufs_namei.o \
! 	ufs_subr.o xp.o
  OV1=	sys_generic.o ufs_syscalls.o
  OV2=	kern_acct.o kern_exec.o kern_exit.o kern_fork.o kern_resource.o
  OV3=	clock.o cons.o kern_time.o \
--- 51,57 ----
  	subr_rmap.o subr_xxx.o sys_inode.o sys_pipe.o trap.o tty.o \
  	tty_conf.o tty_subr.o tty_tb.o tty_tty.o ufs_alloc.o ufs_bio.o \
  	ufs_bmap.o ufs_dsort.o ufs_fio.o ufs_inode.o ufs_namei.o \
! 	xp.o
  OV1=	sys_generic.o ufs_syscalls.o
  OV2=	kern_acct.o kern_exec.o kern_exit.o kern_fork.o kern_resource.o
  OV3=	clock.o cons.o kern_time.o \
***************
*** 63,69 ****
  OV6=	tmscp.o tmscpdump.o
  OV7=	rl.o mch_fpsim.o ingreslock.o ufs_disksubr.o
  OV8=	rx.o kern_sysctl.o vm_sched.o vm_text.o
! OV9=	kern_pdp.o kern_xxx.o ufs_syscalls2.o mem.o
  
  KERNOBJ=${CONF} ${BASE} ${OV1} ${OV2} ${OV3} ${OV4} ${OV5} \
  	${OV6} ${OV7} ${OV8} ${OV9} ${OV10} ${OV11} ${OV12} \
--- 63,69 ----
  OV6=	tmscp.o tmscpdump.o
  OV7=	rl.o mch_fpsim.o ingreslock.o ufs_disksubr.o
  OV8=	rx.o kern_sysctl.o vm_sched.o vm_text.o
! OV9=	kern_pdp.o kern_xxx.o ufs_syscalls2.o mem.o ufs_subr.o
  
  KERNOBJ=${CONF} ${BASE} ${OV1} ${OV2} ${OV3} ${OV4} ${OV5} \
  	${OV6} ${OV7} ${OV8} ${OV9} ${OV10} ${OV11} ${OV12} \
*** /VERSION.old	Mon Sep 16 21:00:09 1996
--- /VERSION	Sat Sep 21 21:29:06 1996
***************
*** 1,4 ****
! Current Patch Level: 330
  
  2.11 BSD
  ============
--- 1,4 ----
! Current Patch Level: 331
  
  2.11 BSD
  ============