summaryrefslogtreecommitdiff
path: root/lib/libc/aio
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/aio')
-rw-r--r--lib/libc/aio/Kbuild9
-rw-r--r--lib/libc/aio/aio.c150
-rw-r--r--lib/libc/aio/aio_cancel.c47
-rw-r--r--lib/libc/aio/aio_error.c26
-rw-r--r--lib/libc/aio/aio_fsync.c26
-rw-r--r--lib/libc/aio/aio_read.c16
-rw-r--r--lib/libc/aio/aio_return.c26
-rw-r--r--lib/libc/aio/aio_suspend.c57
-rw-r--r--lib/libc/aio/aio_write.c16
-rw-r--r--lib/libc/aio/lio_listio.c82
10 files changed, 455 insertions, 0 deletions
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 <io_uring.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+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 <errno.h>
+#include <io_uring.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+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 <errno.h>
+#include <stddef.h>
+
+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 <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io_uring.h>
+
+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 <aio.h>
+#include <io_uring.h>
+#include <stdlib.h>
+
+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 <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+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 <aio.h>
+#include <errno.h>
+#include <io_uring.h>
+#include <poll.h>
+#include <stdint.h>
+#include <time.h>
+#include <unistd.h>
+
+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 <aio.h>
+#include <io_uring.h>
+#include <stdlib.h>
+
+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 <aio.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <stdint.h>
+
+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;
+}