svn commit: r321581 - in head/sys: fs/nfsclient fs/smbfs vm

Konstantin Belousov kib at FreeBSD.org
Wed Jul 26 20:07:07 UTC 2017


Author: kib
Date: Wed Jul 26 20:07:05 2017
New Revision: 321581
URL: https://svnweb.freebsd.org/changeset/base/321581

Log:
  Mark pages after EOF as clean after pageout.
  
  Suppose that a file on NFS has partially filled last page, and this
  page is dirty.  NFS VOP_PAGEOUT() method only marks the the page clean
  up to the block of the last written byte, leaving other blocks dirty.
  Also any page which erronously exists in the vnode vm_object past EOF
  is also left marked as dirty.
  
  With the introduction of the buf-cache coherent pager, each pass of
  syncer over the object with such page results in creation of B_DELWRI
  buffer due to VOP_WRITE() call.  This buffer is noted on next syncer
  pass, which results e.g. a visible manifestation of shutdown never
  finishing vnode sync.  Note that before buf-cache coherency commit, a
  dirty page might left never synced to server if a partial writes
  occur.
  
  Fix this by clearing dirty bits after EOF.  Only blocks of the partial
  page which are completely after EOF are marked clean, to avoid
  possible user data loss.
  
  Reported by:	mav
  Reviewed by:	alc, markj
  Tested by:	mav, pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	1 week
  Differential revision:	https://reviews.freebsd.org/D11697

Modified:
  head/sys/fs/nfsclient/nfs_clbio.c
  head/sys/fs/smbfs/smbfs_io.c
  head/sys/vm/vnode_pager.c
  head/sys/vm/vnode_pager.h

Modified: head/sys/fs/nfsclient/nfs_clbio.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clbio.c	Wed Jul 26 20:01:31 2017	(r321580)
+++ head/sys/fs/nfsclient/nfs_clbio.c	Wed Jul 26 20:07:05 2017	(r321581)
@@ -336,8 +336,10 @@ ncl_putpages(struct vop_putpages_args *ap)
 	    cred);
 	crfree(cred);
 
-	if (error == 0 || !nfs_keep_dirty_on_error)
-		vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid);
+	if (error == 0 || !nfs_keep_dirty_on_error) {
+		vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid,
+		    np->n_size - offset, npages * PAGE_SIZE);
+	}
 	return (rtvals[0]);
 }
 

Modified: head/sys/fs/smbfs/smbfs_io.c
==============================================================================
--- head/sys/fs/smbfs/smbfs_io.c	Wed Jul 26 20:01:31 2017	(r321580)
+++ head/sys/fs/smbfs/smbfs_io.c	Wed Jul 26 20:07:05 2017	(r321581)
@@ -621,9 +621,11 @@ smbfs_putpages(ap)
 
 	relpbuf(bp, &smbfs_pbuf_freecnt);
 
-	if (!error)
-		vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid);
-	return rtvals[0];
+	if (error == 0) {
+		vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid,
+		    npages * PAGE_SIZE, npages * PAGE_SIZE);
+	}
+	return (rtvals[0]);
 #endif /* SMBFS_RWGENERIC */
 }
 

Modified: head/sys/vm/vnode_pager.c
==============================================================================
--- head/sys/vm/vnode_pager.c	Wed Jul 26 20:01:31 2017	(r321580)
+++ head/sys/vm/vnode_pager.c	Wed Jul 26 20:07:05 2017	(r321581)
@@ -1315,13 +1315,24 @@ vnode_pager_putpages_ioflags(int pager_flags)
 	return (ioflags);
 }
 
+/*
+ * vnode_pager_undirty_pages().
+ *
+ * A helper to mark pages as clean after pageout that was possibly
+ * done with a short write.  The lpos argument specifies the page run
+ * length in bytes, and the written argument specifies how many bytes
+ * were actually written.  eof is the offset past the last valid byte
+ * in the vnode using the absolute file position of the first byte in
+ * the run as the base from which it is computed.
+ */
 void
-vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, int written)
+vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, int written, off_t eof,
+    int lpos)
 {
 	vm_object_t obj;
-	int i, pos;
+	int i, pos, pos_devb;
 
-	if (written == 0)
+	if (written == 0 && eof >= lpos)
 		return;
 	obj = ma[0]->object;
 	VM_OBJECT_WLOCK(obj);
@@ -1335,6 +1346,37 @@ vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, 
 			vm_page_clear_dirty(ma[i], 0, written & PAGE_MASK);
 		}
 	}
+	if (eof >= lpos) /* avoid truncation */
+		goto done;
+	for (pos = eof, i = OFF_TO_IDX(trunc_page(pos)); pos < lpos; i++) {
+		if (pos != trunc_page(pos)) {
+			/*
+			 * The page contains the last valid byte in
+			 * the vnode, mark the rest of the page as
+			 * clean, potentially making the whole page
+			 * clean.
+			 */
+			pos_devb = roundup2(pos & PAGE_MASK, DEV_BSIZE);
+			vm_page_clear_dirty(ma[i], pos_devb, PAGE_SIZE -
+			    pos_devb);
+
+			/*
+			 * If the page was cleaned, report the pageout
+			 * on it as successful.  msync() no longer
+			 * needs to write out the page, endlessly
+			 * creating write requests and dirty buffers.
+			 */
+			if (ma[i]->dirty == 0)
+				rtvals[i] = VM_PAGER_OK;
+
+			pos = round_page(pos);
+		} else {
+			/* vm_pageout_flush() clears dirty */
+			rtvals[i] = VM_PAGER_BAD;
+			pos += PAGE_SIZE;
+		}
+	}
+done:
 	VM_OBJECT_WUNLOCK(obj);
 }
 

Modified: head/sys/vm/vnode_pager.h
==============================================================================
--- head/sys/vm/vnode_pager.h	Wed Jul 26 20:01:31 2017	(r321580)
+++ head/sys/vm/vnode_pager.h	Wed Jul 26 20:07:05 2017	(r321581)
@@ -50,7 +50,8 @@ int vnode_pager_local_getpages_async(struct vop_getpag
 int vnode_pager_putpages_ioflags(int pager_flags);
 void vnode_pager_release_writecount(vm_object_t object, vm_offset_t start,
     vm_offset_t end);
-void vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, int written);
+void vnode_pager_undirty_pages(vm_page_t *ma, int *rtvals, int written,
+    off_t eof, int lpos);
 void vnode_pager_update_writecount(vm_object_t object, vm_offset_t start,
     vm_offset_t end);
 


More information about the svn-src-head mailing list