SchOS/include/memlayout.h
Nexoniarz b70e36e0a2 kalloc: never-used memory stays a range; 256 GB; free is a counter
Every page of RAM went onto the free list at boot, which means writing
each one -- a link, and kfree()'s 4 KB of junk -- before the first
process ran: 64 GB of memset on a 64 GB machine to say it was free.
Memory nobody has used yet is kept as a cursor and an end per region
now, and kalloc() takes from those once the list of given-back pages
is empty. A page is first touched by whoever asked for it.

That makes the cap on RAM cheap to raise, and PHYSTOP_MAX goes from 64
to 256 GB, which still leaves 248 GB of the kernel PDPT for device
windows. MEMREGION_MAX goes from 32 to 64 for the firmware maps that
cut RAM into many pieces.

kalloc_free_bytes() walked the whole free list under the allocator's
lock -- two million links on 8 GB, every second top or fastfetch was
running, with every CPU that wanted a page waiting behind it. It reads
a counter now. And the buffer cache, which keeps what it grows to, is
reported as Buffers in /proc/meminfo and sysinfo(2) rather than as
memory used by nothing.

Checked under QEMU at 1, 2 and 6 GB: MemFree right, zsh splitting
200000 words, fork loops, a 74 MB write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 14:05:40 +02:00

98 lines
4 KiB
C

#pragma once
// Memory layout
#define EXTMEM 0x100000 // Start of extended memory
// Top of usable physical memory. A *variable*, read from the firmware's E820
// map at boot -- see kernel/arch/x86_64/e820.c. It used to be the constant
// below, which was not a limit on what the kernel used but an assertion that
// exactly 224 MB existed: less and the allocator walked off the end of RAM,
// more and it was invisible.
//
// The macro spelling is kept so that the twenty-odd call sites read the same as
// they always did. It must not be used before e820_init(), which runs first
// thing in main() for that reason.
#ifndef __ASSEMBLER__
extern uint64 phystop;
#define PHYSTOP phystop
#endif
// PHYSTOP alone describes memory as one run ending at a top address, which is
// what a BIOS map usually looks like and what UEFI never does: OVMF drops an
// ACPI NVS region at 0x800000, so the run containing the kernel ends at 8 MB
// and everything above the hole was invisible. The usable regions are
// therefore kept as a list as well, and kinit1() frees each one rather than
// walking a single range from the kernel's end to the top.
//
// PHYSTOP still means what it always did -- the top of what kalloc may hand
// out, and the bound kfree() rejects pages above -- so every existing call
// site is unaffected.
#define MEMREGION_MAX 64
#ifndef __ASSEMBLER__
struct memregion {
uint64 base; // first byte, page aligned
uint64 end; // one past the last byte, page aligned
};
extern struct memregion mem_usable[MEMREGION_MAX];
extern int nmem_usable;
// The top of the low region: where the DMA arena is carved from, and the
// ceiling for kinit1(). Always below DIRECTMAP_BOOT, and separate from
// `phystop` because a device is not obliged to be able to reach the top of
// memory -- an RTL8139's buffer address register is thirty-two bits wide.
extern uint64 lowtop;
#endif
// What to assume when the firmware gives us nothing usable: the old constant,
// so that path behaves exactly as the kernel always did.
#define PHYSTOP_FALLBACK 0xE000000ULL
// The most RAM the kernel will use.
//
// This was 0xC0000000 -- three gigabytes -- because kvmalloc() mapped exactly
// four 1 GB pages and the fourth was marked uncached for the PCI hole. On a
// laptop with sixteen gigabytes that threw away thirteen of them, silently.
//
// kvmalloc() now maps as many 1 GB pages as the machine has RAM, so the limit
// is the page table rather than a constant: the kernel PDPT has 512 entries
// and ioremap takes what the direct map does not, so 256 GB of RAM still
// leaves 248 GB of device window. A machine with more than this gets the rest
// ignored, with a line saying so.
//
// It was 64 GB while handing memory to the allocator meant writing every page
// of it at boot. kalloc.c keeps never-used memory as ranges now, so a bigger
// machine costs nothing until its memory is used.
#define PHYSTOP_MAX (256ULL << 30)
// What the *loader's* page tables cover, and therefore the ceiling on anything
// that runs before kvmalloc() builds the kernel's own.
//
// kinit1() frees pages by writing a free-list pointer into each one, so every
// page it touches has to be mapped at the time. Before kvmalloc that means the
// low four gigabytes and nothing else, which is why freeing memory happens in
// two passes -- see kinit_high().
#define DIRECTMAP_BOOT (4ULL << 30)
// Below this the kernel image, its page tables and the DMA arena do not fit.
#define PHYSTOP_MIN (24ULL * 1024 * 1024)
// Key addresses for address space layout
#define KERNBASE 0xFFFF800000000000 // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
#ifndef __ASSEMBLER__
static inline addr_t v2p(void *a) {
return ((addr_t)(a)) - ((addr_t)KERNBASE);
}
static inline void *p2v(addr_t a) {
return (void *)((a) + ((addr_t)KERNBASE));
}
#endif
#define V2P(a) (((addr_t) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)
#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE) // same as P2V, but without casts