aboutsummaryrefslogtreecommitdiff
path: root/include/kpl
diff options
context:
space:
mode:
authornodist <kevin.comas.git@gmail.com>2026-10-17 15:41:01 -0400
committernodist <kevin.comas.git@gmail.com>2026-10-17 15:41:01 -0400
commit801aeb9172136ca93a4603fcf8dcf8e19a1522fe (patch)
tree3e55e80388c53188d042bb3ced3dad36c510abf9 /include/kpl
parent6673bb9357b9ed8187f3dcb2917e2c6a9da25ee7 (diff)
add io_uring
Diffstat (limited to 'include/kpl')
-rw-r--r--include/kpl/class.h2
-rw-r--r--include/kpl/env.h9
-rw-r--r--include/kpl/io.h95
-rw-r--r--include/kpl/memory.h1
-rw-r--r--include/kpl/object.h19
5 files changed, 116 insertions, 10 deletions
diff --git a/include/kpl/class.h b/include/kpl/class.h
index b1b7054..f7f9678 100644
--- a/include/kpl/class.h
+++ b/include/kpl/class.h
@@ -2,6 +2,8 @@
* The kpl class a 64bit value with an table of functions
*/
+#pragma once
+
typedef uint8_t kpl_utf8[4];
typedef union {
diff --git a/include/kpl/env.h b/include/kpl/env.h
index f3ad721..7806969 100644
--- a/include/kpl/env.h
+++ b/include/kpl/env.h
@@ -12,7 +12,7 @@ typedef enum : int32_t {
typedef struct _kpl_thread {
int32_t thread_id; // self id
kpl_memory_allocator memory_allocator;
- // TODO io_uring
+ kpl_io_uring io_uring;
// TODO jit allocator
_Atomic ssize_t priority; // running + the total number of tasks in threads queue, lower is higher priority
kpl_object_queue task_queue;
@@ -39,12 +39,13 @@ inline void *kpl_thread_object_allocate(kpl_thread *thread, int32_t size) {
* kpl_env_free &env
*/
typedef struct _kpl_env {
+ int32_t io_uring_queue_size_pow2; // the power of two to calculate the entries in the io_uring queue
int32_t thread_count; // -1 updates this value to max possible, 0 invalid, >= 1 for specific number
_Atomic ssize_t total_priority; // the sum of the threads priority fields, application stops on zero
kpl_thread *threads; // 0 is main thread, >= 1 are spawned pthreads
kpl_tree *namespace_tree, *module_tree; // *namespace_tree lookups are thread safe, *module_tree needs mutex
pthread_mutex_t module_tree_mutex;
-} kpl_env; // kpl state, no globals
+} kpl_env; // kpl state, no globals, must be zero initialized
/*
* All objects are returned by this
@@ -76,14 +77,14 @@ kpl_exit_code kpl_env_config(kpl_env *env, int32_t argc, char *argv[]);
kpl_exit_code kpl_env_init(kpl_env *env);
/*
- * Can be run multiple times as long as new tasks are added after each run
+ * Run once, not re-enetrant
+ * TODO RUN TASK AND IO ORDERING
*/
kpl_exit_code kpl_env_run(kpl_env *env);
/*
* Stops KPL, can be called on any task in KPL
* Does not return just signals to all threads to stop, the return status will be from kpl_env_run
- * Start can be called again
*/
void kpl_env_stop(kpl_env *env);
diff --git a/include/kpl/io.h b/include/kpl/io.h
new file mode 100644
index 0000000..f353da2
--- /dev/null
+++ b/include/kpl/io.h
@@ -0,0 +1,95 @@
+/*
+ * IO uring
+ */
+
+#pragma once
+
+typedef enum : int32_t {
+ KPL_IO_URING_QUEUE_SIZE_POW2_MIN = 12,
+ KPL_IO_URING_QUEUE_SIZE_POW2_MAX = 20,
+ KPL_IO_URING_QUEUE_SIZE_POW2_DEFAULT = 15
+} kpl_io_uring_enum;
+
+/*
+ * Per thread io_uring container, do not use across threads
+ * Operations are added to the submit queue by a functions specific to that operation
+ * Once one or more operations are added, call kpl_io_uring_submit to send the operations to the kernel
+ * Only one completed operation can be read at a time by kpl_io_uring_get_completed
+ * Uses defaults that require kernel version of 6.6 or greater
+ */
+typedef struct {
+ int32_t ring_fd, ring_queue_entries; // fd for submit, number of queue slots
+ uint32_t added_to_queue, submitted_to_kernel; //added to submission queue, added with io uring enter
+ uint32_t sq_cq_size, sqes_size; // using single mmap for kernel submission and completion
+ uint32_t *sring_head, *sring_tail, *sring_mask;
+ uint32_t *cring_head, *cring_tail, *cring_mask;
+ void *sring_ptr, *cring_ptr;
+ struct io_uring_sqe *sqes;
+ struct io_uring_cqe *cqes;
+ struct io_uring_params params;
+} kpl_io_uring; // must be zero initialized
+
+/*
+ * Called by owner thread
+ */
+int32_t kpl_io_uring_init(kpl_io_uring *io_uring, int32_t queue_size_pow2);
+
+/*
+ * Be sure no operations are in queue before calling
+ * Called by owner thread
+ */
+inline void kpl_io_uring_free(kpl_io_uring *io_uring) {
+ munmap(io_uring->sring_ptr, io_uring->sq_cq_size);
+ munmap(io_uring->sqes, io_uring->sqes_size);
+}
+
+/*
+ * Reads one from cqes, will call io uring enter if none are complete and submitted_to_kernel is not zero
+ * This will wait if io uring enter is called
+ * NULL for none completed
+ * If an error occurred, the error details will be on the kpl_task_object
+ */
+kpl_object_task *kpl_io_uring_get_completed(kpl_io_uring *io_uring);
+
+/*
+ * Submits all sqes (added_to_queue has total) in one io uring eneter
+ * This does not wait
+ */
+int32_t kpl_io_uring_submit(kpl_io_uring *io_uring);
+
+/*
+ * Only call this though one of the functions below to add an operation to the queue
+ */
+struct io_uring_sqe *kpl_io_uring_op(kpl_io_uring *io_uring, kpl_object_task *task, int32_t op, int32_t fd,
+ const void *addr, uint32_t len, uint64_t offset);
+
+inline bool kpl_io_uring_openat(kpl_io_uring *io_uring, kpl_object_task *task, int32_t dir_fd,
+ const char *path, int32_t flags, mode_t mode) {
+ struct io_uring_sqe *sqe = kpl_io_uring_op(io_uring, task, IORING_OP_OPENAT, dir_fd, path, mode, 0);
+ if (sqe)
+ sqe->open_flags = (uint32_t) flags;
+ return sqe;
+}
+
+inline bool kpl_io_uring_close(kpl_io_uring *io_uring, kpl_object_task *task, int32_t fd) {
+ return kpl_io_uring_op(io_uring, task, IORING_OP_CLOSE, fd, NULL, 0, 0);
+}
+
+inline bool kpl_io_uring_read(kpl_io_uring *io_uring, kpl_object_task *task, int32_t fd, void *buffer,
+ uint32_t byte_size, uint64_t offset) {
+ return kpl_io_uring_op(io_uring, task, IORING_OP_READ, fd, buffer, byte_size, offset);
+}
+
+inline bool kpl_io_uring_write(kpl_io_uring *uring, kpl_object_task *task, int32_t fd, void *buffer,
+ uint32_t byte_size, uint64_t offset) {
+ return kpl_io_uring_op(uring, task, IORING_OP_WRITE, fd, buffer, byte_size, offset);
+}
+
+inline bool kpl_io_uring_statx(kpl_io_uring *io_uring, kpl_object_task *task, int32_t dir_fd,
+ const char *path, int32_t flags, uint32_t mask, struct statx *statx_object) {
+ struct io_uring_sqe *sqe = kpl_io_uring_op(io_uring, task, IORING_OP_STATX, dir_fd, path, mask,
+ (uint64_t) statx_object);
+ if (sqe)
+ sqe->statx_flags = (uint32_t) flags;
+ return sqe;
+}
diff --git a/include/kpl/memory.h b/include/kpl/memory.h
index 5eb6d85..b61d861 100644
--- a/include/kpl/memory.h
+++ b/include/kpl/memory.h
@@ -9,7 +9,6 @@ typedef enum : int32_t {
KPL_MEMORY_ALLOCATOR_SLAB_COUNT = KPL_MEMORY_ALLOCATOR_MAX_SIZE_POW2 + 1
} kpl_memory_allocator_enum;
-
/*
* Each slab is a linked list allocated with calloc and free
* *buffer is allocated with mmap
diff --git a/include/kpl/object.h b/include/kpl/object.h
index 751088d..1f3ec04 100644
--- a/include/kpl/object.h
+++ b/include/kpl/object.h
@@ -48,9 +48,18 @@ inline void kpl_object_queue_enqueue(kpl_object_queue *object_queue, void *objec
*/
void *kpl_object_queue_dequeue(kpl_object_queue *object_queue);
-typedef void kpl_task_fn(kpl_task *task, kpl_env *env);
+typedef void kpl_task_fn(kpl_object_task *task, kpl_env *env);
-typedef struct _kpl_task {
- KPL_OBJECT_HEADER(_kpl_task);
- // TODO
-} kpl_task;
+/*
+ * Coroutine state object
+ */
+typedef struct _kpl_object_task {
+ KPL_OBJECT_HEADER(_kpl_object_task);
+ _Atomic bool join_ready __attribute__((aligned(4))); // shares cache line with variables_size
+ int32_t variables_size, io_res; // variables_size is set once then only read
+ uint32_t io_flags; // io_res and io_flags are set by io_uring complete
+ kpl_object_task *join;
+ kpl_task_fn *task_fn;
+ kpl_result result;
+ kpl_class variables[]; // a task has no distinction between an argument or a local, both live here
+} kpl_object_task;