wip(blocks): a BAM flush reads and writes only the pages that hold the volume's bits (#155) [skip ci]

Not yet accepted. bam_flush_to_disk() and bam_sync_from_disk() read and
wrote every page of the BAM; with the stick's BAM sized for 256 GiB (6144
devblocks, 24 MiB) every flush of a dirty block moved 24 MiB each way.
They now touch only the pages that hold the slot's bits -- its reserved
bits and one per user block (bam_pages_used()) -- the pages past them are
never set, so the bytes on disk are the same.

Test (qemu_session.py bam_flush, --target qemu-stick --stick-gib 16):
three SAVE-BUFFERS of an Artemis block, each timed, then the BAM hashed.
Before: 5.1 / 5.6 / 6.1 s (the 2 s limit fails; it was set after seeing
this baseline). After: 1.1 / 1.1 / 1.1 s. The BAM's bytes the same either
way: sha256 79197a40...b9e44b50, 5 non-zero bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
This commit is contained in:
Claude
2026-11-08 14:21:57 +00:00
parent b399cb7c35
commit 52f48ed2a9
6 changed files with 412 additions and 7 deletions
+20 -5
View File
@@ -402,6 +402,19 @@ void blk_subsys_set_format_bam_for(uint64_t total_devblocks) {
/* ===== physical BAM I/O (sync to/from slot->bam[]) ===== */
/* The BAM pages that hold a slot's bits: its reserved bits and one per user
* block (bit i is in page i / 32768). A BAM sized for a larger volume than
* the slot is -- the stick's, sized for 256 GiB -- has pages past these that
* no allocation ever sets, so they are neither read nor written: the bytes on
* disk are the same, and a flush of a 2 GiB volume no longer moves a 24 MiB
* BAM (#155). */
static uint32_t bam_pages_used(const blk_dev_slot_t *slot) {
uint64_t bits = (uint64_t) BLK_DISK_SYS_RESERVED + slot->user_blocks;
uint64_t pages = (bits + 32767ULL) / 32768ULL;
uint32_t b = slot->vol_meta.bam_devblocks;
return (pages < (uint64_t) b) ? (uint32_t) pages : b;
}
/*
* Read physical BAM from disk; populate slot->bam[i].allocated for user blocks.
* Physical bit [BLK_DISK_SYS_RESERVED + i] → slot->bam[i].allocated.
@@ -410,11 +423,12 @@ static int bam_sync_from_disk(blk_dev_slot_t *slot) {
blk_volume_meta_t *m = &slot->vol_meta;
if (!slot->dev || m->bam_devblocks == 0) return BLK_EINVAL;
size_t psize = (size_t)4096u * m->bam_devblocks;
uint32_t npages = bam_pages_used(slot);
size_t psize = (size_t)4096u * npages;
uint8_t *pbam = (uint8_t *) calloc(1, psize);
if (!pbam) return BLK_ENOMEM;
for (uint32_t i = 0; i < m->bam_devblocks; i++) {
for (uint32_t i = 0; i < npages; i++) {
uint32_t base1k = (m->bam_start + i) * 4u;
for (uint32_t k = 0; k < 4; k++) {
if (blkio_read(slot->dev, base1k + k, pbam + i*4096u + k*1024u) != BLKIO_OK)
@@ -439,12 +453,13 @@ static int bam_flush_to_disk(blk_dev_slot_t *slot) {
blk_volume_meta_t *m = &slot->vol_meta;
if (m->bam_devblocks == 0) return BLK_OK;
size_t psize = (size_t)4096u * m->bam_devblocks;
uint32_t npages = bam_pages_used(slot);
size_t psize = (size_t)4096u * npages;
uint8_t *pbam = (uint8_t *) calloc(1, psize);
if (!pbam) return BLK_ENOMEM;
/* read current on-disk BAM */
for (uint32_t i = 0; i < m->bam_devblocks; i++) {
for (uint32_t i = 0; i < npages; i++) {
uint32_t base1k = (m->bam_start + i) * 4u;
for (uint32_t k = 0; k < 4; k++) {
if (blkio_read(slot->dev, base1k + k, pbam + i*4096u + k*1024u) != BLKIO_OK)
@@ -462,7 +477,7 @@ static int bam_flush_to_disk(blk_dev_slot_t *slot) {
}
/* write back */
for (uint32_t i = 0; i < m->bam_devblocks; i++) {
for (uint32_t i = 0; i < npages; i++) {
uint32_t base1k = (m->bam_start + i) * 4u;
for (uint32_t k = 0; k < 4; k++) {
if (blkio_write(slot->dev, base1k + k, pbam + i*4096u + k*1024u) != BLKIO_OK) {