From: Matthew Dillon Date: Mon, 24 Aug 2009 23:27:32 +0000 (-0700) Subject: Kernel - more NFS fixes, more dirty bit fixes, remove vfs_bio_set_validclean() X-Git-Url: https://gitweb.dragonflybsd.org/~syl/dragonfly.git/commitdiff_plain/1a54183b97974fc4fb7e564cf05a7e94db2a31c6 Kernel - more NFS fixes, more dirty bit fixes, remove vfs_bio_set_validclean() * 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). --- diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index 622a2a2eba..151f9f4549 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -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: diff --git a/sys/sys/buf.h b/sys/sys/buf.h index 3fe1417d3b..7392e3796e 100644 --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -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 *); diff --git a/sys/vfs/devfs/devfs_vnops.c b/sys/vfs/devfs/devfs_vnops.c index a23f367bc3..0adba2de2b 100644 --- a/sys/vfs/devfs/devfs_vnops.c +++ b/sys/vfs/devfs/devfs_vnops.c @@ -50,18 +50,19 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include #include +#include +#include +#include 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); diff --git a/sys/vfs/nfs/nfs_bio.c b/sys/vfs/nfs/nfs_bio.c index 705811cc24..573b90dca5 100644 --- a/sys/vfs/nfs/nfs_bio.c +++ b/sys/vfs/nfs/nfs_bio.c @@ -60,6 +60,7 @@ #include #include +#include #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); } /* diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index e45ad1928c..0a0b907072 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -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); + } } /* diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h index 22d4ed4a31..49ae5f2e8b 100644 --- a/sys/vm/vm_page.h +++ b/sys/vm/vm_page.h @@ -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); diff --git a/sys/vm/vm_page2.h b/sys/vm/vm_page2.h index e4f2762992..6f54edb26d 100644 --- a/sys/vm/vm_page2.h +++ b/sys/vm/vm_page2.h @@ -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_ */ diff --git a/sys/vm/vnode_pager.c b/sys/vm/vnode_pager.c index fb3d2b97a7..ae5f4c4926 100644 --- a/sys/vm/vnode_pager.c +++ b/sys/vm/vnode_pager.c @@ -62,7 +62,6 @@ #include #include #include -#include #include #include @@ -72,6 +71,9 @@ #include #include +#include +#include + 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);