Kernel - more NFS fixes, more dirty bit fixes, remove vfs_bio_set_validclean()
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 24 Aug 2009 23:27:32 +0000 (16:27 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 24 Aug 2009 23:27:32 +0000 (16:27 -0700)
* Remove vfs_bio_set_validclean().  It is no longer needed.

* General getpages operations must clear dirty bits non-inclusive of
  the end of the range.  A read which partially overlaps dirty VM
  pages shouldn't happen in the first place but if it were to happen
  we don't want to lose the dirty status on the DEV_BSIZE'd chunk
  straddling the end of the read.

* General truncation support.  Replace previous fix with a call to
  a new inline, vm_page_clear_dirty_beg_nonincl().  Similar to
  the getpages() issue, we do not want to lose the dirty status
  on the DEV_BSIZE'd chunk straddling the beginning of a truncation.

  (side note: Only effecs NFS as all other filesystems DEV_BSIZE-align
  their operations, but a good general fix in anycase).

sys/kern/vfs_bio.c
sys/sys/buf.h
sys/vfs/devfs/devfs_vnops.c
sys/vfs/nfs/nfs_bio.c
sys/vm/vm_page.c
sys/vm/vm_page.h
sys/vm/vm_page2.h
sys/vm/vnode_pager.c

index 622a2a2..151f9f4 100644 (file)
@@ -3907,16 +3907,16 @@ vfs_clean_pages(struct buf *bp)
        }
 }
 
+#if 0
 /*
- * vfs_bio_set_validclean:
+ * vfs_bio_set_valid:
  *
- *     Set the range within the buffer to valid and clean.  The range is 
- *     relative to the beginning of the buffer, b_loffset.  Note that
- *     b_loffset itself may be offset from the beginning of the first page.
+ * Set the range within the buffer to valid.  The range is relative
+ * to the beginning of the buffer, b_loffset.  Note that b_loffset
+ * itself may be offset from the beginning of the first page.
  */
-
 void   
-vfs_bio_set_validclean(struct buf *bp, int base, int size)
+vfs_bio_set_valid(struct buf *bp, int base, int size)
 {
        if (bp->b_flags & B_VMIO) {
                int i;
@@ -3937,13 +3937,14 @@ vfs_bio_set_validclean(struct buf *bp, int base, int size)
                        if (n > size)
                                n = size;
 
-                       vm_page_set_validclean(m, base & PAGE_MASK, n);
+                       vm_page_set_valid(m, base & PAGE_MASK, n);
                        base += n;
                        size -= n;
                        n = PAGE_SIZE;
                }
        }
 }
+#endif
 
 /*
  * vfs_bio_clrbuf:
index 3fe1417..7392e37 100644 (file)
@@ -427,7 +427,6 @@ int cluster_wbuild (struct vnode *, int, off_t, int);
 void   cluster_write (struct buf *, off_t, int, int);
 int    physread (struct dev_read_args *);
 int    physwrite (struct dev_write_args *);
-void   vfs_bio_set_validclean (struct buf *, int base, int size);
 void   vfs_bio_clrbuf (struct buf *);
 void   vfs_busy_pages (struct vnode *, struct buf *);
 void   vfs_unbusy_pages (struct buf *);
index a23f367..0adba2d 100644 (file)
 #include <sys/malloc.h>
 #include <sys/stat.h>
 #include <sys/reg.h>
-#include <sys/buf2.h>
 #include <vm/vm_pager.h>
 #include <vm/vm_zone.h>
 #include <vm/vm_object.h>
 #include <sys/filio.h>
 #include <sys/ttycom.h>
-#include <sys/sysref2.h>
 #include <sys/tty.h>
 #include <sys/devfs.h>
 #include <sys/pioctl.h>
 
 #include <machine/limits.h>
+#include <vm/vm_page2.h>
+#include <sys/buf2.h>
+#include <sys/sysref2.h>
 
 MALLOC_DECLARE(M_DEVFS);
 #define DEVFS_BADOP    (void *)devfs_badop
@@ -1937,7 +1938,8 @@ devfs_spec_getpages(struct vop_getpages_args *ap)
                         * unaligned offset to allow vm_page_set_validclean()
                         * to zero sub-DEV_BSIZE'd portions of the page.
                         */
