feat(kernel): Add RB-tree/kfifo/hashtable, wait queues, futex, tiny RCU, page cache and KTest harness

Expands the kernel-and-TTY core with the primitives the higher layers have
been missing, plus an in-kernel harness that proves they work.

Data structures (lib/):
  - rbtree.c: Linux-compatible red-black tree with parent pointers, full
    insert/erase rebalancing, in-order iteration and an invariant validator.
  - kfifo.c: power-of-two byte ring with free-running indices, so the wrap
    path costs one mask instead of a branch per byte.
  - hashtable.c: separately-chained table with FNV-1a hashing, string or
    u64 keys, and rehash-on-growth at a 3/4 load factor.

Kernel subsystems:
  - kernel/wait.c: wait queues and one-shot completion barriers, backed by a
    static sleeper pool so a blocking path never re-enters the allocator.
  - kernel/futex.c: 32-bucket futex hash with value re-check, requeue, and an
    fmutex_t sleeping mutex built on the uncontended-fastpath protocol.
  - kernel/rcu.c: tiny RCU with reader nesting, deferred callbacks, kfree_rcu
    and rcu_assign_pointer/rcu_dereference publish barriers.
  - mm/page_cache.c: write-back block page cache, 4 KB pages indexed by
    (device, page), CLOCK second-chance eviction and dirty write-back.
  - kernel/ktest.c: KUnit-style harness with 6 built-in suites, 21 cases and
    735 assertions covering every subsystem above (all passing under QEMU).

LazyBox userland:
  - coreutils.c: sort, uniq, cut, tr, rev, tac, nl, seq, diff, xxd, du,
    factor, sum and truncate, reading through the VFS and handling the
    zero-length synthetic nodes that procfs and sysfs expose.
  - ktest, rcuinfo, futexinfo, waitinfo and pagecache diagnostics applets.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01L5yKJT4Wo2cV3rfPrwY5MP
