ramdisk: the live root grows into half of memory
The live root was a fixed 96 MB image in the kernel: 60 MB of zeros on the ISO, twice, and room for 60 MB of packages whatever the machine had. Now the image is only what the root holds, plus slack, measured by building it once large. Past its end the ramdisk goes on to half of the memory free at mount, as pages taken the first time each block is written; an unwritten block reads as zeros and costs nothing. SchFS keeps no free-space map on disk, so growing a volume is a larger total before open builds the map: grow_to, set for the ramdisk root. 8 GB machine: a 4 GB root, Xorg installed into it in 77 s with 7.6 GB still free. The ISO is 172 MB instead of 344. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
3f8e5190aa
commit
d0941780f5
7 changed files with 120 additions and 13 deletions
|
|
@ -14,6 +14,19 @@
|
|||
//
|
||||
// Enabled by building with RAMROOT=1. Without it this file still compiles, the
|
||||
// device reports zero size, and the kernel boots from the IDE disk as before.
|
||||
//
|
||||
// The device is larger than the image. The image is only what the root holds,
|
||||
// with a little slack; past its end the device goes on to half of the memory
|
||||
// free at mount time, and those blocks are pages taken from kalloc the first
|
||||
// time each is written -- a block nobody has written reads as zeros and costs
|
||||
// nothing. SchFS grows the volume to the device's size when it mounts (see
|
||||
// grow_to in kernel/fs/schfs/vol.c), so `apk add` on a live system has as
|
||||
// much room as the machine has memory to give, rather than whatever empty
|
||||
// space was built into the ISO. The ISO used to carry 60 MB of zeros for it.
|
||||
//
|
||||
// Pages are not given back when SchFS frees a block: there is no discard.
|
||||
// A freed block is reused by the next write, so the cost is the high-water
|
||||
// mark, not a leak.
|
||||
|
||||
#include "core/types.h"
|
||||
#include "core/defs.h"
|
||||
|
|
@ -33,11 +46,23 @@ extern uchar _binary_ramdisk_size[];
|
|||
|
||||
static uchar *rd_base;
|
||||
static uint64 rd_size; // bytes
|
||||
static uint rd_blocks; // rd_size / BSIZE
|
||||
static uint rd_blocks; // rd_size / BSIZE: the image, served in place
|
||||
|
||||
// Past the image: rd_dir[i] is a page of pointers, entry j the page holding
|
||||
// block rd_blocks + i*RD_PTRS + j, or 0 for one never written. 32768 pages of
|
||||
// 512 pointers reach 64 GB, which is as far as half of any machine this runs
|
||||
// on is likely to go, and costs 256 KB of pointers here.
|
||||
#define RD_PTRS (PGSIZE / sizeof(uchar *))
|
||||
#define RD_DIRS 32768
|
||||
static uchar **rd_dir[RD_DIRS];
|
||||
static uint64 rd_total; // blocks, image and the rest; 0 until sized
|
||||
static uint64 rd_pages; // pages taken for blocks past the image
|
||||
static struct spinlock rd_lock;
|
||||
|
||||
void
|
||||
ramdiskinit(void)
|
||||
{
|
||||
initlock(&rd_lock, "ramdisk");
|
||||
#ifdef RAMDISK_ROOT
|
||||
rd_base = _binary_ramdisk_start;
|
||||
rd_size = (uint64)_binary_ramdisk_size;
|
||||
|
|
@ -66,6 +91,53 @@ ramdisk_present(void)
|
|||
return rd_blocks != 0;
|
||||
}
|
||||
|
||||
// The device's size in blocks, fixed the first time anyone asks -- which is
|
||||
// SchFS mounting the root, after every page has reached the allocator. Asked
|
||||
// earlier, during ramdiskinit, the free count would be the first few
|
||||
// megabytes kinit1 handed over and nothing more.
|
||||
uint64
|
||||
ramdisk_blocks(void)
|
||||
{
|
||||
if (rd_blocks == 0)
|
||||
return 0;
|
||||
acquire(&rd_lock);
|
||||
if (rd_total == 0) {
|
||||
uint64 extra = kalloc_free_bytes() / 2 / BSIZE;
|
||||
if (extra > (uint64)RD_DIRS * RD_PTRS)
|
||||
extra = (uint64)RD_DIRS * RD_PTRS;
|
||||
rd_total = rd_blocks + extra;
|
||||
cprintf("ramdisk: %u MB image, %u MB of memory to grow into\n",
|
||||
(uint)(rd_size >> 20), (uint)(extra * BSIZE >> 20));
|
||||
}
|
||||
release(&rd_lock);
|
||||
return rd_total;
|
||||
}
|
||||
|
||||
// The page behind a block past the image, made when `make` says so.
|
||||
static uchar *
|
||||
rd_page(uint64 blk, int make)
|
||||
{
|
||||
uint64 i = (blk - rd_blocks) / RD_PTRS, j = (blk - rd_blocks) % RD_PTRS;
|
||||
uchar *pg = 0;
|
||||
|
||||
acquire(&rd_lock);
|
||||
if (rd_dir[i] == 0 && make) {
|
||||
if ((rd_dir[i] = (uchar **)kalloc()) != 0)
|
||||
memset(rd_dir[i], 0, PGSIZE);
|
||||
}
|
||||
if (rd_dir[i]) {
|
||||
pg = rd_dir[i][j];
|
||||
if (pg == 0 && make && (pg = (uchar *)kalloc()) != 0) {
|
||||
rd_dir[i][j] = pg;
|
||||
rd_pages++;
|
||||
}
|
||||
}
|
||||
release(&rd_lock);
|
||||
if (make && pg == 0)
|
||||
panic("ramdisk: out of memory for the live root");
|
||||
return pg;
|
||||
}
|
||||
|
||||
// Sync buf with the in-memory image. Mirrors iderw()'s contract: on a read,
|
||||
// fill b->data and set B_VALID; on a write, copy out and clear B_DIRTY.
|
||||
void
|
||||
|
|
@ -77,8 +149,21 @@ ramdiskrw(struct buf *b)
|
|||
panic("ramdiskrw: nothing to do");
|
||||
if(rd_base == 0)
|
||||
panic("ramdiskrw: no ramdisk");
|
||||
if(b->blockno >= rd_blocks)
|
||||
panic("ramdiskrw: block out of range");
|
||||
if(b->blockno >= rd_blocks){
|
||||
if(b->blockno >= ramdisk_blocks())
|
||||
panic("ramdiskrw: block out of range");
|
||||
uchar *pg = rd_page(b->blockno, (b->flags & B_DIRTY) != 0);
|
||||
if(b->flags & B_DIRTY){
|
||||
memmove(pg, b->data, BSIZE);
|
||||
b->flags &= ~B_DIRTY;
|
||||
} else if(pg){
|
||||
memmove(b->data, pg, BSIZE);
|
||||
} else {
|
||||
memset(b->data, 0, BSIZE); // never written
|
||||
}
|
||||
b->flags |= B_VALID;
|
||||
return;
|
||||
}
|
||||
|
||||
uchar *p = rd_base + (uint64)b->blockno * BSIZE;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue