From 3b3325f761b09ebbfef04c44eed546cc4fdeb329 Mon Sep 17 00:00:00 2001 From: Kacper Date: Mon, 15 Dec 2025 02:01:33 +0100 Subject: Added aio and eventfd support, along with sleep and yes utilities --- lib/libc/aio/Kbuild | 9 +++ lib/libc/aio/aio.c | 150 +++++++++++++++++++++++++++++++++++++++++++++ lib/libc/aio/aio_cancel.c | 47 ++++++++++++++ lib/libc/aio/aio_error.c | 26 ++++++++ lib/libc/aio/aio_fsync.c | 26 ++++++++ lib/libc/aio/aio_read.c | 16 +++++ lib/libc/aio/aio_return.c | 26 ++++++++ lib/libc/aio/aio_suspend.c | 57 +++++++++++++++++ lib/libc/aio/aio_write.c | 16 +++++ lib/libc/aio/lio_listio.c | 82 +++++++++++++++++++++++++ 10 files changed, 455 insertions(+) create mode 100644 lib/libc/aio/Kbuild create mode 100644 lib/libc/aio/aio.c create mode 100644 lib/libc/aio/aio_cancel.c create mode 100644 lib/libc/aio/aio_error.c create mode 100644 lib/libc/aio/aio_fsync.c create mode 100644 lib/libc/aio/aio_read.c create mode 100644 lib/libc/aio/aio_return.c create mode 100644 lib/libc/aio/aio_suspend.c create mode 100644 lib/libc/aio/aio_write.c create mode 100644 lib/libc/aio/lio_listio.c (limited to 'lib/libc/aio') diff --git a/lib/libc/aio/Kbuild b/lib/libc/aio/Kbuild new file mode 100644 index 00000000..6bd83c98 --- /dev/null +++ b/lib/libc/aio/Kbuild @@ -0,0 +1,9 @@ +obj-y += aio.o +obj-y += aio_cancel.o +obj-y += aio_error.o +obj-y += aio_fsync.o +obj-y += aio_read.o +obj-y += aio_return.o +obj-y += aio_suspend.o +obj-y += aio_write.o +obj-y += lio_listio.o diff --git a/lib/libc/aio/aio.c b/lib/libc/aio/aio.c new file mode 100644 index 00000000..0ca2ec64 --- /dev/null +++ b/lib/libc/aio/aio.c @@ -0,0 +1,150 @@ +#include <__aio.h> +#include +#include +#include +#include +#include +#include + +struct aio_context __aio_context = { 0 }; + +// TODO +// static void *__aio_thread_notify(void *arg) +// { +// struct sigevent *sev = arg; +// sev->sigev_notify_function(sev->sigev_value); +// return NULL; +// } + +static void __aio_deliver_signal(struct sigevent *sev) +{ + if (!sev) + return; + + switch (sev->sigev_notify) { + case SIGEV_SIGNAL: + kill(getpid(), sev->sigev_signo); + break; + + case SIGEV_THREAD: { + // TODO + // pthread_t thread; + // pthread_create(&thread, NULL, __aio_thread_notify, sev); + // pthread_detach(thread); + break; + } + + default: + break; + } +} + +static void __aio_complete(struct aio_request *req, int res) +{ + req->result = res; + req->status = AIO_REQUEST_STATUS_COMPLETED; + + if (req->grp) { + if (res < 0) + req->grp->error = 1; + + if (--req->grp->pending == 0) { + uint64_t one = 1; + write(req->grp->eventfd, &one, sizeof(one)); + + if (req->grp->sig) { + __aio_deliver_signal(req->grp->sig); + } + } + } +} + +int __aio_request(struct aio_request *req, int opcode) +{ + struct io_uring_sqe *sqe; + unsigned index, tail; + + if (req == NULL) + return -1; + + tail = *__io_uring.sq.tail; + index = tail & *__io_uring.sq.ring_mask; + + sqe = &__io_uring.sq.sqes[index]; + memset(sqe, 0, sizeof(*sqe)); + sqe->opcode = opcode; + sqe->fd = req->aiocbp->aio_fildes; + sqe->addr = (uint64_t)req->aiocbp->aio_buf; + sqe->len = req->aiocbp->aio_nbytes; + sqe->off = req->aiocbp->aio_offset; + sqe->user_data = (uint64_t)req; + + __io_uring.sq.array[index] = index; + *__io_uring.sq.tail = tail + 1; + + io_uring_enter(__io_uring.fd, 1, 0, IORING_ENTER_GETEVENTS, NULL, 0); + + req->status = AIO_REQUEST_STATUS_PENDING; + req->next = NULL; + + if (__aio_context.head == NULL) { + __aio_context.head = __aio_context.tail = req; + } else { + __aio_context.tail->next = req; + __aio_context.tail = req; + } + + return 0; +} + +void __aio_poll(void) +{ + unsigned head = *__io_uring.cq.head; + unsigned tail = *__io_uring.cq.tail; + + while (head != tail) { + struct io_uring_cqe *cqe = + &__io_uring.cq.cqes[head & *__io_uring.cq.ring_mask]; + + struct aio_request *req = (struct aio_request *)cqe->user_data; + + __aio_complete(req, cqe->res); + + head++; + } + + *__io_uring.cq.head = head; +} + +struct aio_request *__aio_lookup(const struct aiocb *aiocbp) +{ + struct aio_request *req = __aio_context.head; + + while (req != NULL) { + if (req->aiocbp == aiocbp) + return req; + + req = req->next; + } + + return NULL; +} + +struct aio_request *__aio_remove(const struct aiocb *aiocbp) +{ + struct aio_request **cur = &__aio_context.head; + struct aio_request *req = NULL; + + while (*cur) { + if ((*cur)->aiocbp == aiocbp) { + req = *cur; + *cur = req->next; + if (__aio_context.tail == req) + __aio_context.tail = NULL; + break; + } + cur = &(*cur)->next; + } + + return req; +} diff --git a/lib/libc/aio/aio_cancel.c b/lib/libc/aio/aio_cancel.c new file mode 100644 index 00000000..b4d373bf --- /dev/null +++ b/lib/libc/aio/aio_cancel.c @@ -0,0 +1,47 @@ +#include <__aio.h> +#include +#include +#include +#include +#include +#include +#include + +int aio_cancel(int __unused fildes, struct aiocb *aiocbp) +{ + unsigned tail; + struct aio_request *req; + struct io_uring_sqe sqe; + + if (aiocbp == NULL) { + errno = EINVAL; + return -1; + } + + req = __aio_lookup(aiocbp); + + if (req == NULL) { + errno = EINVAL; + return -1; + } + + if (req->status == AIO_REQUEST_STATUS_COMPLETED) { + return AIO_ALLDONE; + } + + memset(&sqe, 0, sizeof(sqe)); + sqe.opcode = IORING_OP_ASYNC_CANCEL; + sqe.user_data = (uint64_t)req; + + tail = *__io_uring.sq.tail; + __io_uring.sq.sqes[tail % IO_URING_ENTRIES] = sqe; + __io_uring.sq.array[tail % IO_URING_ENTRIES] = tail % IO_URING_ENTRIES; + *__io_uring.sq.tail = tail + 1; + + io_uring_enter(__io_uring.fd, 1, 0, IORING_ENTER_GETEVENTS, NULL, 0); + + __aio_remove(aiocbp); + free(req); + + return AIO_CANCELED; +} diff --git a/lib/libc/aio/aio_error.c b/lib/libc/aio/aio_error.c new file mode 100644 index 00000000..27c7f134 --- /dev/null +++ b/lib/libc/aio/aio_error.c @@ -0,0 +1,26 @@ +#include <__aio.h> +#include +#include + +int aio_error(const struct aiocb *aiocbp) +{ + struct aio_request *req; + + if (aiocbp == NULL) { + errno = EINVAL; + return -1; + } + + req = __aio_lookup(aiocbp); + + if (req == NULL) { + errno = EINVAL; + return -1; + } + + if (req->status == AIO_REQUEST_STATUS_PENDING) { + return EINPROGRESS; + } + + return req->result < 0 ? -req->result : 0; +} diff --git a/lib/libc/aio/aio_fsync.c b/lib/libc/aio/aio_fsync.c new file mode 100644 index 00000000..00d56b42 --- /dev/null +++ b/lib/libc/aio/aio_fsync.c @@ -0,0 +1,26 @@ +#include <__aio.h> +#include +#include +#include +#include + +int aio_fsync(int op, struct aiocb *aiocbp) +{ + int opcode; + struct aio_request *req; + + req = __aio_lookup(aiocbp); + + if (req == NULL) { + errno = EINVAL; + return -1; + } + + if (op == O_DSYNC) { + opcode = IORING_OP_FSYNC | IORING_FSYNC_DATASYNC; + } else { + opcode = IORING_OP_FSYNC; + } + + return __aio_request(req, opcode); +} diff --git a/lib/libc/aio/aio_read.c b/lib/libc/aio/aio_read.c new file mode 100644 index 00000000..286b2d02 --- /dev/null +++ b/lib/libc/aio/aio_read.c @@ -0,0 +1,16 @@ +#include <__aio.h> +#include +#include +#include + +int aio_read(struct aiocb *aiocbp) +{ + struct aio_request *req = malloc(sizeof(*req)); + + if (req == NULL) + return -1; + + req->aiocbp = aiocbp; + + return __aio_request(req, IORING_OP_READ); +} diff --git a/lib/libc/aio/aio_return.c b/lib/libc/aio/aio_return.c new file mode 100644 index 00000000..45b0d795 --- /dev/null +++ b/lib/libc/aio/aio_return.c @@ -0,0 +1,26 @@ +#include <__aio.h> +#include +#include +#include + +ssize_t aio_return(struct aiocb *aiocbp) +{ + ssize_t result; + struct aio_request *req; + + if (aiocbp == NULL) { + errno = EINVAL; + return -1; + } + + req = __aio_remove(aiocbp); + + if (req->status == AIO_REQUEST_STATUS_PENDING) { + free(req); + return -1; + } + + result = req->result; + free(req); + return result; +} diff --git a/lib/libc/aio/aio_suspend.c b/lib/libc/aio/aio_suspend.c new file mode 100644 index 00000000..939b9d6b --- /dev/null +++ b/lib/libc/aio/aio_suspend.c @@ -0,0 +1,57 @@ +#include <__aio.h> +#include +#include +#include +#include +#include +#include +#include + +int aio_suspend(const struct aiocb *const list[], int nent, + const struct timespec *timeout) +{ + struct pollfd pfd; + struct aio_request *req; + + if ((void *)list == NULL || nent <= 0) { + errno = EINVAL; + return -1; + } + + while (1) { + int timeout_ms, ret; + __aio_poll(); + + for (int i = 0; i < nent; i++) { + req = __aio_lookup((struct aiocb *)list[i]); + if (req && req->status == AIO_REQUEST_STATUS_COMPLETED) + return 0; + } + + pfd.fd = __io_uring.eventfd; + pfd.events = POLLIN; + + if (timeout) { + timeout_ms = timeout->tv_sec * 1000 + + timeout->tv_nsec / 1000000; + } else { + timeout_ms = -1; + } + + ret = poll(&pfd, 1, timeout_ms); + + if (ret == 0) { + errno = EAGAIN; + return -1; + } + + if (ret < 0) { + return -1; + } + + uint64_t val; + read(__io_uring.eventfd, &val, sizeof(val)); + io_uring_enter(__io_uring.fd, 0, 0, IORING_ENTER_GETEVENTS, + NULL, 0); + } +} diff --git a/lib/libc/aio/aio_write.c b/lib/libc/aio/aio_write.c new file mode 100644 index 00000000..4254fdcd --- /dev/null +++ b/lib/libc/aio/aio_write.c @@ -0,0 +1,16 @@ +#include <__aio.h> +#include +#include +#include + +int aio_write(struct aiocb *aiocbp) +{ + struct aio_request *req = malloc(sizeof(*req)); + + if (req == NULL) + return -1; + + req->aiocbp = aiocbp; + + return __aio_request(req, IORING_OP_WRITE); +} diff --git a/lib/libc/aio/lio_listio.c b/lib/libc/aio/lio_listio.c new file mode 100644 index 00000000..0a345c16 --- /dev/null +++ b/lib/libc/aio/lio_listio.c @@ -0,0 +1,82 @@ +#include <__aio.h> +#include +#include +#include +#include +#include +#include +#include +#include + +int lio_listio(int mode, struct aiocb *restrict const list[restrict], int nent, + struct sigevent *restrict sig) +{ + struct lio_group *grp = NULL; + + if ((void *)list == NULL || nent <= 0) { + errno = EINVAL; + return -1; + } + + if (mode != LIO_WAIT && mode != LIO_NOWAIT) { + errno = EINVAL; + return -1; + } + + grp = malloc(sizeof(*grp)); + memset(grp, 0, sizeof(*grp)); + + grp->eventfd = eventfd(0, EFD_CLOEXEC); + grp->sig = sig; + + for (int i = 0; i < nent; i++) { + int ret; + struct aio_request *req; + struct aiocb *aiocbp = list[i]; + + if (aiocbp == NULL) { + continue; + } + + switch (aiocbp->aio_lio_opcode) { + case LIO_READ: + ret = aio_read(aiocbp); + break; + case LIO_WRITE: + ret = aio_write(aiocbp); + break; + case LIO_NOP: + ret = 0; + continue; + default: + errno = EINVAL; + return -1; + } + + if (ret < 0) { + return -1; + } + + req = __aio_lookup(aiocbp); + req->grp = grp; + grp->pending++; + } + + if (mode == LIO_NOWAIT) { + return 0; + } + + while (grp->pending > 0) { + uint64_t val; + if (read(grp->eventfd, &val, sizeof(val)) < 0) { + return -1; + } + } + + if (grp->error) { + errno = EIO; + return -1; + } + + return 0; +} -- cgit v1.2.3