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

+227
View File
@@ -0,0 +1,227 @@
// Chained hash table with FNV-1a hashing and load-factor driven growth
#include <lib/hashtable.h>
#include <lib/string.h>
#include <mm/kmalloc.h>
#define HT_MAX_LOAD_NUM 3 // Grow once entries > 3/4 of bucket count
#define HT_MAX_LOAD_DEN 4
#define HT_MAX_BUCKETS 4096
uint64_t hash_fnv1a(const void* data, size_t len) {
const uint8_t* p = (const uint8_t*)data;
uint64_t h = 1469598103934665603ULL; // FNV offset basis
for (size_t i = 0; i < len; i++) {
h ^= p[i];
h *= 1099511628211ULL; // FNV prime
}
return h;
}
uint64_t hash_string(const char* s) {
if (!s) return 0;
return hash_fnv1a(s, strlen(s));
}
uint64_t hash_u64(uint64_t v) {
// Fibonacci-style mixing; cheap and well distributed for dense ids.
v ^= v >> 33;
v *= 0xFF51AFD7ED558CCDULL;
v ^= v >> 33;
v *= 0xC4CEB9FE1A85EC53ULL;
v ^= v >> 33;
return v;
}
static size_t round_up_pow2(size_t v) {
if (v < 8) return 8;
size_t p = 1;
while (p < v) p <<= 1;
return p;
}
hashtable_t* hashtable_create(size_t initial_buckets, bool string_keys) {
hashtable_t* ht = (hashtable_t*)kzalloc(sizeof(hashtable_t));
if (!ht) return NULL;
size_t n = round_up_pow2(initial_buckets);
ht->buckets = (hnode_t**)kzalloc(n * sizeof(hnode_t*));
if (!ht->buckets) {
kfree(ht);
return NULL;
}
ht->bucket_count = n;
ht->entries = 0;
ht->collisions = 0;
ht->string_keys = string_keys;
return ht;
}
void hashtable_destroy(hashtable_t* ht) {
if (!ht) return;
for (size_t i = 0; i < ht->bucket_count; i++) {
hnode_t* n = ht->buckets[i];
while (n) {
hnode_t* next = n->next;
if (n->skey) kfree(n->skey);
kfree(n);
n = next;
}
}
kfree(ht->buckets);
kfree(ht);
}
static void hashtable_grow(hashtable_t* ht) {
if (ht->bucket_count >= HT_MAX_BUCKETS) return;
size_t new_count = ht->bucket_count * 2;
hnode_t** nb = (hnode_t**)kzalloc(new_count * sizeof(hnode_t*));
if (!nb) return; // Growth is best effort; chains simply stay longer.
size_t collisions = 0;
for (size_t i = 0; i < ht->bucket_count; i++) {
hnode_t* n = ht->buckets[i];
while (n) {
hnode_t* next = n->next;
size_t idx = (size_t)(n->hash & (new_count - 1));
if (nb[idx]) collisions++;
n->next = nb[idx];
nb[idx] = n;
n = next;
}
}
kfree(ht->buckets);
ht->buckets = nb;
ht->bucket_count = new_count;
ht->collisions = collisions;
}
static hnode_t* find_node(const hashtable_t* ht, uint64_t hash,
const char* skey, uint64_t ikey) {
size_t idx = (size_t)(hash & (ht->bucket_count - 1));
for (hnode_t* n = ht->buckets[idx]; n; n = n->next) {
if (n->hash != hash) continue;
if (ht->string_keys) {
if (n->skey && skey && strcmp(n->skey, skey) == 0) return n;
} else if (n->ikey == ikey) {
return n;
}
}
return NULL;
}
static int hashtable_insert(hashtable_t* ht, uint64_t hash, const char* skey,
uint64_t ikey, void* value) {
hnode_t* existing = find_node(ht, hash, skey, ikey);
if (existing) {
existing->value = value;
return 0;
}
if (ht->entries * HT_MAX_LOAD_DEN > ht->bucket_count * HT_MAX_LOAD_NUM) {
hashtable_grow(ht);
}
hnode_t* n = (hnode_t*)kzalloc(sizeof(hnode_t));
if (!n) return -1;
n->hash = hash;
n->ikey = ikey;
n->value = value;
if (ht->string_keys && skey) {
size_t len = strlen(skey);
n->skey = (char*)kmalloc(len + 1);
if (!n->skey) {
kfree(n);
return -1;
}
memcpy(n->skey, skey, len + 1);
}
size_t idx = (size_t)(hash & (ht->bucket_count - 1));
if (ht->buckets[idx]) ht->collisions++;
n->next = ht->buckets[idx];
ht->buckets[idx] = n;
ht->entries++;
return 0;
}
static bool hashtable_unlink(hashtable_t* ht, uint64_t hash, const char* skey,
uint64_t ikey) {
size_t idx = (size_t)(hash & (ht->bucket_count - 1));
hnode_t** link = &ht->buckets[idx];
while (*link) {
hnode_t* n = *link;
bool match = (n->hash == hash) &&
(ht->string_keys ? (n->skey && skey && strcmp(n->skey, skey) == 0)
: (n->ikey == ikey));
if (match) {
*link = n->next;
if (n->skey) kfree(n->skey);
kfree(n);
ht->entries--;
return true;
}
link = &n->next;
}
return false;
}
int hashtable_put(hashtable_t* ht, const char* key, void* value) {
if (!ht || !key || !ht->string_keys) return -1;
return hashtable_insert(ht, hash_string(key), key, 0, value);
}
void* hashtable_get(const hashtable_t* ht, const char* key) {
if (!ht || !key || !ht->string_keys) return NULL;
hnode_t* n = find_node(ht, hash_string(key), key, 0);
return n ? n->value : NULL;
}
bool hashtable_remove(hashtable_t* ht, const char* key) {
if (!ht || !key || !ht->string_keys) return false;
return hashtable_unlink(ht, hash_string(key), key, 0);
}
int hashtable_put_u64(hashtable_t* ht, uint64_t key, void* value) {
if (!ht || ht->string_keys) return -1;
return hashtable_insert(ht, hash_u64(key), NULL, key, value);
}
void* hashtable_get_u64(const hashtable_t* ht, uint64_t key) {
if (!ht || ht->string_keys) return NULL;
hnode_t* n = find_node(ht, hash_u64(key), NULL, key);
return n ? n->value : NULL;
}
bool hashtable_remove_u64(hashtable_t* ht, uint64_t key) {
if (!ht || ht->string_keys) return false;
return hashtable_unlink(ht, hash_u64(key), NULL, key);
}
size_t hashtable_size(const hashtable_t* ht) { return ht ? ht->entries : 0; }
size_t hashtable_buckets(const hashtable_t* ht) { return ht ? ht->bucket_count : 0; }
size_t hashtable_collisions(const hashtable_t* ht) { return ht ? ht->collisions : 0; }
size_t hashtable_longest_chain(const hashtable_t* ht) {
if (!ht) return 0;
size_t longest = 0;
for (size_t i = 0; i < ht->bucket_count; i++) {
size_t len = 0;
for (hnode_t* n = ht->buckets[i]; n; n = n->next) len++;
if (len > longest) longest = len;
}
return longest;
}
void hashtable_foreach(const hashtable_t* ht, hashtable_iter_fn fn, void* ctx) {
if (!ht || !fn) return;
for (size_t i = 0; i < ht->bucket_count; i++) {
for (hnode_t* n = ht->buckets[i]; n; n = n->next) fn(n, ctx);
}
}
+134
View File
@@ -0,0 +1,134 @@
// Power-of-two lock-protected byte ring buffer for SUB-OS
#include <lib/kfifo.h>
#include <lib/string.h>
#include <mm/kmalloc.h>
static size_t round_up_pow2(size_t v) {
if (v < 2) return 2;
size_t p = 1;
while (p < v) p <<= 1;
return p;
}
int kfifo_alloc(kfifo_t* fifo, size_t size) {
if (!fifo || size == 0) return -1;
size = round_up_pow2(size);
fifo->buffer = (uint8_t*)kmalloc(size);
if (!fifo->buffer) return -1;
fifo->size = size;
fifo->mask = size - 1;
fifo->in = 0;
fifo->out = 0;
fifo->owns_buffer = true;
spinlock_init(&fifo->lock);
return 0;
}
int kfifo_init_static(kfifo_t* fifo, void* buffer, size_t size) {
if (!fifo || !buffer || size == 0) return -1;
// A caller-supplied buffer must already be a power of two.
if (size & (size - 1)) return -1;
fifo->buffer = (uint8_t*)buffer;
fifo->size = size;
fifo->mask = size - 1;
fifo->in = 0;
fifo->out = 0;
fifo->owns_buffer = false;
spinlock_init(&fifo->lock);
return 0;
}
void kfifo_free(kfifo_t* fifo) {
if (!fifo) return;
if (fifo->owns_buffer && fifo->buffer) kfree(fifo->buffer);
fifo->buffer = NULL;
fifo->size = fifo->mask = fifo->in = fifo->out = 0;
fifo->owns_buffer = false;
}
void kfifo_reset(kfifo_t* fifo) {
if (!fifo) return;
spin_lock(&fifo->lock);
fifo->in = fifo->out = 0;
spin_unlock(&fifo->lock);
}
size_t kfifo_len(const kfifo_t* fifo) {
if (!fifo || !fifo->buffer) return 0;
return fifo->in - fifo->out;
}
size_t kfifo_avail(const kfifo_t* fifo) {
if (!fifo || !fifo->buffer) return 0;
return fifo->size - (fifo->in - fifo->out);
}
bool kfifo_is_empty(const kfifo_t* fifo) { return kfifo_len(fifo) == 0; }
bool kfifo_is_full(const kfifo_t* fifo) { return kfifo_avail(fifo) == 0; }
// Copy into the ring at a free-running offset, wrapping at most once.
static void copy_in(kfifo_t* fifo, const uint8_t* src, size_t len, size_t off) {
size_t start = off & fifo->mask;
size_t first = fifo->size - start;
if (first > len) first = len;
memcpy(fifo->buffer + start, src, first);
if (len > first) memcpy(fifo->buffer, src + first, len - first);
}
static void copy_out(const kfifo_t* fifo, uint8_t* dst, size_t len, size_t off) {
size_t start = off & fifo->mask;
size_t first = fifo->size - start;
if (first > len) first = len;
memcpy(dst, fifo->buffer + start, first);
if (len > first) memcpy(dst + first, fifo->buffer, len - first);
}
size_t kfifo_in(kfifo_t* fifo, const void* src, size_t len) {
if (!fifo || !fifo->buffer || !src || len == 0) return 0;
spin_lock(&fifo->lock);
size_t space = fifo->size - (fifo->in - fifo->out);
if (len > space) len = space;
copy_in(fifo, (const uint8_t*)src, len, fifo->in);
fifo->in += len;
spin_unlock(&fifo->lock);
return len;
}
size_t kfifo_out(kfifo_t* fifo, void* dst, size_t len) {
if (!fifo || !fifo->buffer || !dst || len == 0) return 0;
spin_lock(&fifo->lock);
size_t used = fifo->in - fifo->out;
if (len > used) len = used;
copy_out(fifo, (uint8_t*)dst, len, fifo->out);
fifo->out += len;
spin_unlock(&fifo->lock);
return len;
}
size_t kfifo_peek(const kfifo_t* fifo, void* dst, size_t len) {
if (!fifo || !fifo->buffer || !dst || len == 0) return 0;
size_t used = fifo->in - fifo->out;
if (len > used) len = used;
copy_out(fifo, (uint8_t*)dst, len, fifo->out);
return len;
}
int kfifo_put_byte(kfifo_t* fifo, uint8_t byte) {
return kfifo_in(fifo, &byte, 1) == 1 ? 0 : -1;
}
int kfifo_get_byte(kfifo_t* fifo, uint8_t* byte_out) {
if (!byte_out) return -1;
return kfifo_out(fifo, byte_out, 1) == 1 ? 0 : -1;
}
+309
View File
@@ -0,0 +1,309 @@
// Red-Black Tree implementation for SUB-OS (Linux-compatible semantics)
#include <lib/rbtree.h>
static void rb_rotate_left(struct rb_node* node, struct rb_root* root) {
struct rb_node* right = node->rb_right;
struct rb_node* parent = node->rb_parent;
node->rb_right = right->rb_left;
if (right->rb_left) right->rb_left->rb_parent = node;
right->rb_left = node;
right->rb_parent = parent;
if (parent) {
if (node == parent->rb_left) parent->rb_left = right;
else parent->rb_right = right;
} else {
root->rb_node = right;
}
node->rb_parent = right;
}
static void rb_rotate_right(struct rb_node* node, struct rb_root* root) {
struct rb_node* left = node->rb_left;
struct rb_node* parent = node->rb_parent;
node->rb_left = left->rb_right;
if (left->rb_right) left->rb_right->rb_parent = node;
left->rb_right = node;
left->rb_parent = parent;
if (parent) {
if (node == parent->rb_right) parent->rb_right = left;
else parent->rb_left = left;
} else {
root->rb_node = left;
}
node->rb_parent = left;
}
void rb_insert_color(struct rb_node* node, struct rb_root* root) {
struct rb_node *parent, *gparent;
while ((parent = node->rb_parent) && parent->rb_color == RB_RED) {
gparent = parent->rb_parent;
if (!gparent) break;
if (parent == gparent->rb_left) {
struct rb_node* uncle = gparent->rb_right;
if (uncle && uncle->rb_color == RB_RED) {
uncle->rb_color = RB_BLACK;
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
node = gparent;
continue;
}
if (parent->rb_right == node) {
rb_rotate_left(parent, root);
struct rb_node* tmp = parent;
parent = node;
node = tmp;
}
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
rb_rotate_right(gparent, root);
} else {
struct rb_node* uncle = gparent->rb_left;
if (uncle && uncle->rb_color == RB_RED) {
uncle->rb_color = RB_BLACK;
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
node = gparent;
continue;
}
if (parent->rb_left == node) {
rb_rotate_right(parent, root);
struct rb_node* tmp = parent;
parent = node;
node = tmp;
}
parent->rb_color = RB_BLACK;
gparent->rb_color = RB_RED;
rb_rotate_left(gparent, root);
}
}
root->rb_node->rb_color = RB_BLACK;
}
// Rebalance after removing a black node. `node` may be NULL, hence the
// explicit parent pointer.
static void rb_erase_color(struct rb_node* node, struct rb_node* parent,
struct rb_root* root) {
struct rb_node* other;
while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) {
if (parent->rb_left == node) {
other = parent->rb_right;
if (!other) break;
if (other->rb_color == RB_RED) {
other->rb_color = RB_BLACK;
parent->rb_color = RB_RED;
rb_rotate_left(parent, root);
other = parent->rb_right;
if (!other) break;
}
if ((!other->rb_left || other->rb_left->rb_color == RB_BLACK) &&
(!other->rb_right || other->rb_right->rb_color == RB_BLACK)) {
other->rb_color = RB_RED;
node = parent;
parent = node->rb_parent;
} else {
if (!other->rb_right || other->rb_right->rb_color == RB_BLACK) {
if (other->rb_left) other->rb_left->rb_color = RB_BLACK;
other->rb_color = RB_RED;
rb_rotate_right(other, root);
other = parent->rb_right;
if (!other) break;
}
other->rb_color = parent->rb_color;
parent->rb_color = RB_BLACK;
if (other->rb_right) other->rb_right->rb_color = RB_BLACK;
rb_rotate_left(parent, root);
node = root->rb_node;
break;
}
} else {
other = parent->rb_left;
if (!other) break;
if (other->rb_color == RB_RED) {
other->rb_color = RB_BLACK;
parent->rb_color = RB_RED;
rb_rotate_right(parent, root);
other = parent->rb_left;
if (!other) break;
}
if ((!other->rb_left || other->rb_left->rb_color == RB_BLACK) &&
(!other->rb_right || other->rb_right->rb_color == RB_BLACK)) {
other->rb_color = RB_RED;
node = parent;
parent = node->rb_parent;
} else {
if (!other->rb_left || other->rb_left->rb_color == RB_BLACK) {
if (other->rb_right) other->rb_right->rb_color = RB_BLACK;
other->rb_color = RB_RED;
rb_rotate_left(other, root);
other = parent->rb_left;
if (!other) break;
}
other->rb_color = parent->rb_color;
parent->rb_color = RB_BLACK;
if (other->rb_left) other->rb_left->rb_color = RB_BLACK;
rb_rotate_right(parent, root);
node = root->rb_node;
break;
}
}
}
if (node) node->rb_color = RB_BLACK;
}
void rb_erase(struct rb_node* node, struct rb_root* root) {
struct rb_node *child, *parent;
int color;
if (!node->rb_left) {
child = node->rb_right;
} else if (!node->rb_right) {
child = node->rb_left;
} else {
// Two children: splice in the in-order successor.
struct rb_node* old = node;
struct rb_node* left;
node = node->rb_right;
while ((left = node->rb_left) != NULL) node = left;
// Put the successor where `old` used to hang.
if (old->rb_parent) {
if (old->rb_parent->rb_left == old) old->rb_parent->rb_left = node;
else old->rb_parent->rb_right = node;
} else {
root->rb_node = node;
}
child = node->rb_right;
parent = node->rb_parent;
color = node->rb_color;
if (parent == old) {
// The successor is `old`'s direct right child, so it keeps its own
// right subtree and becomes the parent of the deficient side.
parent = node;
} else {
if (child) child->rb_parent = parent;
parent->rb_left = child;
node->rb_right = old->rb_right;
old->rb_right->rb_parent = node;
}
node->rb_parent = old->rb_parent;
node->rb_color = old->rb_color;
node->rb_left = old->rb_left;
old->rb_left->rb_parent = node;
if (color == RB_BLACK) rb_erase_color(child, parent, root);
return;
}
parent = node->rb_parent;
color = node->rb_color;
if (child) child->rb_parent = parent;
if (parent) {
if (parent->rb_left == node) parent->rb_left = child;
else parent->rb_right = child;
} else {
root->rb_node = child;
}
if (color == RB_BLACK) rb_erase_color(child, parent, root);
}
struct rb_node* rb_first(const struct rb_root* root) {
struct rb_node* n = root->rb_node;
if (!n) return NULL;
while (n->rb_left) n = n->rb_left;
return n;
}
struct rb_node* rb_last(const struct rb_root* root) {
struct rb_node* n = root->rb_node;
if (!n) return NULL;
while (n->rb_right) n = n->rb_right;
return n;
}
struct rb_node* rb_next(const struct rb_node* node) {
if (!node) return NULL;
if (node->rb_right) {
struct rb_node* n = node->rb_right;
while (n->rb_left) n = n->rb_left;
return n;
}
struct rb_node* parent;
while ((parent = node->rb_parent) && node == parent->rb_right) {
node = parent;
}
return parent;
}
struct rb_node* rb_prev(const struct rb_node* node) {
if (!node) return NULL;
if (node->rb_left) {
struct rb_node* n = node->rb_left;
while (n->rb_right) n = n->rb_right;
return n;
}
struct rb_node* parent;
while ((parent = node->rb_parent) && node == parent->rb_left) {
node = parent;
}
return parent;
}
size_t rb_count(const struct rb_root* root) {
size_t count = 0;
for (struct rb_node* n = rb_first(root); n; n = rb_next(n)) count++;
return count;
}
// Returns the number of black nodes on any root-to-leaf path, or -1 when the
// black-height invariant is violated.
static int rb_black_height_of(const struct rb_node* node) {
if (!node) return 1;
int left = rb_black_height_of(node->rb_left);
int right = rb_black_height_of(node->rb_right);
if (left < 0 || right < 0 || left != right) return -1;
if (node->rb_color == RB_RED) {
if ((node->rb_left && node->rb_left->rb_color == RB_RED) ||
(node->rb_right && node->rb_right->rb_color == RB_RED)) {
return -1;
}
return left;
}
return left + 1;
}
int rb_black_height(const struct rb_root* root) {
return rb_black_height_of(root->rb_node);
}
bool rb_validate(const struct rb_root* root) {
if (!root->rb_node) return true;
if (root->rb_node->rb_color != RB_BLACK) return false;
if (root->rb_node->rb_parent != NULL) return false;
return rb_black_height_of(root->rb_node) > 0;
}