The cache was NBUF buffers, 1 MB, on every machine: enough for one with tens of megabytes and nothing on one with gigabytes, which re-read the same directory and binary blocks off the disk for every command. It still starts as those 256 in the kernel image, and a miss with every buffer busy now makes a new one rather than evicting, up to 1/32 of RAM or 256 MB. Growing on demand means nothing is spent that is not used, and binit() needs nothing from the page allocator. Lookups go through a hash on (dev, blockno) instead of walking the LRU list, and bflush() walks a creation-order list with an unlocked filter first, so neither costs more at 65536 buffers than it did at 256. struct buf's data is a pointer to its own page now; the drivers copy through their own DMA buffers, so where it sits does not matter. On a 2 GB QEMU guest: a 40 MB file read back in 0.89 s instead of 1.83, the big binaries in 0.17 s instead of 0.26. /proc/fsstat shows the cache as buf_cached now / most. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
26 lines
1 KiB
C
26 lines
1 KiB
C
struct buf {
|
|
int flags;
|
|
uint dev;
|
|
// 64-bit, because the filesystem's block numbers are. This was a uint, which
|
|
// capped any device at 2^32 blocks (16 TB at 4 KB) no matter what the format
|
|
// above it could address -- SchFS is a 64-bit format and was being served by
|
|
// a 32-bit cache.
|
|
uint64 blockno;
|
|
struct sleeplock lock;
|
|
uint refcnt;
|
|
struct buf *prev; // LRU cache list
|
|
struct buf *next;
|
|
struct buf *qnext; // disk queue
|
|
struct buf *hnext; // hash chain, by (dev, blockno)
|
|
struct buf *anext; // every buffer, in the order made; never reordered
|
|
// BSIZE bytes, a page of its own. A pointer rather than an array since the
|
|
// cache grows at run time (see bio.c); drivers copy through their own DMA
|
|
// buffers, so where the page sits does not matter to them.
|
|
uchar *data;
|
|
};
|
|
#define B_VALID 0x2 // buffer has been read from disk
|
|
#define B_DIRTY 0x4 // buffer needs to be written to disk
|
|
|
|
|
|
// How many buffers the cache holds now, and the most it will grow to. bio.c.
|
|
void bcache_size(uint *n, uint *max);
|