Subject: NFS buffer cache problem
To: None <karels@bsdi.com>
From: None <rick@snowhite.cis.uoguelph.ca>
List: netbsd-bugs
Date: 01/31/1995 22:55:22
Hi,

While banging away at NFS v3 I ran into this little problem w.r.t. buffer
cache semantics for FreeBSD2.0R. The attached code is meant to write the
dirty area back to the server and then invalidate the buffer. The 4.4BSD-Lite
code (nfs_bio.c.bak) worked with the 4.4BSD buffer cache, but for FreeBSD2.0R
the write was just thrown away. There was also a race when another process
did a getblk() for the same block and would allocate a new one before the
write completed. For the FreeBSD2.0R buffer cache code the fix is to simply
use the B_NOCACHE flag instead of the B_INVAL flag. Does the same fix seem
appropriate for the other 4.4BSD-Lite descendants?

In the FreeBSD2.0R getblk() there is also the possibility of a race when
two processes try to get a block for the same file at the same time and
one ends up sleeping in getnewbuf(), but admittedly the likelyhood of this
actually happening is pretty slim:-)

Also, I stuck an alpha test release of the nfs v3 code on
snowhite.cis.uoguelph.ca [131.104.48.1] for anyone who is interested.

Keep at it, rick
*** nfs_bio.c.bak	Tue Jan 31 17:44:27 1995
--- nfs_bio.c	Tue Jan 31 17:44:51 1995
***************
*** 256,276 ****
  		if (diff < n)
  			n = diff;
  		if (not_readin && n > 0) {
  			if (on < bp->b_validoff || (on + n) > bp->b_validend) {
  				if (!got_buf) {
  				    bp = nfs_getcacheblk(vp, bn, biosize, p);
  				    if (!bp)
  					return (EINTR);
  				    got_buf = 1;
  				}
! 				bp->b_flags |= B_INVAL;
  				if (bp->b_dirtyend > 0) {
  				    if ((bp->b_flags & B_DELWRI) == 0)
  					panic("nfsbioread");
  				    if (VOP_BWRITE(bp) == EINTR)
  					return (EINTR);
  				} else
  				    brelse(bp);
  				goto again;
  			}
  		}
--- 256,276 ----
  		if (diff < n)
  			n = diff;
  		if (not_readin && n > 0) {
  			if (on < bp->b_validoff || (on + n) > bp->b_validend) {
  				if (!got_buf) {
  				    bp = nfs_getcacheblk(vp, bn, biosize, p);
  				    if (!bp)
  					return (EINTR);
  				    got_buf = 1;
  				}
! 				bp->b_flags |= B_NOCACHE;
  				if (bp->b_dirtyend > 0) {
  				    if ((bp->b_flags & B_DELWRI) == 0)
  					panic("nfsbioread");
  				    if (VOP_BWRITE(bp) == EINTR)
  					return (EINTR);
  				} else
  				    brelse(bp);
  				goto again;
  			}
  		}