diff --git a/include/blkdev.h b/include/blkdev.h index 2cd2a1d..3c0a28a 100644 --- a/include/blkdev.h +++ b/include/blkdev.h @@ -42,6 +42,7 @@ int blkdev_register(const char *name, const struct blkdev_ops *ops, // blocks. A filesystem still mounted from it fails on its next access. void blkdev_unregister(int dev); void binval(uint dev); // kernel/dev/bio.c +struct buf *bgetblk(uint dev, uint64 blockno); // bread() without the read int blkdev_present(uint dev); void blkdev_rw(struct buf *b); diff --git a/kernel/dev/bio.c b/kernel/dev/bio.c index 60ef676..7b6d68d 100644 --- a/kernel/dev/bio.c +++ b/kernel/dev/bio.c @@ -224,6 +224,20 @@ bread(uint dev, uint64 blockno) return b; } +// A locked buf for a block the caller is about to overwrite whole, without +// reading what is there first: Linux's getblk(). The caller must fill every +// byte and then bdirty() it. +// +// A copy-on-write filesystem writes almost nothing but blocks it has just +// allocated, and reading each one off the disk only to replace it was every +// fourth disk operation of a SchFS write. +struct buf* +bgetblk(uint dev, uint64 blockno) +{ + fsstat.bread_calls++; + return bget(dev, blockno); +} + // Write b's contents to disk. Must be locked. void bwrite(struct buf *b) diff --git a/kernel/fs/schfs/kmount.c b/kernel/fs/schfs/kmount.c index ec34c38..e8423c0 100644 --- a/kernel/fs/schfs/kmount.c +++ b/kernel/fs/schfs/kmount.c @@ -51,6 +51,7 @@ #include "errno.h" /* mounting reports why it failed, not just that it did */ #include "x86.h" #include "fsstat.h" +#include "blkdev.h" /* bgetblk */ #include "schfs_int.h" // How many buffer-cache blocks one filesystem block spans. BSIZE was 1024 when @@ -392,9 +393,9 @@ kd_write(void *ctx, uint64_t blk, const void *in) // modifying a block the committed tree can reach, so logging every write // would double the I/O to re-solve a problem the format has already solved. // - // bread before the overwrite because the cache has no "give me this block - // without reading it" entry point. It costs a read the first time a block is - // touched and nothing afterwards. + // bgetblk, not bread: the block is overwritten whole, so what the disk held + // there does not matter. Reading it first cost a disk read for every block + // a write allocated, which is most of them. // // bdirty, not bwrite. The B-tree seals and writes a node on every record it // inserts, so a leaf that is collecting one file's checksums was written to @@ -403,7 +404,7 @@ kd_write(void *ctx, uint64_t blk, const void *in) // into the last one, and costs nothing in safety because kd_flush below // gives the format the one ordering guarantee it actually asks for. for (int i = 0; i < SCHFS_BPB; i++) { - struct buf *b = bread(d->dev, (uint)(blk * SCHFS_BPB + i)); + struct buf *b = bgetblk(d->dev, (uint)(blk * SCHFS_BPB + i)); memmove(b->data, (const char *)in + i * BSIZE, BSIZE); bdirty(b); brelse(b);