Date: Tue, 29 Sep 1998 12:12:47 +1000 From: Bruce Evans <bde@zeta.org.au> To: bugs@FreeBSD.ORG Subject: vfs_bio_clrbuf() broken for sub-page buffers Message-ID: <199809290212.MAA32074@godzilla.zeta.org.au>
next in thread | raw e-mail | index | archive | help
vfs_bio_clrbuf() seems to be quite broken for sub-page VMIO buffers at
a nonzero offset in the page. Such buffers are common for filesystems
with a sub-page blocksize, e.g., ext2fs.
The diff has a fixed(?) version ifdefed out so that the bug can be
found by the test function vfs_bio_clrbuf_check().
The unfixed version looks at the wrong `valid' bits and clears `valid'
bits that it hasn't even looked at. I don't understand why `valid'
parts of pages are guaranteed to be zero.
Bruce
diff -c2 vfs_bio.c~ vfs_bio.c
*** vfs_bio.c~ Sun Sep 27 10:59:38 1998
--- vfs_bio.c Tue Sep 29 11:54:22 1998
***************
*** 2295,2298 ****
--- 2337,2368 ----
}
+ static void
+ vfs_bio_clrbuf_check(struct buf *bp)
+ {
+ int i, n;
+ int32_t *p;
+
+ p = (int32_t *)bp->b_data;
+ n = bp->b_bcount / 4;
+ for (i = 0; i < n; i++)
+ if (p[i] != 0)
+ break;
+ if (i != n) {
+ Debugger("not clear 1");
+ clrbuf(bp);
+ }
+ if (bp->b_bufsize / 4 == n)
+ return;
+ p = (int32_t *)bp->b_data;
+ n = bp->b_bufsize / 4;
+ for (i = 0; i < n; i++)
+ if (p[i] != 0)
+ break;
+ if (i != n) {
+ clrbuf(bp);
+ Debugger("not clear 2");
+ }
+ }
+
void
vfs_bio_clrbuf(struct buf *bp) {
***************
*** 2301,2304 ****
--- 2371,2397 ----
if( (bp->b_npages == 1) && (bp->b_bufsize < PAGE_SIZE)) {
int mask;
+ u_int off;
+
+ #if 0
+ /*
+ * The mask calculation can be optimized to
+ * ((1 << x) - (1 << y)), where (I think)
+ * x = (off + bp->b_bufsize) / DEV_BSIZE and
+ * y = off / DEV_BSIZE. See also vm_page_bits()
+ * for a (probably worse) lookup table method.
+ */
+ mask = 0;
+ off = (vm_offset_t)bp->b_data & PAGE_MASK;
+ if (off + bp->b_bufsize > PAGE_SIZE)
+ panic("vfs_bio_clrbuf: page overrun");
+ for(i=0;i<bp->b_bufsize;i+=DEV_BSIZE)
+ mask |= 1 << ((off + i) / DEV_BSIZE);
+ if(((bp->b_pages[0]->flags & PG_ZERO) == 0) &&
+ (bp->b_pages[0]->valid & mask) != mask) {
+ bzero(bp->b_data, bp->b_bufsize);
+ bp->b_pages[0]->valid |= mask;
+ }
+ bp->b_resid = 0;
+ #else
mask = 0;
for(i=0;i<bp->b_bufsize;i+=DEV_BSIZE)
***************
*** 2310,2313 ****
--- 2403,2408 ----
bp->b_pages[0]->valid = mask;
bp->b_resid = 0;
+ #endif
+ vfs_bio_clrbuf_check(bp);
return;
}
***************
*** 2331,2336 ****
--- 2426,2435 ----
}
bp->b_resid = 0;
+ vfs_bio_clrbuf_check(bp);
} else {
+ if (bp->b_bcount != bp->b_bufsize)
+ Debugger("bcount != bufsize");
clrbuf(bp);
+ vfs_bio_clrbuf_check(bp);
}
}
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199809290212.MAA32074>
