schfs: do not read a block off the disk just to overwrite it

kd_write() went through bread(), so every block a write allocated was
read from the disk first and then replaced whole. That was a quarter of
the disk operations of a SchFS write. bgetblk() is bread() without the
read, as Linux's getblk() is.

1 MB on AHCI under QEMU: 2.0 -> 2.8 MB/s in 4 KB writes, 4.5 -> 5.6
MB/s in 64 KB ones. 3 MB of random data written and copied through the
cache's evictions reads back with the same md5.

The 63 KB/s seen earlier is the legacy IDE driver on QEMU's default
machine, at about 10 ms per emulated PIO write; the same disk on AHCI
(q35) is 40 times faster.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-23 11:02:44 +02:00
commit b213ab5778
3 changed files with 20 additions and 4 deletions

View file

@ -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)