This commit is contained in:
subhobhai943andClaude Opus 5 committed 2026-08-24 23:38:59 +05:30
1 parent 492045b24d
commit f9400f6ac4
22 files changed
+3631 -5

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
#ifndef _KERNEL_FUTEX_H
#define _KERNEL_FUTEX_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <kernel/wait.h>
// Fast Userspace muTEX. A futex is an ordinary 32-bit word that stays entirely
// in user memory while uncontended; the kernel is only consulted when a thread
// actually has to block, at which point the word's address is hashed onto one
// of FUTEX_BUCKETS wait queues.
#define FUTEX_BUCKETS 32
typedef enum {
FUTEX_OP_WAIT = 0,
FUTEX_OP_WAKE = 1,
FUTEX_OP_REQUEUE = 2,
FUTEX_OP_WAKE_ALL = 3
} futex_op_t;
typedef struct {
uint64_t waits;
uint64_t wakes;
uint64_t requeues;
uint64_t spurious;
uint64_t timeouts;
uint32_t active_waiters;
} futex_stats_t;
void futex_init(void);
// Sleep on `uaddr` only while *uaddr still equals `expected`. Returns 0 when
// woken, 1 on timeout, -1 if the value changed before parking (the caller
// should retry its fast path).
int futex_wait(volatile uint32_t* uaddr, uint32_t expected, uint64_t timeout_ticks);
// Wake up to `count` waiters parked on `uaddr`. Returns how many were woken.
int futex_wake(volatile uint32_t* uaddr, int count);
// Move waiters from `uaddr1` onto `uaddr2` without waking them (used to avoid
// the thundering herd when a condvar hands off to a mutex).
int futex_requeue(volatile uint32_t* uaddr1, volatile uint32_t* uaddr2, int count);
futex_stats_t futex_get_stats(void);
void futex_dump(void);
// ---------------------------------------------------------------------------
// A futex-backed mutex, for kernel code that wants sleeping mutual exclusion.
// ---------------------------------------------------------------------------
typedef struct {
volatile uint32_t state; // 0 = unlocked, 1 = locked, 2 = locked + waiters
char name[24];
} fmutex_t;
void fmutex_init(fmutex_t* m, const char* name);
void fmutex_lock(fmutex_t* m);
bool fmutex_trylock(fmutex_t* m);
void fmutex_unlock(fmutex_t* m);
#endif // _KERNEL_FUTEX_H
+81
View File
@@ -0,0 +1,81 @@
#ifndef _KERNEL_KTEST_H
#define _KERNEL_KTEST_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// In-kernel self-test harness (KUnit-style). Suites register a list of cases;
// `ktest` from the LazyBox shell runs them and reports pass/fail per assertion.
#define KTEST_MAX_SUITES 16
#define KTEST_MAX_CASES 24
typedef struct ktest_context {
const char* suite;
const char* test;
uint32_t assertions;
uint32_t failures;
bool aborted;
} ktest_context_t;
typedef void (*ktest_fn_t)(ktest_context_t* ctx);
typedef struct {
const char* name;
ktest_fn_t func;
} ktest_case_t;
typedef struct {
const char* name;
const ktest_case_t* cases;
size_t case_count;
} ktest_suite_t;
typedef struct {
uint32_t suites_run;
uint32_t tests_run;
uint32_t tests_passed;
uint32_t tests_failed;
uint32_t assertions;
uint32_t assertion_failures;
uint64_t elapsed_ticks;
} ktest_result_t;
void ktest_init(void);
int ktest_register_suite(const ktest_suite_t* suite);
// Run every suite, or one by name. Returns the number of failed tests.
int ktest_run_all(ktest_result_t* result_out);
int ktest_run_suite(const char* name, ktest_result_t* result_out);
void ktest_list_suites(void);
// Assertion primitives used by the macros below.
void ktest_report(ktest_context_t* ctx, bool passed, const char* expr,
const char* file, int line, const char* detail);
#define KTEST_ASSERT(ctx, cond) \
ktest_report((ctx), (cond), #cond, __FILE__, __LINE__, NULL)
#define KTEST_ASSERT_EQ(ctx, a, b) \
do { \
long long _a = (long long)(a); \
long long _b = (long long)(b); \
ktest_expect_eq((ctx), _a, _b, #a " == " #b, __FILE__, __LINE__); \
} while (0)
#define KTEST_ASSERT_STREQ(ctx, a, b) \
ktest_expect_streq((ctx), (a), (b), #a " == " #b, __FILE__, __LINE__)
#define KTEST_ASSERT_NOT_NULL(ctx, p) \
ktest_report((ctx), (p) != NULL, #p " != NULL", __FILE__, __LINE__, NULL)
void ktest_expect_eq(ktest_context_t* ctx, long long a, long long b,
const char* expr, const char* file, int line);
void ktest_expect_streq(ktest_context_t* ctx, const char* a, const char* b,
const char* expr, const char* file, int line);
// Built-in suites covering the core data structures and subsystems.
void ktest_register_builtin_suites(void);
#endif // _KERNEL_KTEST_H
+58
View File
@@ -0,0 +1,58 @@
#ifndef _KERNEL_RCU_H
#define _KERNEL_RCU_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// Tiny RCU (Read-Copy-Update).
//
// Readers are free: rcu_read_lock() only bumps a per-CPU nesting depth, so a
// lookup never contends with an update. Writers publish a new version with
// rcu_assign_pointer() and defer reclamation of the old one until every reader
// that could still hold a reference has finished — a "grace period".
#define RCU_MAX_CALLBACKS 64
typedef void (*rcu_callback_t)(void* arg);
typedef struct {
uint64_t grace_periods;
uint64_t callbacks_queued;
uint64_t callbacks_invoked;
uint64_t read_locks;
uint32_t nesting_depth;
uint32_t pending;
} rcu_stats_t;
void rcu_init(void);
void rcu_read_lock(void);
void rcu_read_unlock(void);
bool rcu_read_lock_held(void);
// Block until every reader active at call time has left its critical section.
void rcu_synchronize(void);
// Queue `func(arg)` to run once the current grace period elapses.
int call_rcu(rcu_callback_t func, void* arg);
// Free `ptr` after a grace period (the common case for call_rcu).
int kfree_rcu(void* ptr);
// Advance the state machine; called from the timer tick and from rcu_synchronize.
void rcu_process_callbacks(void);
uint64_t rcu_get_grace_period(void);
rcu_stats_t rcu_get_stats(void);
void rcu_dump(void);
// Publish/consume helpers. The barriers keep the compiler from reordering the
// initialisation of an object past the store that makes it visible.
#define rcu_assign_pointer(p, v) \
do { __atomic_store_n(&(p), (v), __ATOMIC_RELEASE); } while (0)
#define rcu_dereference(p) \
__atomic_load_n(&(p), __ATOMIC_ACQUIRE)
#endif // _KERNEL_RCU_H
+65
View File
@@ -0,0 +1,65 @@
#ifndef _KERNEL_WAIT_H
#define _KERNEL_WAIT_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <lib/list.h>
#include <kernel/sync.h>
#include <kernel/task.h>
// Wait queues and completions.
//
// SUB-OS runs a cooperative round-robin scheduler, so a sleeper parks itself
// on the queue, marks its task BLOCKED and yields until a waker flips the
// entry's `woken` flag. Timed waits fall back to the PIT tick counter.
typedef struct wait_entry {
task_t* task;
volatile bool woken;
struct list_head node;
} wait_entry_t;
typedef struct wait_queue {
char name[24];
struct list_head waiters;
spinlock_t lock;
uint64_t wakeups;
uint64_t sleeps;
uint32_t nr_waiting;
} wait_queue_t;
void wait_queue_init(wait_queue_t* wq, const char* name);
// Park the caller until woken. Returns 0 on wake, -1 if the queue is full.
int wait_event(wait_queue_t* wq);
// Park the caller for at most `timeout_ticks` PIT ticks (100 Hz).
// Returns 0 if woken, 1 if the wait timed out.
int wait_event_timeout(wait_queue_t* wq, uint64_t timeout_ticks);
// Wake the longest-waiting sleeper, or every sleeper. Returns the count woken.
int wake_up(wait_queue_t* wq);
int wake_up_all(wait_queue_t* wq);
uint32_t wait_queue_waiters(const wait_queue_t* wq);
// ---------------------------------------------------------------------------
// Completions: one-shot "this is done" barriers built on a wait queue.
// ---------------------------------------------------------------------------
typedef struct completion {
wait_queue_t wq;
volatile bool done;
} completion_t;
void completion_init(completion_t* c, const char* name);
void wait_for_completion(completion_t* c);
int wait_for_completion_timeout(completion_t* c, uint64_t timeout_ticks);
void complete(completion_t* c);
void complete_all(completion_t* c);
bool completion_done(const completion_t* c);
void wait_subsystem_init(void);
void wait_dump_stats(void);
#endif // _KERNEL_WAIT_H