summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_bio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/vfs_bio.c')
-rw-r--r--sys/kern/vfs_bio.c152
1 files changed, 102 insertions, 50 deletions
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index d5f6a9d..0023dca 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -18,7 +18,7 @@
* 5. Modifications may be freely made to this file if the above conditions
* are met.
*
- * $Id: vfs_bio.c,v 1.114 1997/04/13 03:33:25 dyson Exp $
+ * $Id: vfs_bio.c,v 1.115 1997/05/10 09:09:42 joerg Exp $
*/
/*
@@ -77,6 +77,10 @@ static void vm_hold_free_pages(struct buf * bp, vm_offset_t from,
vm_offset_t to);
static void vm_hold_load_pages(struct buf * bp, vm_offset_t from,
vm_offset_t to);
+static void vfs_buf_set_valid(struct buf *bp, vm_ooffset_t foff,
+ vm_offset_t off, vm_offset_t size,
+ vm_page_t m);
+static void vfs_page_set_valid(struct buf *bp, vm_offset_t off, vm_page_t m);
static void vfs_clean_pages(struct buf * bp);
static void vfs_setdirty(struct buf *bp);
static void vfs_vmio_release(struct buf *bp);
@@ -501,8 +505,19 @@ brelse(struct buf * bp)
* constituted, so the B_INVAL flag is used to *invalidate* the buffer,
* but the VM object is kept around. The B_NOCACHE flag is used to
* invalidate the pages in the VM object.
+ *
+ * If the buffer is a partially filled NFS buffer, keep it
+ * since invalidating it now will lose informatio. The valid
+ * flags in the vm_pages have only DEV_BSIZE resolution but
+ * the b_validoff, b_validend fields have byte resolution.
+ * This can avoid unnecessary re-reads of the buffer.
*/
- if (bp->b_flags & B_VMIO) {
+ if ((bp->b_flags & B_VMIO)
+ && (bp->b_vp->v_tag != VT_NFS
+ || (bp->b_flags & (B_NOCACHE | B_INVAL | B_ERROR))
+ || bp->b_validend == 0
+ || (bp->b_validoff == 0
+ && bp->b_validend == bp->b_bufsize))) {
vm_ooffset_t foff;
vm_object_t obj;
int i, resid;
@@ -1418,6 +1433,7 @@ allocbuf(struct buf * bp, int size)
curbpnpages = bp->b_npages;
doretry:
bp->b_flags |= B_CACHE;
+ bp->b_validoff = bp->b_validend = 0;
for (toff = 0; toff < newbsize; toff += tinc) {
int bytesinpage;
@@ -1433,12 +1449,8 @@ allocbuf(struct buf * bp, int size)
bytesinpage = tinc;
if (tinc > (newbsize - toff))
bytesinpage = newbsize - toff;
- if ((bp->b_flags & B_CACHE) &&
- !vm_page_is_valid(m,
- (vm_offset_t) ((toff + off) & PAGE_MASK),
- bytesinpage)) {
- bp->b_flags &= ~B_CACHE;
- }
+ if (bp->b_flags & B_CACHE)
+ vfs_buf_set_valid(bp, off, toff, bytesinpage, m);
continue;
}
m = vm_page_lookup(obj, objoff);
@@ -1474,17 +1486,15 @@ allocbuf(struct buf * bp, int size)
bytesinpage = tinc;
if (tinc > (newbsize - toff))
bytesinpage = newbsize - toff;
- if ((bp->b_flags & B_CACHE) &&
- !vm_page_is_valid(m,
- (vm_offset_t) ((toff + off) & PAGE_MASK),
- bytesinpage)) {
- bp->b_flags &= ~B_CACHE;
- }
+ if (bp->b_flags & B_CACHE)
+ vfs_buf_set_valid(bp, off, toff, bytesinpage, m);
vm_page_wire(m);
}
bp->b_pages[pageindex] = m;
curbpnpages = pageindex + 1;
}
+ if (vp->v_tag == VT_NFS && bp->b_validend == 0)
+ bp->b_flags &= ~B_CACHE;
bp->b_data = (caddr_t) trunc_page(bp->b_data);
bp->b_npages = curbpnpages;
pmap_qenter((vm_offset_t) bp->b_data,
@@ -1562,7 +1572,7 @@ biodone(register struct buf * bp)
}
if (bp->b_flags & B_VMIO) {
int i, resid;
- vm_ooffset_t foff;
+ vm_ooffset_t foff, bfoff;
vm_page_t m;
vm_object_t obj;
int iosize;
@@ -1572,6 +1582,7 @@ biodone(register struct buf * bp)
foff = (vm_ooffset_t) DEV_BSIZE * bp->b_lblkno;
else
foff = (vm_ooffset_t) vp->v_mount->mnt_stat.f_iosize * bp->b_lblkno;
+ bfoff = foff;
obj = vp->v_object;
if (!obj) {
panic("biodone: no object");
@@ -1613,8 +1624,7 @@ biodone(register struct buf * bp)
* here in the read case.
*/
if ((bp->b_flags & B_READ) && !bogusflag && resid > 0) {
- vm_page_set_validclean(m,
- (vm_offset_t) (foff & PAGE_MASK), resid);
+ vfs_page_set_valid(bp, foff - bfoff, m);
}
/*
@@ -1761,6 +1771,73 @@ vfs_unbusy_pages(struct buf * bp)
}
/*
+ * Set NFS' b_validoff and b_validend fields from the valid bits
+ * of a page. If the consumer is not NFS, and the page is not
+ * valid for the entire range, clear the B_CACHE flag to force
+ * the consumer to re-read the page.
+ */
+static void
+vfs_buf_set_valid(struct buf *bp,
+ vm_ooffset_t foff, vm_offset_t off, vm_offset_t size,
+ vm_page_t m)
+{
+ if (bp->b_vp->v_tag == VT_NFS) {
+ vm_offset_t svalid, evalid;
+ int validbits = m->valid;
+
+ /*
+ * This only bothers with the first valid range in the
+ * page.
+ */
+ svalid = off;
+ while (validbits && !(validbits & 1)) {
+ svalid += DEV_BSIZE;
+ validbits >>= 1;
+ }
+ evalid = svalid;
+ while (validbits & 1) {
+ evalid += DEV_BSIZE;
+ validbits >>= 1;
+ }
+ /*
+ * Make sure this range is contiguous with the range
+ * built up from previous pages. If not, then we will
+ * just use the range from the previous pages.
+ */
+ if (svalid == bp->b_validend) {
+ bp->b_validoff = min(bp->b_validoff, svalid);
+ bp->b_validend = max(bp->b_validend, evalid);
+ }
+ } else if (!vm_page_is_valid(m,
+ (vm_offset_t) ((foff + off) & PAGE_MASK),
+ size)) {
+ bp->b_flags &= ~B_CACHE;
+ }
+}
+
+/*
+ * Set the valid bits in a page, taking care of the b_validoff,
+ * b_validend fields which NFS uses to optimise small reads. Off is
+ * the offset of the page within the buf.
+ */
+static void
+vfs_page_set_valid(struct buf *bp, vm_offset_t off, vm_page_t m)
+{
+ struct vnode *vp = bp->b_vp;
+ vm_offset_t soff, eoff;
+
+ soff = off;
+ eoff = min(off + PAGE_SIZE, bp->b_bufsize);
+ if (vp->v_tag == VT_NFS) {
+ soff = max((bp->b_validoff + DEV_BSIZE - 1) & -DEV_BSIZE, soff);
+ eoff = min(bp->b_validend & -DEV_BSIZE, eoff);
+ }
+ vm_page_set_invalid(m, 0, PAGE_SIZE);
+ if (eoff > soff)
+ vm_page_set_validclean(m, soff, eoff - soff);
+}
+
+/*
* This routine is called before a device strategy routine.
* It is used to tell the VM system that paging I/O is in
* progress, and treat the pages associated with the buffer
@@ -1775,36 +1852,25 @@ vfs_busy_pages(struct buf * bp, int clear_modify)
if (bp->b_flags & B_VMIO) {
vm_object_t obj = bp->b_vp->v_object;
- vm_ooffset_t foff;
- int iocount = bp->b_bufsize;
+ vm_offset_t off;
- if (bp->b_vp->v_type == VBLK)
- foff = (vm_ooffset_t) DEV_BSIZE * bp->b_lblkno;
- else
- foff = (vm_ooffset_t) bp->b_vp->v_mount->mnt_stat.f_iosize * bp->b_lblkno;
vfs_setdirty(bp);
- for (i = 0; i < bp->b_npages; i++) {
+ for (i = 0, off = 0; i < bp->b_npages; i++, off += PAGE_SIZE) {
vm_page_t m = bp->b_pages[i];
- int resid = IDX_TO_OFF(m->pindex + 1) - foff;
- if (resid > iocount)
- resid = iocount;
if ((bp->b_flags & B_CLUSTER) == 0) {
obj->paging_in_progress++;
m->busy++;
}
vm_page_protect(m, VM_PROT_NONE);
- if (clear_modify) {
- vm_page_set_validclean(m,
- (vm_offset_t) (foff & PAGE_MASK), resid);
- } else if (bp->b_bcount >= PAGE_SIZE) {
+ if (clear_modify)
+ vfs_page_set_valid(bp, off, m);
+ else if (bp->b_bcount >= PAGE_SIZE) {
if (m->valid && (bp->b_flags & B_CACHE) == 0) {
bp->b_pages[i] = bogus_page;
pmap_qenter(trunc_page(bp->b_data), bp->b_pages, bp->b_npages);
}
}
- foff += resid;
- iocount -= resid;
}
}
}
@@ -1820,26 +1886,12 @@ vfs_clean_pages(struct buf * bp)
int i;
if (bp->b_flags & B_VMIO) {
- vm_ooffset_t foff;
- int iocount = bp->b_bufsize;
-
- if (bp->b_vp->v_type == VBLK)
- foff = (vm_ooffset_t) DEV_BSIZE * bp->b_lblkno;
- else
- foff = (vm_ooffset_t) bp->b_vp->v_mount->mnt_stat.f_iosize * bp->b_lblkno;
+ vm_offset_t off;
- for (i = 0; i < bp->b_npages; i++) {
+ for (i = 0, off = 0; i < bp->b_npages; i++, off += PAGE_SIZE) {
vm_page_t m = bp->b_pages[i];
- int resid = IDX_TO_OFF(m->pindex + 1) - foff;
- if (resid > iocount)
- resid = iocount;
- if (resid > 0) {
- vm_page_set_validclean(m,
- ((vm_offset_t) foff & PAGE_MASK), resid);
- }
- foff += resid;
- iocount -= resid;
+ vfs_page_set_valid(bp, off, m);
}
}
}
OpenPOWER on IntegriCloud