Subject: Re: that VM/fs cache coherency problem
To: None <mouse@Collatz.McRCIM.McGill.EDU, tech-kern@NetBSD.ORG>
From: Mike Hibler <mike@cs.utah.edu>
List: tech-kern
Date: 08/02/1995 10:18:06
> Date: Wed, 2 Aug 1995 06:55:38 -0400
> From: der Mouse <mouse@Collatz.McRCIM.McGill.EDU>
> To: tech-kern@NetBSD.ORG
> Subject: Re: that VM/fs cache coherency problem
> 
> ...
> I have been unable to reproduce the problem with tail.  But I have
> found a way to provoke it consistently (this works even on the sparc):
> 
> 	# cd /usr/src
> 	# make >& zerrs &
> 	# tail -f zerrs
> ...wait for a couple of lines to show up after tail -f prints its
> initial blip of output, then in another window...
> 	# vi zerrs
> 
> vi sees the file with an up-to-date size, but contents as of the time
> tail -f started up.  The space between that point and EOF is filled
> with NULs.
> 
So vi is using mmap?  This is probably the same problem that David Greenman
sent me mail about at one time.  vnode_pager_setsize doesn't properly deal
with files whose length is not a multiple of the page size (either shrinking
or growing).  I am guilty of not making the fix to 4.4.  I resisted because
this is just another band aide and it furthers the dangerous illusion that
there is some sort of coherency between the VM and filesystem caches.  It
only fixes the case where there is a partial page at the end of a file.

However, this is arguably not a coherency issue since the file is not really
changing, just growing.  So it may be worth patching (you were right David!)

Here is the diff he sent:

diff -c -r1.17 vnode_pager.c
*** 1.17	1994/04/05 03:23:53
--- vnode_pager.c	1994/06/11 14:54:05
***************
*** 37,43 ****
   * SUCH DAMAGE.
   *
   *	from: @(#)vnode_pager.c	7.5 (Berkeley) 4/20/91
!  *	$Id: vnode_pager.c,v 1.17 1994/04/05 03:23:53 davidg Exp $
   */
  
  /*
--- 37,43 ----
   * SUCH DAMAGE.
   *
   *	from: @(#)vnode_pager.c	7.5 (Berkeley) 4/20/91
!  *	$Id: vnode_pager.c,v 1.18 1994/06/11 07:58:08 davidg Exp $
   */
  
  /*
***************
*** 351,363 ****
  	 * File has shrunk.
  	 * Toss any cached pages beyond the new EOF.
  	 */
! 	if (round_page(nsize) < round_page(vnp->vnp_size)) {
  		vm_object_lock(object);
  		vm_object_page_remove(object,
! 				      (vm_offset_t)round_page(nsize), round_page(vnp->vnp_size));
  		vm_object_unlock(object);
  	}
  	vnp->vnp_size = (vm_offset_t)nsize;
  	vm_object_deallocate(object);
  }
  
--- 351,397 ----
  	 * File has shrunk.
  	 * Toss any cached pages beyond the new EOF.
  	 */
! 	if (nsize < vnp->vnp_size) {
  		vm_object_lock(object);
  		vm_object_page_remove(object,
! 				      round_page((vm_offset_t)nsize), vnp->vnp_size);
  		vm_object_unlock(object);
+ 		/*
+ 		 * this gets rid of garbage at the end of a page that is now only
+ 		 * partially backed by the vnode...
+ 		 */
+ 		if (nsize & PAGE_MASK) {
+ 			vm_offset_t kva;
+ 			vm_page_t m;
+ 			m = vm_page_lookup( object, trunc_page((vm_offset_t)nsize));
+ 			if( m) {
+ 				kva = vm_pager_map_page(m);
+ 				bzero( (caddr_t) kva + (nsize & PAGE_MASK),
+ 						round_page(nsize) - nsize);
+ 				vm_pager_unmap_page(kva);
+ 			}
+ 		}
+ 	} else {
+ 		/*
+ 		 * this allows the filesystem and VM cache to stay in sync
+ 		 * if the VM page hasn't been modified...  After the page is
+ 		 * removed -- it will be faulted back in from the filesystem
+ 		 * cache.
+ 		 */
+ 		if (vnp->vnp_size & PAGE_MASK) {
+ 			vm_page_t m;
+ 			m = vm_page_lookup( object, trunc_page(vnp->vnp_size));
+ 			if (m && (m->flags & PG_CLEAN)) {
+ 				vm_object_lock(object);
+ 				vm_object_page_remove(object,
+ 					      vnp->vnp_size, vnp->vnp_size);
+ 				vm_object_unlock(object);
+ 			}
+ 		}
  	}
  	vnp->vnp_size = (vm_offset_t)nsize;
+ 	object->size = round_page(nsize);
+ 
  	vm_object_deallocate(object);
  }