From d7857f18cda58d5e31a1913f4cb4b4ebce1d7672 Mon Sep 17 00:00:00 2001 From: Marvin Friedrich Date: Wed, 14 Oct 2026 23:26:30 +0200 Subject: [PATCH] ringbuffer, guid, iovec stuff, and mmio --- core/lib/guid.c | 31 +++++ core/lib/iovec.c | 53 ++++++++ core/lib/meson.build | 4 + core/lib/radix.c | 255 +++++++++++++++++++++++++++++++++++++++ core/lib/ringbuf.c | 94 +++++++++++++++ include/zinnia/endian.h | 26 ++++ include/zinnia/guid.h | 19 +++ include/zinnia/iovec.h | 21 +++- include/zinnia/mmio.h | 103 ++++++++++++++++ include/zinnia/radix.h | 51 ++++++++ include/zinnia/ringbuf.h | 34 ++++++ include/zinnia/tailq.h | 8 ++ include/zinnia/vfs.h | 14 +-- 13 files changed, 701 insertions(+), 12 deletions(-) create mode 100644 core/lib/guid.c create mode 100644 core/lib/iovec.c create mode 100644 core/lib/radix.c create mode 100644 core/lib/ringbuf.c create mode 100644 include/zinnia/endian.h create mode 100644 include/zinnia/guid.h create mode 100644 include/zinnia/radix.h create mode 100644 include/zinnia/ringbuf.h diff --git a/core/lib/guid.c b/core/lib/guid.c new file mode 100644 index 0000000..518b9e9 --- /dev/null +++ b/core/lib/guid.c @@ -0,0 +1,31 @@ +#include +#include +#include + +bool guid_is_zero(const struct guid* guid) { + static const struct guid zero = {}; + return guid_equal(guid, &zero); +} + +bool guid_equal(const struct guid* a, const struct guid* b) { + return memcmp(a, b, sizeof(struct guid)) == 0; +} + +void guid_format(const struct guid* guid, char out[static GUID_STR_LEN]) { + ksnprintf( + out, + GUID_STR_LEN, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->time_low, + guid->time_mid, + guid->time_hi_version, + guid->clock_seq[0], + guid->clock_seq[1], + guid->node[0], + guid->node[1], + guid->node[2], + guid->node[3], + guid->node[4], + guid->node[5] + ); +} diff --git a/core/lib/iovec.c b/core/lib/iovec.c new file mode 100644 index 0000000..44a1cfe --- /dev/null +++ b/core/lib/iovec.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +// Copies between `buf` and the iterator. If `to_iter` is set, data flows from `buf` into the iovecs. +static errno_t iovec_iter_copy(iovec_iter_t* iter, void* buf, size_t len, bool to_iter, size_t* out_copied) { + size_t progress = 0; + errno_t status = 0; + + while (progress < len && iter->current_idx < iter->iovecs_len) { + iovec_t* vec = &iter->iovecs[iter->current_idx]; + size_t remaining = vec->len - iter->current_offset; + if (remaining == 0) { + iter->current_idx++; + iter->current_offset = 0; + continue; + } + + size_t chunk = MIN(remaining, len - progress); + void* ptr = (void*)(vec->base + iter->current_offset); + uint8_t* kbuf = (uint8_t*)buf + progress; + + if (iter->user) { + bool ok = to_iter ? usercopy_write((__user void*)ptr, kbuf, chunk) + : usercopy_read(kbuf, (const __user void*)ptr, chunk); + if (!ok) { + status = EFAULT; + break; + } + } else if (to_iter) { + memcpy(ptr, kbuf, chunk); + } else { + memcpy(kbuf, ptr, chunk); + } + + progress += chunk; + iter->current_offset += chunk; + iter->total_offset += chunk; + } + + if (out_copied) + *out_copied = progress; + return status; +} + +errno_t iovec_iter_copy_to(iovec_iter_t* iter, const void* src, size_t len, size_t* out_copied) { + return iovec_iter_copy(iter, (void*)src, len, true, out_copied); +} + +errno_t iovec_iter_copy_from(iovec_iter_t* iter, void* dst, size_t len, size_t* out_copied) { + return iovec_iter_copy(iter, dst, len, false, out_copied); +} diff --git a/core/lib/meson.build b/core/lib/meson.build index ca0a476..b6e5ecd 100644 --- a/core/lib/meson.build +++ b/core/lib/meson.build @@ -1,6 +1,10 @@ # Freestanding libc functions that are needed in the kernel kernel_sources += files( + 'guid.c', + 'iovec.c', + 'radix.c', + 'ringbuf.c', 'runtime.c', 'stdlib.c', 'string.c', diff --git a/core/lib/radix.c b/core/lib/radix.c new file mode 100644 index 0000000..cd6e6bb --- /dev/null +++ b/core/lib/radix.c @@ -0,0 +1,255 @@ +#include +#include + +static bool covers(const struct radix_node* node, uint64_t index) { + return node->shift + RADIX_BITS >= 64 || (index >> (node->shift + RADIX_BITS)) == 0; +} + +static size_t slot_of(const struct radix_node* node, uint64_t index) { + return (index >> node->shift) & (RADIX_SLOTS - 1); +} + +static struct radix_node* node_alloc(uint8_t shift, struct radix_node* parent, uint8_t offset) { + struct radix_node* node = mem_alloc(sizeof(struct radix_node), 0); + if (!node) + return nullptr; + node->shift = shift; + node->parent = parent; + node->offset = offset; + return node; +} + +static void free_subtree(struct radix_node* node) { + if (node->shift > 0) { + for (size_t i = 0; i < RADIX_SLOTS; i++) { + if (node->slots[i]) + free_subtree(node->slots[i]); + } + } + mem_free(node); +} + +void radix_init(struct radix_tree* tree) { + tree->root = nullptr; +} + +void radix_destroy(struct radix_tree* tree) { + if (tree->root) + free_subtree(tree->root); + tree->root = nullptr; +} + +bool radix_empty(struct radix_tree* tree) { + return tree->root == nullptr; +} + +static struct radix_node* leaf_of(struct radix_tree* tree, uint64_t index) { + struct radix_node* node = tree->root; + if (!node || !covers(node, index)) + return nullptr; + while (node->shift > 0) { + node = node->slots[slot_of(node, index)]; + if (!node) + return nullptr; + } + return node; +} + +void* radix_lookup(struct radix_tree* tree, uint64_t index) { + struct radix_node* leaf = leaf_of(tree, index); + return leaf ? leaf->slots[slot_of(leaf, index)] : nullptr; +} + +void** radix_slot(struct radix_tree* tree, uint64_t index) { + struct radix_node* leaf = leaf_of(tree, index); + return leaf ? &leaf->slots[slot_of(leaf, index)] : nullptr; +} + +static errno_t grow(struct radix_tree* tree, uint64_t index) { + if (!tree->root) { + uint8_t shift = 0; + while (shift + RADIX_BITS < 64 && (index >> (shift + RADIX_BITS)) != 0) + shift += RADIX_BITS; + tree->root = node_alloc(shift, nullptr, 0); + return tree->root ? 0 : ENOMEM; + } + + while (!covers(tree->root, index)) { + struct radix_node* old = tree->root; + struct radix_node* top = node_alloc(old->shift + RADIX_BITS, nullptr, 0); + if (!top) + return ENOMEM; + top->slots[0] = old; + top->count = 1; + for (size_t t = 0; t < RADIX_NUM_TAGS; t++) { + if (old->tags[t]) + top->tags[t] = 1; + } + old->parent = top; + old->offset = 0; + tree->root = top; + } + return 0; +} + +errno_t radix_insert(struct radix_tree* tree, uint64_t index, void* value) { + errno_t status = grow(tree, index); + if (status) + return status; + + struct radix_node* node = tree->root; + while (node->shift > 0) { + const size_t slot = slot_of(node, index); + struct radix_node* child = node->slots[slot]; + if (!child) { + child = node_alloc(node->shift - RADIX_BITS, node, slot); + if (!child) + return ENOMEM; + node->slots[slot] = child; + node->count++; + } + node = child; + } + + const size_t slot = slot_of(node, index); + if (node->slots[slot]) + return EEXIST; + node->slots[slot] = value; + node->count++; + return 0; +} + +static void clear_tag_upwards(struct radix_node* node, size_t slot, enum radix_tag tag) { + while (node) { + node->tags[tag] &= ~((uint64_t)1 << slot); + if (node->tags[tag]) + return; + slot = node->offset; + node = node->parent; + } +} + +void* radix_remove(struct radix_tree* tree, uint64_t index) { + struct radix_node* node = leaf_of(tree, index); + if (!node) + return nullptr; + + size_t slot = slot_of(node, index); + void* value = node->slots[slot]; + if (!value) + return nullptr; + + for (size_t t = 0; t < RADIX_NUM_TAGS; t++) { + if (node->tags[t] & ((uint64_t)1 << slot)) + clear_tag_upwards(node, slot, t); + } + + node->slots[slot] = nullptr; + node->count--; + while (node->count == 0) { + struct radix_node* parent = node->parent; + if (!parent) { + tree->root = nullptr; + mem_free(node); + break; + } + slot = node->offset; + parent->slots[slot] = nullptr; + parent->count--; + mem_free(node); + node = parent; + } + return value; +} + +void radix_tag_set(struct radix_tree* tree, uint64_t index, enum radix_tag tag) { + struct radix_node* node = leaf_of(tree, index); + if (!node || !node->slots[slot_of(node, index)]) + return; + size_t slot = slot_of(node, index); + while (node) { + const uint64_t bit = (uint64_t)1 << slot; + if (node->tags[tag] & bit) + return; + node->tags[tag] |= bit; + slot = node->offset; + node = node->parent; + } +} + +void radix_tag_clear(struct radix_tree* tree, uint64_t index, enum radix_tag tag) { + struct radix_node* node = leaf_of(tree, index); + if (!node) + return; + const size_t slot = slot_of(node, index); + if (node->tags[tag] & ((uint64_t)1 << slot)) + clear_tag_upwards(node, slot, tag); +} + +bool radix_tag_get(struct radix_tree* tree, uint64_t index, enum radix_tag tag) { + struct radix_node* node = leaf_of(tree, index); + return node && (node->tags[tag] & ((uint64_t)1 << slot_of(node, index))); +} + +bool radix_tagged(struct radix_tree* tree, enum radix_tag tag) { + return tree->root && tree->root->tags[tag]; +} + +struct gang { + size_t max; + size_t found; + void** out; + uint64_t* out_index; + int tag; +}; + +static void gang_walk(struct radix_node* node, uint64_t base, uint64_t first, struct gang* g) { + size_t start = 0; + if (first > base) { + const uint64_t delta = (first - base) >> node->shift; + if (delta >= RADIX_SLOTS) + return; + start = delta; + } + + for (size_t i = start; i < RADIX_SLOTS && g->found < g->max; i++) { + if (!node->slots[i]) + continue; + if (g->tag >= 0 && !(node->tags[g->tag] & ((uint64_t)1 << i))) + continue; + const uint64_t index = base + ((uint64_t)i << node->shift); + if (node->shift == 0) { + if (g->out) + g->out[g->found] = node->slots[i]; + if (g->out_index) + g->out_index[g->found] = index; + g->found++; + } else { + gang_walk(node->slots[i], index, first > index ? first : index, g); + } + } +} + +static size_t gang_lookup(struct radix_tree* tree, uint64_t first, int tag, size_t max, void** out, uint64_t* idx) { + struct radix_node* root = tree->root; + if (!root || max == 0 || !covers(root, first)) + return 0; + struct gang g = {.max = max, .out = out, .out_index = idx, .tag = tag}; + gang_walk(root, 0, first, &g); + return g.found; +} + +size_t radix_gang_lookup(struct radix_tree* tree, uint64_t first, size_t max, void** out, uint64_t* out_index) { + return gang_lookup(tree, first, -1, max, out, out_index); +} + +size_t radix_gang_lookup_tag( + struct radix_tree* tree, + uint64_t first, + enum radix_tag tag, + size_t max, + void** out, + uint64_t* out_index +) { + return gang_lookup(tree, first, tag, max, out, out_index); +} diff --git a/core/lib/ringbuf.c b/core/lib/ringbuf.c new file mode 100644 index 0000000..5b50b01 --- /dev/null +++ b/core/lib/ringbuf.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +errno_t ringbuf_init(struct ringbuf* rb, size_t capacity) { + rb->data = mem_alloc(capacity, ALLOC_NOZERO); + if (!rb->data) + return ENOMEM; + rb->capacity = capacity; + rb->head = 0; + rb->len = 0; + return 0; +} + +void ringbuf_fini(struct ringbuf* rb) { + mem_free(rb->data); + rb->data = nullptr; + rb->capacity = 0; + rb->len = 0; +} + +static size_t tail(const struct ringbuf* rb) { + return (rb->head + rb->len) % rb->capacity; +} + +static size_t write_chunk(const struct ringbuf* rb, size_t len) { + return MIN(len, MIN(ringbuf_space(rb), rb->capacity - tail(rb))); +} + +static size_t read_chunk(const struct ringbuf* rb, size_t len) { + return MIN(len, MIN(rb->len, rb->capacity - rb->head)); +} + +static void advance_read(struct ringbuf* rb, size_t len) { + rb->head = (rb->head + len) % rb->capacity; + rb->len -= len; + if (rb->len == 0) + rb->head = 0; +} + +size_t ringbuf_write(struct ringbuf* rb, const void* src, size_t len) { + size_t done = 0; + size_t chunk; + while ((chunk = write_chunk(rb, len - done))) { + memcpy(rb->data + tail(rb), (const uint8_t*)src + done, chunk); + rb->len += chunk; + done += chunk; + } + return done; +} + +size_t ringbuf_read(struct ringbuf* rb, void* dst, size_t len) { + size_t done = 0; + size_t chunk; + while ((chunk = read_chunk(rb, len - done))) { + memcpy((uint8_t*)dst + done, rb->data + rb->head, chunk); + advance_read(rb, chunk); + done += chunk; + } + return done; +} + +errno_t ringbuf_write_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_written) { + size_t done = 0; + errno_t status = 0; + size_t chunk; + while (!status && (chunk = write_chunk(rb, len - done))) { + size_t copied; + status = iovec_iter_copy_from(iter, rb->data + tail(rb), chunk, &copied); + rb->len += copied; + done += copied; + if (copied < chunk) + break; + } + *out_written = done; + return done ? 0 : status; +} + +errno_t ringbuf_read_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_read) { + size_t done = 0; + errno_t status = 0; + size_t chunk; + while (!status && (chunk = read_chunk(rb, len - done))) { + size_t copied; + status = iovec_iter_copy_to(iter, rb->data + rb->head, chunk, &copied); + advance_read(rb, copied); + done += copied; + if (copied < chunk) + break; + } + *out_read = done; + return done ? 0 : status; +} diff --git a/include/zinnia/endian.h b/include/zinnia/endian.h new file mode 100644 index 0000000..efb0b47 --- /dev/null +++ b/include/zinnia/endian.h @@ -0,0 +1,26 @@ +#pragma once + +#include // IWYU pragma: keep + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define cpu_to_le16(x) ((uint16_t)(x)) +#define cpu_to_le32(x) ((uint32_t)(x)) +#define cpu_to_le64(x) ((uint64_t)(x)) +#define cpu_to_be16(x) __builtin_bswap16(x) +#define cpu_to_be32(x) __builtin_bswap32(x) +#define cpu_to_be64(x) __builtin_bswap64(x) +#else +#define cpu_to_le16(x) __builtin_bswap16(x) +#define cpu_to_le32(x) __builtin_bswap32(x) +#define cpu_to_le64(x) __builtin_bswap64(x) +#define cpu_to_be16(x) ((uint16_t)(x)) +#define cpu_to_be32(x) ((uint32_t)(x)) +#define cpu_to_be64(x) ((uint64_t)(x)) +#endif + +#define le16_to_cpu(x) cpu_to_le16(x) +#define le32_to_cpu(x) cpu_to_le32(x) +#define le64_to_cpu(x) cpu_to_le64(x) +#define be16_to_cpu(x) cpu_to_be16(x) +#define be32_to_cpu(x) cpu_to_be32(x) +#define be64_to_cpu(x) cpu_to_be64(x) diff --git a/include/zinnia/guid.h b/include/zinnia/guid.h new file mode 100644 index 0000000..9b1d2f0 --- /dev/null +++ b/include/zinnia/guid.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +struct [[gnu::packed]] guid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_version; + uint8_t clock_seq[2]; + uint8_t node[6]; +}; +static_assert(sizeof(struct guid) == 16); + +constexpr size_t GUID_STR_LEN = 37; + +bool guid_is_zero(const struct guid* guid); +bool guid_equal(const struct guid* a, const struct guid* b); +void guid_format(const struct guid* guid, char out[static GUID_STR_LEN]); diff --git a/include/zinnia/iovec.h b/include/zinnia/iovec.h index 10db69d..d997746 100644 --- a/include/zinnia/iovec.h +++ b/include/zinnia/iovec.h @@ -8,31 +8,42 @@ typedef struct __iovec iovec_t; -struct iovec_iter { +typedef struct { iovec_t* iovecs; size_t iovecs_len; size_t total_len; size_t total_offset; size_t current_idx; size_t current_offset; -}; + // Whether the iovecs point to user memory. + bool user; +} iovec_iter_t; -static inline size_t iovec_iter_len(struct iovec_iter* iter) { +static inline size_t iovec_iter_len(iovec_iter_t* iter) { return iter->total_len - iter->total_offset; } // Creates an iovec iterator over kernel memory. -static inline struct iovec_iter iovec_iter_kernel(iovec_t* iovecs, size_t count) { +static inline iovec_iter_t iovec_iter_kernel(iovec_t* iovecs, size_t count) { size_t total = 0; for (size_t i = 0; i < count; i++) total += iovecs[i].len; - return (struct iovec_iter){ + return (iovec_iter_t){ .iovecs = iovecs, .iovecs_len = count, .total_len = total, .total_offset = 0, .current_idx = 0, .current_offset = 0, + .user = false, }; } + +// Copies up to `len` bytes from `src` into the iterator and advances it. +// Returns the amount of bytes actually copied in `out_copied`. +errno_t iovec_iter_copy_to(iovec_iter_t* iter, const void* src, size_t len, size_t* out_copied); + +// Copies up to `len` bytes out of the iterator into `dst` and advances it. +// Returns the amount of bytes actually copied in `out_copied`. +errno_t iovec_iter_copy_from(iovec_iter_t* iter, void* dst, size_t len, size_t* out_copied); diff --git a/include/zinnia/mmio.h b/include/zinnia/mmio.h index 29fc475..1051c86 100644 --- a/include/zinnia/mmio.h +++ b/include/zinnia/mmio.h @@ -1,5 +1,6 @@ #pragma once +#include #include static inline uint8_t mmio_read8(void* ptr) { @@ -33,3 +34,105 @@ static inline void mmio_write32(void* ptr, uint32_t value) { static inline void mmio_write64(void* ptr, uint64_t value) { *((volatile uint64_t*)ptr) = value; } + +enum mmio_endian : uint8_t { + MMIO_NATIVE = 0, +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + MMIO_LE = 0, + MMIO_BE = 1, +#else + MMIO_BE = 0, + MMIO_LE = 1, +#endif +}; + +typedef struct { + size_t offset; + enum mmio_endian endian; +} mmio_reg8; + +typedef struct { + size_t offset; + enum mmio_endian endian; +} mmio_reg16; + +typedef struct { + size_t offset; + enum mmio_endian endian; +} mmio_reg32; + +typedef struct { + size_t offset; + enum mmio_endian endian; +} mmio_reg64; + +typedef struct { + mmio_reg8 reg; + uint8_t shift; + uint8_t width; +} mmio_field8; + +typedef struct { + mmio_reg16 reg; + uint8_t shift; + uint8_t width; +} mmio_field16; + +typedef struct { + mmio_reg32 reg; + uint8_t shift; + uint8_t width; +} mmio_field32; + +typedef struct { + mmio_reg64 reg; + uint8_t shift; + uint8_t width; +} mmio_field64; + +#define MMIO_REG_ZERO(reg) \ + _Generic((reg), mmio_reg8: (uint8_t)0, mmio_reg16: (uint16_t)0, mmio_reg32: (uint32_t)0, mmio_reg64: (uint64_t)0) + +#define MMIO_REG_TYPE(reg) typeof(MMIO_REG_ZERO(reg)) +#define MMIO_FIELD_TYPE(field) MMIO_REG_TYPE((field).reg) + +#define mmio_bswap(value) \ + _Generic( \ + (value), \ + uint8_t: (value), \ + uint16_t: __builtin_bswap16(value), \ + uint32_t: __builtin_bswap32(value), \ + uint64_t: __builtin_bswap64(value) \ + ) + +#define mmio_width_mask(width) ((width) >= 64 ? UINT64_MAX : ((uint64_t)1 << (width)) - 1) + +// Reads an MMIO register. +#define mmio_reg_read(base, reg) \ + ({ \ + auto __mmio_rr = (reg); \ + typedef MMIO_REG_TYPE(__mmio_rr) __mmio_rt; \ + __mmio_rt __mmio_rv = *(volatile __mmio_rt*)((uint8_t*)(base) + __mmio_rr.offset); \ + (__mmio_rt)(__mmio_rr.endian ? mmio_bswap(__mmio_rv) : __mmio_rv); \ + }) + +// Writes a value to an MMIO register. +#define mmio_reg_write(base, reg, value) \ + ({ \ + auto __mmio_rw = (reg); \ + static_assert( \ + sizeof(value) <= sizeof(MMIO_REG_TYPE(reg)), \ + "High bits of the value might get truncated. Explicitly cast if this is intended." \ + ); \ + typedef MMIO_REG_TYPE(__mmio_rw) __mmio_wt; \ + __mmio_wt __mmio_wv = (value); \ + *(volatile __mmio_wt*)((uint8_t*)(base) + __mmio_rw.offset) = \ + __mmio_rw.endian ? mmio_bswap(__mmio_wv) : __mmio_wv; \ + }) + +// Shifts and masks the bits of `value` to the position it needs to be stored in the register. +#define mmio_field(field, value) ((MMIO_FIELD_TYPE(field))(((value) & mmio_width_mask((field).width)) << (field).shift)) + +// Returns the contents of a `field` from a `value`. +#define mmio_field_get(field, value) \ + ((MMIO_FIELD_TYPE(field))(((value) >> (field).shift) & mmio_width_mask((field).width))) diff --git a/include/zinnia/radix.h b/include/zinnia/radix.h new file mode 100644 index 0000000..e012d57 --- /dev/null +++ b/include/zinnia/radix.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +#define RADIX_BITS 6 +#define RADIX_SLOTS (1 << RADIX_BITS) + +enum radix_tag : uint8_t { + RADIX_TAG_DIRTY, + RADIX_TAG_WRITEBACK, + RADIX_NUM_TAGS, +}; + +struct radix_node { + uint8_t shift; + uint8_t count; + uint8_t offset; + struct radix_node* parent; + uint64_t tags[RADIX_NUM_TAGS]; + void* slots[RADIX_SLOTS]; +}; + +struct radix_tree { + struct radix_node* root; +}; + +void radix_init(struct radix_tree* tree); +void radix_destroy(struct radix_tree* tree); +bool radix_empty(struct radix_tree* tree); + +void* radix_lookup(struct radix_tree* tree, uint64_t index); +errno_t radix_insert(struct radix_tree* tree, uint64_t index, void* value); +void* radix_remove(struct radix_tree* tree, uint64_t index); +void** radix_slot(struct radix_tree* tree, uint64_t index); + +void radix_tag_set(struct radix_tree* tree, uint64_t index, enum radix_tag tag); +void radix_tag_clear(struct radix_tree* tree, uint64_t index, enum radix_tag tag); +bool radix_tag_get(struct radix_tree* tree, uint64_t index, enum radix_tag tag); +bool radix_tagged(struct radix_tree* tree, enum radix_tag tag); + +size_t radix_gang_lookup(struct radix_tree* tree, uint64_t first, size_t max, void** out, uint64_t* out_index); +size_t radix_gang_lookup_tag( + struct radix_tree* tree, + uint64_t first, + enum radix_tag tag, + size_t max, + void** out, + uint64_t* out_index +); diff --git a/include/zinnia/ringbuf.h b/include/zinnia/ringbuf.h new file mode 100644 index 0000000..be966aa --- /dev/null +++ b/include/zinnia/ringbuf.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include + +struct ringbuf { + uint8_t* data; + size_t capacity; + size_t head; + size_t len; +}; + +errno_t ringbuf_init(struct ringbuf* rb, size_t capacity); +void ringbuf_fini(struct ringbuf* rb); + +static inline size_t ringbuf_len(const struct ringbuf* rb) { + return rb->len; +} + +static inline size_t ringbuf_space(const struct ringbuf* rb) { + return rb->capacity - rb->len; +} + +static inline void ringbuf_clear(struct ringbuf* rb) { + rb->head = 0; + rb->len = 0; +} + +size_t ringbuf_write(struct ringbuf* rb, const void* src, size_t len); +size_t ringbuf_read(struct ringbuf* rb, void* dst, size_t len); +errno_t ringbuf_write_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_written); +errno_t ringbuf_read_iter(struct ringbuf* rb, iovec_iter_t* iter, size_t len, size_t* out_read); diff --git a/include/zinnia/tailq.h b/include/zinnia/tailq.h index 276debc..f0ce0cd 100644 --- a/include/zinnia/tailq.h +++ b/include/zinnia/tailq.h @@ -42,6 +42,14 @@ (elm)->field.tql_prev = &(head)->tqh_first; \ } while (0) +#define tailq_insert_before(listelm, elm, field) \ + do { \ + (elm)->field.tql_prev = (listelm)->field.tql_prev; \ + (elm)->field.tql_next = (listelm); \ + *(listelm)->field.tql_prev = (elm); \ + (listelm)->field.tql_prev = &(elm)->field.tql_next; \ + } while (0) + #define tailq_remove(head, elm, field) \ do { \ if (((elm)->field.tql_next) != nullptr) \ diff --git a/include/zinnia/vfs.h b/include/zinnia/vfs.h index 5165730..5e80ae9 100644 --- a/include/zinnia/vfs.h +++ b/include/zinnia/vfs.h @@ -179,7 +179,7 @@ struct vfs_node_ops { // VNODE_SYMLINK struct { // Reads the value of the symlink into `buf`. - errno_t (*read_link)(struct vfs_node* node, struct iovec_iter* iter, size_t* out_read); + errno_t (*read_link)(struct vfs_node* node, iovec_iter_t* iter, size_t* out_read); } symlink; // VNODE_DIR @@ -264,8 +264,8 @@ struct vfs_node { }; struct vfs_file_ops { - errno_t (*read)(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read); - errno_t (*write)(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written); + errno_t (*read)(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_read); + errno_t (*write)(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_written); errno_t (*ioctl)(struct vfs_file* file, uintptr_t request, uintptr_t arg, uintptr_t* out_result); errno_t (*sync)(struct vfs_file* file, bool data_only); errno_t (*mmap)( @@ -411,17 +411,17 @@ errno_t vfs_file_open( // Reads from a `file` into a `buffer` from the current offset. // Returns the actual bytes read in `out_read`. -errno_t vfs_file_read(struct vfs_file* file, struct iovec_iter* buffer, ssize_t* out_read); +errno_t vfs_file_read(struct vfs_file* file, iovec_iter_t* buffer, ssize_t* out_read); // Same as `vfs_file_read`, but from a given offset. -errno_t vfs_file_pread(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read); +errno_t vfs_file_pread(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_read); // Wites to a `file` from a `buffer` from the current offset. // Returns the actual bytes written in `out_written`. -errno_t vfs_file_write(struct vfs_file* file, struct iovec_iter* buffer, ssize_t* out_written); +errno_t vfs_file_write(struct vfs_file* file, iovec_iter_t* buffer, ssize_t* out_written); // Same as `vfs_file_write`, but from a given offset. -errno_t vfs_file_pwrite(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written); +errno_t vfs_file_pwrite(struct vfs_file* file, iovec_iter_t* buffer, uint64_t offset, ssize_t* out_written); // Performs a generic `request` on a `file`. // Returns the result in `out_result`.