feat: extend slab with KmemCache and SpinLock
This commit is contained in:
1 parent
8d2b1df100
commit
98e09f13f8
4 files changed
+199
No files matched your search
@@ -0,0 +1,55 @@
|
||||
#ifndef FROSTBYTE_SLAB_H
|
||||
#define FROSTBYTE_SLAB_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
* Slab: a fixed-size object cache backed directly by physical frames from
|
||||
* pmm, bypassing the general-purpose heap. Each slab is exactly one frame;
|
||||
* the Slab descriptor lives at the start of that same frame (so freeing a
|
||||
* pointer is O(1): mask it down to the enclosing frame to recover the
|
||||
* descriptor, no list walk needed), and the rest of the frame is carved
|
||||
* into objSize-aligned objects threaded onto a singly-linked free list
|
||||
* through their own (currently-unused) storage.
|
||||
*
|
||||
* Intended for kernel-internal fixed-size allocations (list/tree nodes,
|
||||
* PCB-sized structs, ...) where slab's O(1) alloc/free and per-slab
|
||||
* locality beat the heap's general-purpose free-list search.
|
||||
*/
|
||||
|
||||
typedef struct KmemCache {
|
||||
const char* name;
|
||||
usize objSize; /* rounded up to at least sizeof(void*), 8-aligned */
|
||||
usize objsPerSlab;
|
||||
struct Slab* slabs; /* every slab owned by this cache, newest first */
|
||||
usize slabCount;
|
||||
usize usedObjs;
|
||||
} KmemCache;
|
||||
|
||||
/* Returns false if objSize (after rounding) cannot fit at least one object
|
||||
per frame alongside the slab header -- the cache is left zeroed either
|
||||
way, so kmemCacheDestroy is always safe to call. */
|
||||
bool kmemCacheInit(KmemCache* cache, const char* name, usize objSize);
|
||||
|
||||
/* Releases every frame the cache ever grew into back to pmm. The cache is
|
||||
left in a freshly-initialized (empty, growable-again) state. */
|
||||
void kmemCacheDestroy(KmemCache* cache);
|
||||
|
||||
/* Grows the cache (allocates a new frame from pmm) on demand. nullptr only
|
||||
if pmm is out of frames. */
|
||||
void* kmemCacheAlloc(KmemCache* cache);
|
||||
|
||||
/* obj must have come from kmemCacheAlloc on this exact cache and not
|
||||
already be free. */
|
||||
void kmemCacheFree(KmemCache* cache, void* obj);
|
||||
|
||||
usize kmemCacheUsed(const KmemCache* cache);
|
||||
/* Objects across every slab the cache has grown into so far -- 0 until the
|
||||
first kmemCacheAlloc, since slabs are only ever grown lazily on demand. */
|
||||
usize kmemCacheCapacity(const KmemCache* cache);
|
||||
usize kmemCacheSlabCount(const KmemCache* cache);
|
||||
/* How many objects a single slab (frame) holds for this cache -- fixed at
|
||||
kmemCacheInit time, independent of how many slabs have been grown. */
|
||||
usize kmemCacheObjsPerSlab(const KmemCache* cache);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef FROSTBYTE_SPINLOCK_H
|
||||
#define FROSTBYTE_SPINLOCK_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
* SpinLock: an interrupt-safe mutual-exclusion lock for state shared between
|
||||
* thread context and interrupt handlers (irq0/irq1 currently run with
|
||||
* interrupts re-enabled per pic.c, so a naive "just disable interrupts"
|
||||
* critical section is not enough once two handlers can nest).
|
||||
*
|
||||
* Acquire disables interrupts and spins on an atomic test-and-set before
|
||||
* returning; release restores whatever the interrupt flag was at the
|
||||
* matching acquire. This is correct and SMP-ready even though frostbyte is
|
||||
* currently single-core: on one core, acquiring an already-held lock is a
|
||||
* bug (it would spin forever with interrupts off), so spinLockTryAcquire is
|
||||
* provided for call sites -- and tests -- that need to observe contention
|
||||
* without deadlocking.
|
||||
*/
|
||||
|
||||
typedef struct SpinLock {
|
||||
volatile uint32 locked;
|
||||
} SpinLock;
|
||||
|
||||
void spinLockInit(SpinLock* lock);
|
||||
|
||||
/* Disables interrupts, then spins until the lock is acquired. Returns the
|
||||
interrupt flag as it was immediately before this call, to be passed back
|
||||
to spinLockRelease unchanged (callers must not interleave two different
|
||||
locks' flags). */
|
||||
uint64 spinLockAcquire(SpinLock* lock);
|
||||
|
||||
/* Non-blocking. On success (true), interrupts are disabled and *savedFlags
|
||||
holds the pre-call flag word, exactly as spinLockAcquire would leave
|
||||
things. On failure (false) nothing is touched -- interrupts are left
|
||||
exactly as the caller had them. */
|
||||
bool spinLockTryAcquire(SpinLock* lock, uint64* savedFlags);
|
||||
|
||||
/* flags must be the value spinLockAcquire/spinLockTryAcquire handed back. */
|
||||
void spinLockRelease(SpinLock* lock, uint64 flags);
|
||||
|
||||
bool spinLockIsLocked(const SpinLock* lock);
|
||||
|
||||
#endif
|
||||
@@ -15,5 +15,59 @@
|
||||
#define sysGetdents 11
|
||||
#define sysMkdir 12
|
||||
#define sysUnlink 13
|
||||
#define sysPipe 14
|
||||
#define sysSymlink 15
|
||||
#define sysReadlink 16
|
||||
#define sysLink 17
|
||||
#define sysExec 18
|
||||
#define sysSpawn 19
|
||||
#define sysWait 20
|
||||
#define sysPipeCreate 21
|
||||
#define sysPipeAttach 22
|
||||
#define sysKill 23
|
||||
#define sysMkfifo 24
|
||||
#define sysSigSend 25
|
||||
#define sysSigAction 26
|
||||
#define sysSigProcMask 27
|
||||
#define sysSigReturn 28
|
||||
#define sysAlarm 29
|
||||
#define sysSetpgid 30
|
||||
#define sysGetpgid 31
|
||||
#define sysSetFgPgrp 32
|
||||
#define sysGetFgPgrp 33
|
||||
#define sysPoll 34
|
||||
#define sysChdir 35
|
||||
#define sysGetcwd 36
|
||||
#define sysGetEnv 37
|
||||
#define sysSetEnv 38
|
||||
#define sysNice 39
|
||||
#define sysGetPriority 40
|
||||
#define sysTime 41
|
||||
#define sysUnsetEnv 42
|
||||
#define sysRmdir 43
|
||||
#define sysChmod 44
|
||||
#define sysGetuid 45
|
||||
#define sysSetuid 46
|
||||
#define sysChown 47
|
||||
#define sysGetgid 48
|
||||
#define sysSetgid 49
|
||||
#define sysChgrp 50
|
||||
#define sysPidfdOpen 51
|
||||
#define sysPidfdSend 52
|
||||
#define sysPidfdWait 53
|
||||
#define sysMmap 54
|
||||
#define sysMunmap 55
|
||||
|
||||
/* Sentinel pid for sysWait (syscall.c)/userWaitAny (ulib.c): real
|
||||
waitpid(-1)-style "reap whichever child happens to already be dead,
|
||||
without caring which specific pid" instead of the ordinary "wait for
|
||||
exactly this one" contract every other sysWait id value has.
|
||||
(unsigned int)-1 can never collide with a real pid, since pid 0 (idle)
|
||||
and every id returned by threadCreate/threadCreateUser are always <
|
||||
maxThreads (sched.c). Deliberately spelled with the plain C type, not
|
||||
sched.h's uint32, so this header stays usable from userland (ulib.h/
|
||||
ulib.c) exactly like fs.h/signalnum.h's own shared definitions already
|
||||
are -- see fs.h's FbStat for the same convention. */
|
||||
#define waitAnyPid ((unsigned int)-1)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef FROSTBYTE_VEC_H
|
||||
#define FROSTBYTE_VEC_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/*
|
||||
* Vec: a growable, type-erased array backed by the kernel heap. Elements
|
||||
* are stored by value (memcpy'd in and out), sized by elemSize so a single
|
||||
* implementation serves every element type without void* indirection or
|
||||
* per-type code generation.
|
||||
*/
|
||||
|
||||
typedef struct Vec {
|
||||
uint8* data;
|
||||
usize elemSize;
|
||||
usize length;
|
||||
usize capacity;
|
||||
} Vec;
|
||||
|
||||
void vecInit(Vec* v, usize elemSize);
|
||||
void vecFree(Vec* v);
|
||||
|
||||
/* Ensures capacity for at least newCapacity elements. Returns false only
|
||||
if the underlying allocation failed (existing contents are untouched). */
|
||||
bool vecReserve(Vec* v, usize newCapacity);
|
||||
|
||||
bool vecPush(Vec* v, const void* elem);
|
||||
/* Removes and copies out the last element. False (and *outElem untouched)
|
||||
when the vector is empty; outElem may be nullptr to just drop it. */
|
||||
bool vecPop(Vec* v, void* outElem);
|
||||
|
||||
/* Pointer into the backing storage; nullptr if index is out of range.
|
||||
Invalidated by any push/insert/remove/reserve that reallocates. */
|
||||
void* vecAt(Vec* v, usize index);
|
||||
bool vecSet(Vec* v, usize index, const void* elem);
|
||||
|
||||
bool vecInsert(Vec* v, usize index, const void* elem);
|
||||
bool vecRemove(Vec* v, usize index, void* outElem);
|
||||
|
||||
void vecClear(Vec* v);
|
||||
|
||||
usize vecLen(const Vec* v);
|
||||
usize vecCap(const Vec* v);
|
||||
bool vecIsEmpty(const Vec* v);
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user