diff options
author | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2017-07-12 11:13:23 +0000 |
---|---|---|
committer | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2017-07-12 11:13:23 +0000 |
commit | 674a39490c64e2e8d3716dcb7dde4c5241255fc1 (patch) | |
tree | df7c2de8b0b6a0281ceed03d53d998a0e37c40bd /sys/kern/vfs_bio.c | |
parent | 127439ed2b58ab563084242d516ee4f726acdf53 (diff) |
Invalidate read-ahead buffers when read short
Buffercache performs read-ahead for cluster reads by extending
the length of an original read operation to the MAXPHYS (64k).
Upon I/O completion, the length is trimmed and the buffer is
returned to the filesystem and the remaining data is cached.
However, under certain circumstances, the underlying hardware
may fail to do a complete I/O operation and return with a non-
zero value of the residual length (i.e. data that wasn't read).
The residual length may exceed the size of an original request
and must be re-adjusted to uphold the contract with the caller,
e.g. the filesystem. At the same time, read-ahead buffers that
cover chunks of memory corresponding to the residual length
must be invalidated and not cached.
Discussed at length during d2k17, ok tedu
Diffstat (limited to 'sys/kern/vfs_bio.c')
-rw-r--r-- | sys/kern/vfs_bio.c | 22 |
1 files changed, 19 insertions, 3 deletions
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c index 95bc80bc0e6..88adfeff237 100644 --- a/sys/kern/vfs_bio.c +++ b/sys/kern/vfs_bio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_bio.c,v 1.182 2017/04/18 13:41:32 beck Exp $ */ +/* $OpenBSD: vfs_bio.c,v 1.183 2017/07/12 11:13:22 mikeb Exp $ */ /* $NetBSD: vfs_bio.c,v 1.44 1996/06/11 11:15:36 pk Exp $ */ /* @@ -536,7 +536,23 @@ bread_cluster_callback(struct buf *bp) bp->b_bcount = newsize; } - for (i = 1; xbpp[i] != 0; i++) { + /* Invalidate read-ahead buffers if read short */ + if (bp->b_resid > 0) { + for (i = 0; xbpp[i] != NULL; i++) + continue; + for (i = i - 1; i != 0; i--) { + if (xbpp[i]->b_bufsize <= bp->b_resid) { + bp->b_resid -= xbpp[i]->b_bufsize; + SET(xbpp[i]->b_flags, B_INVAL); + } else if (bp->b_resid > 0) { + bp->b_resid = 0; + SET(xbpp[i]->b_flags, B_INVAL); + } else + break; + } + } + + for (i = 1; xbpp[i] != NULL; i++) { if (ISSET(bp->b_flags, B_ERROR)) SET(xbpp[i]->b_flags, B_INVAL | B_ERROR); biodone(xbpp[i]); @@ -607,7 +623,7 @@ bread_cluster(struct vnode *vp, daddr_t blkno, int size, struct buf **rbpp) bp = xbpp[0]; - xbpp[howmany] = 0; + xbpp[howmany] = NULL; inc = btodb(size); |