-                       vm_page_set_validclean(m, 0, nread - toff);
+                       vm_page_set_valid(m, 0, nread - toff);
+                       vm_page_clear_dirty_end_nonincl(m, 0, nread - toff);
                } else {
                        m->valid = 0;
                        vm_page_undirty(m);
index 705811c..573b90d 100644 (file)
@@ -60,6 +60,7 @@
 
 #include <sys/buf2.h>
 #include <sys/thread2.h>
+#include <vm/vm_page2.h>
 
 #include "rpcv2.h"
 #include "nfsproto.h"
@@ -205,7 +206,8 @@ nfs_getpages(struct vop_getpages_args *ap)
                         * Read operation filled a partial page.
                         */
                        m->valid = 0;
-                       vm_page_set_validclean(m, 0, size - toff);
+                       vm_page_set_valid(m, 0, size - toff);
+                       vm_page_clear_dirty_end_nonincl(m, 0, size - toff);
                        /* handled by vm_fault now        */
                        /* vm_page_zero_invalid(m, TRUE); */
                } else {
@@ -997,7 +999,6 @@ again:
                 * have to commit them separately so there isn't much
                 * advantage to it except perhaps a bit of asynchronization.
                 */
-
                if (bp->b_dirtyend > 0 &&
                    (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
                        if (bwrite(bp) == EINTR) {
@@ -1026,6 +1027,12 @@ again:
                /*
                 * Only update dirtyoff/dirtyend if not a degenerate 
                 * condition.
+                *
+                * The underlying VM pages have been marked valid by
+                * virtue of acquiring the bp.  Because the entire buffer
+                * is marked dirty we do not have to worry about cleaning
+                * out the related dirty bits (and wouldn't really know
+                * how to deal with byte ranges anyway)
                 */
                if (n) {
                        if (bp->b_dirtyend > 0) {
@@ -1035,7 +1042,6 @@ again:
                                bp->b_dirtyoff = on;
                                bp->b_dirtyend = on + n;
                        }
-                       vfs_bio_set_validclean(bp, on, n);
                }
 
                /*
index e45ad19..0a0b907 100644 (file)
@@ -1438,10 +1438,9 @@ vm_page_bits(int base, int size)
  *
  * (base + size) must be less then or equal to PAGE_SIZE.
  */
-void
-vm_page_set_validclean(vm_page_t m, int base, int size)
+static void
+_vm_page_zero_valid(vm_page_t m, int base, int size)
 {
-       int pagebits;
        int frag;
        int endoff;
 
@@ -1481,31 +1480,34 @@ vm_page_set_validclean(vm_page_t m, int base, int size)
                    DEV_BSIZE - (endoff & (DEV_BSIZE - 1))
                );
        }
+}
 
-       /*
-        * Set valid, clear dirty bits.  If validating the entire
-        * page we can safely clear the pmap modify bit.  We also
-        * use this opportunity to clear the PG_NOSYNC flag.  If a process
-        * takes a write fault on a MAP_NOSYNC memory area the flag will
-        * be set again.
-        *
-        * We set valid bits inclusive of any overlap, but we can only
-        * clear dirty bits for DEV_BSIZE chunks that are fully within
-        * the range.
-        */
+/*
+ * Set valid, clear dirty bits.  If validating the entire
+ * page we can safely clear the pmap modify bit.  We also
+ * use this opportunity to clear the PG_NOSYNC flag.  If a process
+ * takes a write fault on a MAP_NOSYNC memory area the flag will
+ * be set again.
+ *
+ * We set valid bits inclusive of any overlap, but we can only
+ * clear dirty bits for DEV_BSIZE chunks that are fully within
+ * the range.
+ */
+void
+vm_page_set_valid(vm_page_t m, int base, int size)
+{
+       _vm_page_zero_valid(m, base, size);
+       m->valid |= vm_page_bits(base, size);
+}
 
+void
+vm_page_set_validclean(vm_page_t m, int base, int size)
+{
+       int pagebits;
+
+       _vm_page_zero_valid(m, base, size);
        pagebits = vm_page_bits(base, size);
        m->valid |= pagebits;
-#if 0  /* NOT YET */
-       if ((frag = base & (DEV_BSIZE - 1)) != 0) {
-               frag = DEV_BSIZE - frag;
-               base += frag;
-               size -= frag;
-               if (size < 0)
-                   size = 0;
-       }
-       pagebits = vm_page_bits(base, size & (DEV_BSIZE - 1));
-#endif
        m->dirty &= ~pagebits;
        if (base == 0 && size == PAGE_SIZE) {
                pmap_clear_modify(m);
@@ -1517,6 +1519,10 @@ void
 vm_page_clear_dirty(vm_page_t m, int base, int size)
 {
        m->dirty &= ~vm_page_bits(base, size);
+       if (base == 0 && size == PAGE_SIZE) {
+               pmap_clear_modify(m);
+               vm_page_flag_clear(m, PG_NOSYNC);
+       }
 }
 
 /*
index 22d4ed4..49ae5f2 100644 (file)
@@ -497,6 +497,7 @@ void vm_page_wire (vm_page_t);
 void vm_page_unqueue (vm_page_t);
 void vm_page_unqueue_nowakeup (vm_page_t);
 void vm_page_set_validclean (vm_page_t, int, int);
+void vm_page_set_valid (vm_page_t, int, int);
 void vm_page_set_dirty (vm_page_t, int, int);
 void vm_page_clear_dirty (vm_page_t, int, int);
 void vm_page_set_invalid (vm_page_t, int, int);
index e4f2762..6f54edb 100644 (file)
@@ -172,6 +172,41 @@ vm_page_unregister_action(vm_page_t m, vm_page_action_t action)
     }
 }
 
+/*
+ * Clear dirty bits in the VM page but truncate the
+ * end to a DEV_BSIZE'd boundary.
+ *
+ * Used when reading data in, typically via getpages.
+ * The partial device block at the end of the truncation
+ * range should not lose its dirty bit.
+ */
+static __inline
+void
+vm_page_clear_dirty_end_nonincl(vm_page_t m, int base, int size)
+{
+    size = (base + size) & ~DEV_BMASK;
+    if (base < size)
+       vm_page_clear_dirty(m, base, size - base);
+}
+
+/*
+ * Clear dirty bits in the VM page but truncate the
+ * beginning to a DEV_BSIZE'd boundary.
+ *
+ * Used when truncating a buffer.  The partial device
+ * block at the beginning of the truncation range
+ * should not lose its dirty bit.
+ */
+static __inline
+void
+vm_page_clear_dirty_beg_nonincl(vm_page_t m, int base, int size)
+{
+    size += base;
+    base = (base + DEV_BMASK) & ~DEV_BMASK;
+    if (base < size)
+       vm_page_clear_dirty(m, base, size - base);
+}
+
 #endif /* _KERNEL */
 #endif /* _VM_VM_PAGE2_H_ */
 
index fb3d2b9..ae5f4c4 100644 (file)
@@ -62,7 +62,6 @@
 #include <sys/vmmeter.h>
 #include <sys/conf.h>
 #include <sys/sfbuf.h>
-#include <sys/thread2.h>
 
 #include <vm/vm.h>
 #include <vm/vm_object.h>
@@ -72,6 +71,9 @@
 #include <vm/vnode_pager.h>
 #include <vm/vm_extern.h>
 
+#include <sys/thread2.h>
+#include <vm/vm_page2.h>
+
 static void vnode_pager_dealloc (vm_object_t);
 static int vnode_pager_getpages (vm_object_t, vm_page_t *, int, int);
 static void vnode_pager_putpages (vm_object_t, vm_page_t *, int, boolean_t, int *);
@@ -319,7 +321,6 @@ vnode_pager_setsize(struct vnode *vp, vm_ooffset_t nsize)
                                int base = (int)nsize & PAGE_MASK;
                                int size = PAGE_SIZE - base;
                                struct sf_buf *sf;
-                               int n;
 
                                /*
                                 * Clear out partial-page garbage in case
@@ -364,11 +365,7 @@ vnode_pager_setsize(struct vnode *vp, vm_ooffset_t nsize)
                                 * bit for a partial DEV_BSIZE'd truncation!
                                 * This is DEV_BSIZE aligned!
                                 */
-                               n = ((base + DEV_BMASK) & ~DEV_BMASK) - base;
-                               base += n;
-                               size -= n;
-                               if (size > 0)
-                                       vm_page_set_validclean(m, base, size);
+                               vm_page_clear_dirty_beg_nonincl(m, base, size);
                                if (m->dirty != 0)
                                        m->dirty = VM_PAGE_BITS_ALL;
                                vm_page_wakeup(m);