From e938d0332e2c34ef2041d8381e0dc969729bf4a2 Mon Sep 17 00:00:00 2001 From: Matheus Date: Sat, 8 Jul 2023 22:02:36 -0300 Subject: [PATCH] kernel: poll system and poll syscall --- kernel-src/arch/x86-64/syscall.asm | 4 +- kernel-src/arch/x86-64/syscalllog.c | 8 +- kernel-src/fs/devfs.c | 4 +- kernel-src/fs/pipefs.c | 3 +- kernel-src/fs/tmpfs.c | 13 +-- kernel-src/fs/vfs.c | 13 +++ kernel-src/include/kernel/devfs.h | 4 +- kernel-src/include/kernel/poll.h | 43 ++++++++++ kernel-src/include/kernel/vfs.h | 7 +- kernel-src/io/poll.c | 128 ++++++++++++++++++++++++++++ kernel-src/sys/syscalls/poll.c | 82 ++++++++++++++++++ 11 files changed, 287 insertions(+), 22 deletions(-) create mode 100644 kernel-src/include/kernel/poll.h create mode 100644 kernel-src/io/poll.c create mode 100644 kernel-src/sys/syscalls/poll.c diff --git a/kernel-src/arch/x86-64/syscall.asm b/kernel-src/arch/x86-64/syscall.asm index a4218695..3b192a8a 100644 --- a/kernel-src/arch/x86-64/syscall.asm +++ b/kernel-src/arch/x86-64/syscall.asm @@ -33,6 +33,7 @@ extern syscall_readlinkat extern syscall_fchmod extern syscall_fchmodat extern syscall_umask +extern syscall_poll syscalltab: dq syscall_print dq syscall_mmap @@ -68,7 +69,8 @@ dq syscall_readlinkat dq syscall_fchmod dq syscall_fchmodat dq syscall_umask -syscallcount equ 34 +dq syscall_poll +syscallcount equ 35 section .text global arch_syscall_entry ; on entry: diff --git a/kernel-src/arch/x86-64/syscalllog.c b/kernel-src/arch/x86-64/syscalllog.c index 040699ad..8f072892 100644 --- a/kernel-src/arch/x86-64/syscalllog.c +++ b/kernel-src/arch/x86-64/syscalllog.c @@ -4,7 +4,7 @@ #ifdef SYSCALL_LOGGING -#define SYSCALL_COUNT 34 +#define SYSCALL_COUNT 35 #define LOGSTR(x) arch_e9_puts(x) static char *name[] = { @@ -41,7 +41,8 @@ static char *name[] = { "readlinkat", "fchmod", "fchmodat", - "umask" + "umask", + "poll" }; static char *args[] = { @@ -78,7 +79,8 @@ static char *args[] = { "dirfd %d path %s buffer %p buffer length %lu", // readlinkat "fd %d mode %o", // fchmod "dirfd %d path %s mode %o flags %d", // fchmodat - "mode %o" // umask + "mode %o", // umask + "fds %p nfds %d timeout %d" // poll }; #endif diff --git a/kernel-src/fs/devfs.c b/kernel-src/fs/devfs.c index cb58a032..73e4efde 100644 --- a/kernel-src/fs/devfs.c +++ b/kernel-src/fs/devfs.c @@ -145,7 +145,7 @@ int devfs_setattr(vnode_t *node, vattr_t *attr, cred_t *cred) { return 0; } -int devfs_poll(vnode_t *node, int events) { +int devfs_poll(vnode_t *node, polldata_t *data, int events) { devnode_t *devnode = (devnode_t *)node; if (devnode->master) devnode = devnode->master; @@ -153,7 +153,7 @@ int devfs_poll(vnode_t *node, int events) { if (devnode->devops->poll == NULL) return ENODEV; - return devnode->devops->poll(devnode->attr.rdevminor, events); + return devnode->devops->poll(devnode->attr.rdevminor, data, events); } int devfs_mmap(vnode_t *node, void *addr, uintmax_t offset, int flags, cred_t *cred) { diff --git a/kernel-src/fs/pipefs.c b/kernel-src/fs/pipefs.c index 62970546..ec074b49 100644 --- a/kernel-src/fs/pipefs.c +++ b/kernel-src/fs/pipefs.c @@ -7,6 +7,7 @@ #include #include #include +#include #define BUFFER_SIZE 16 * PAGE_SIZE @@ -147,7 +148,7 @@ int pipefs_write(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int return error; } -int pipefs_poll(vnode_t *node, int events) { +int pipefs_poll(vnode_t *node, polldata_t *data, int events) { pipenode_t *pipenode = (pipenode_t *)node; int revents = 0; VOP_LOCK(node); diff --git a/kernel-src/fs/tmpfs.c b/kernel-src/fs/tmpfs.c index 0b67ee1e..c8879d35 100644 --- a/kernel-src/fs/tmpfs.c +++ b/kernel-src/fs/tmpfs.c @@ -29,17 +29,6 @@ static int tmpfs_mount(vfs_t **vfs, vnode_t *mountpoint, vnode_t *backing, void return 0; } -static int tmpfs_poll(vnode_t *node, int event) { - int ret = 0; - - if (event & POLLIN) - ret |= POLLIN; - if (event & POLLOUT) - ret |= POLLOUT; - - return ret; -} - #define DATAMMUFLAGS (ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC) // expects to be called with node locked @@ -491,7 +480,7 @@ static vops_t vnops = { .getattr = tmpfs_getattr, .setattr = tmpfs_setattr, .lookup = tmpfs_lookup, - .poll = tmpfs_poll, + .poll = vfs_pollstub, .read = tmpfs_read, .write = tmpfs_write, .access = tmpfs_access, diff --git a/kernel-src/fs/vfs.c b/kernel-src/fs/vfs.c index a4b2427f..d3abbc14 100644 --- a/kernel-src/fs/vfs.c +++ b/kernel-src/fs/vfs.c @@ -5,6 +5,7 @@ #include #include #include +#include #define PATHNAME_MAX 512 #define MAXLINKDEPTH 64 @@ -27,6 +28,18 @@ static cred_t *getcred() { return &_cpu()->thread->proc->cred; } +int vfs_pollstub(vnode_t *node, struct polldata *, int events) { + int revents = 0; + + if (events & POLLIN) + revents |= POLLIN; + + if (events & POLLOUT) + revents |= POLLOUT; + + return revents; +} + void vfs_init() { __assert(hashtable_init(&fstable, 20) == 0); vfsroot = alloc(sizeof(vnode_t)); diff --git a/kernel-src/include/kernel/devfs.h b/kernel-src/include/kernel/devfs.h index 5d747eab..94d86f6e 100644 --- a/kernel-src/include/kernel/devfs.h +++ b/kernel-src/include/kernel/devfs.h @@ -3,6 +3,8 @@ #include +#include + #define DEV_MAJOR_NULL 1 #define DEV_MAJOR_FULL 2 #define DEV_MAJOR_ZERO 3 @@ -13,7 +15,7 @@ typedef struct { int (*close)(int minor, int flags); int (*read)(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc); int (*write)(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec); - int (*poll)(int minor, int events); + int (*poll)(int minor, polldata_t *data, int events); int (*mmap)(int minor, void *addr, uintmax_t offset, int flags); int (*munmap)(int minor, void *addr, uintmax_t offset, int flags); int (*isatty)(int minor); diff --git a/kernel-src/include/kernel/poll.h b/kernel-src/include/kernel/poll.h new file mode 100644 index 00000000..6abbf7fb --- /dev/null +++ b/kernel-src/include/kernel/poll.h @@ -0,0 +1,43 @@ +#ifndef _POLL_H +#define _POLL_H + +#include +#include +#include + +struct polldata; + +typedef struct { + thread_t *thread; + spinlock_t lock; + mutex_t eventlock; + struct polldata *data; + struct polldata *event; + size_t size; +} polldesc_t; + +typedef struct { + mutex_t lock; + struct polldata *data; +} pollheader_t; + +typedef struct polldata { + struct polldata *next; + struct polldata *prev; + pollheader_t *header; + polldesc_t *desc; + int events; + int revents; +} polldata_t; + +#define POLL_INITHEADER(x) \ + MUTEX_INIT(&(x)->lock); + +int poll_initdesc(polldesc_t *, size_t size); +void poll_add(pollheader_t *, polldata_t *, int events); +void poll_leave(polldesc_t *); +int poll_dowait(polldesc_t *); +void poll_event(pollheader_t *, int events); +void poll_destroydesc(polldesc_t *); + +#endif diff --git a/kernel-src/include/kernel/vfs.h b/kernel-src/include/kernel/vfs.h index 4f713f83..be3b7367 100644 --- a/kernel-src/include/kernel/vfs.h +++ b/kernel-src/include/kernel/vfs.h @@ -72,6 +72,8 @@ typedef struct vfsops_t { int (*root)(vfs_t *vfs, vnode_t **root); } vfsops_t; +struct polldata; + typedef struct vops_t { int (*open)(vnode_t **node, int flags, cred_t *cred); int (*close)(vnode_t *node, int flags, cred_t *cred); @@ -81,7 +83,7 @@ typedef struct vops_t { int (*create)(vnode_t *parent, char *name, vattr_t *attr, int type, vnode_t **result, cred_t *cred); int (*getattr)(vnode_t *node, vattr_t *attr, cred_t *cred); int (*setattr)(vnode_t *node, vattr_t *attr, cred_t *cred); - int (*poll)(vnode_t *node, int events); + int (*poll)(vnode_t *node, struct polldata *, int events); int (*access)(vnode_t *node, mode_t mode, cred_t *cred); int (*unlink)(vnode_t *node, char *name, cred_t *cred); int (*link)(vnode_t *node, vnode_t *dir, char *name, cred_t *cred); @@ -119,7 +121,7 @@ typedef struct vops_t { #define VOP_MMAP(v, a, o, f, c) (v)->ops->mmap(v, a, o, f, c) #define VOP_MUNMAP(v, a, o, f, c) (v)->ops->munmap(v, a, o, f, c) #define VOP_GETDENTS(v, b, c, o, rc) (v)->ops->getdents(v, b, c, o, rc) -#define VOP_POLL(v, p) (v)->ops->poll(v, p) +#define VOP_POLL(v, d, p) (v)->ops->poll(v, d, p) #define VOP_ISATTY(v) ((v)->ops->isatty ? (v)->ops->isatty(v) : ENOTTY) #define VOP_IOCTL(v, r, a, rp) ((v)->ops->ioctl ? (v)->ops->ioctl(v, r, a, rp) : ENOTTY) #define VOP_HOLD(v) __atomic_add_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) @@ -143,6 +145,7 @@ int vfs_read(vnode_t *node, void *buffer, size_t size, uintmax_t offset, size_t int vfs_create(vnode_t *ref, char *path, vattr_t *attr, int type, vnode_t **node); int vfs_link(vnode_t *destref, char *destpath, vnode_t *linkref, char *linkpath, int type, vattr_t *attr); int vfs_unlink(vnode_t *ref, char *path); +int vfs_pollstub(vnode_t *node, struct polldata *, int events); #define VFS_LOOKUP_PARENT 1 #define VFS_LOOKUP_NOLINK 2 diff --git a/kernel-src/io/poll.c b/kernel-src/io/poll.c new file mode 100644 index 00000000..a1cb5bf6 --- /dev/null +++ b/kernel-src/io/poll.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include + +static void insertinheader(pollheader_t *header, polldata_t *data) { + data->prev = NULL; + data->next = header->data; + + if (header->data) + header->data->prev = data; + + header->data = data; +} + +static void removefromlist(polldata_t **list, polldata_t *data) { + if (data->next) + data->next->prev = data->prev; + + if (data->prev) + data->prev->next = data->next; + else + *list = data->next; + + data->prev = NULL; + data->next = NULL; +} + +int poll_initdesc(polldesc_t *desc, size_t size) { + __assert(size); + + desc->data = alloc(sizeof(polldata_t) * size); + if (desc->data == NULL) + return ENOMEM; + + desc->thread = _cpu()->thread; + desc->size = size; + SPINLOCK_INIT(desc->lock); + MUTEX_INIT(&desc->eventlock); + for (uintmax_t i = 0; i < size; ++i) + desc->data[i].desc = desc; + + return 0; +} + +void poll_add(pollheader_t *header, polldata_t *data, int events) { + MUTEX_ACQUIRE(&header->lock, false); + data->events = events; + data->header = header; + insertinheader(header, data); + MUTEX_RELEASE(&header->lock); +} + +void poll_leave(polldesc_t *desc) { + for (uintmax_t i = 0; i < desc->size; ++i) { + pollheader_t *header = desc->data[i].header; + if (header == NULL) + continue; + + MUTEX_ACQUIRE(&header->lock, false); + removefromlist(&header->data, &desc->data[i]); + MUTEX_RELEASE(&header->lock); + } +} + +int poll_dowait(polldesc_t *desc) { + MUTEX_ACQUIRE(&desc->eventlock, false); + if (desc->event) { + MUTEX_RELEASE(&desc->eventlock); + return 0; + } + + sched_preparesleep(true); + spinlock_release(&desc->lock); + MUTEX_RELEASE(&desc->eventlock); + int ret = sched_yield(); + // on return, the desc lock is REQUIRED to be left locked by the poll_event function. + // XXX interruptible wait breaks this + __assert(spinlock_try(&desc->lock) == false); + return ret; +} + +void poll_event(pollheader_t *header, int events) { + MUTEX_ACQUIRE(&header->lock, false); + polldata_t *pending = header->data; + header->data = NULL; + polldata_t *iterator = pending; + + while (iterator) { + polldesc_t *desc = iterator->desc; + polldata_t *next = iterator->next; + MUTEX_ACQUIRE(&desc->eventlock, false); + // TODO POLLHUP and POLLERR + int revents = iterator->events & events; + + if (spinlock_try(&desc->lock) == false) { + removefromlist(&pending, iterator); + insertinheader(header, iterator); + } + + if (revents && desc->event == NULL) { + iterator->revents = revents; + desc->event = iterator; + } + + MUTEX_RELEASE(&desc->eventlock); + + iterator = next; + } + + int ipl = interrupt_raiseipl(IPL_DPC); + + iterator = pending; + while (iterator) { + polldata_t *next = iterator->next; + sched_wakeup(iterator->desc->thread, 0); + insertinheader(header, iterator); + iterator = next; + } + + MUTEX_RELEASE(&header->lock); + interrupt_loweripl(ipl); +} + +void poll_destroydesc(polldesc_t *desc) { + free(desc->data); +} diff --git a/kernel-src/sys/syscalls/poll.c b/kernel-src/sys/syscalls/poll.c new file mode 100644 index 00000000..0df366f6 --- /dev/null +++ b/kernel-src/sys/syscalls/poll.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + int fd; + short events; + short revents; +} pollfd_t; + +syscallret_t syscall_poll(context_t *, pollfd_t *fds, size_t nfds, int timeoutms) { + syscallret_t ret = { + .ret = -1 + }; + + // timeouts are not supported yet + __assert(timeoutms <= 0); + + if ((void *)fds > USERSPACE_END) { + ret.errno = EFAULT; + return ret; + } + + size_t fdsbuffsize = nfds * sizeof(pollfd_t); + pollfd_t *fdsbuff = alloc(fdsbuffsize); + if (fdsbuff == NULL) { + ret.errno = ENOMEM; + return ret; + } + + memcpy(fdsbuff, fds, fdsbuffsize); + + polldesc_t desc = {0}; + ret.errno = poll_initdesc(&desc, nfds); + if (ret.errno) { + goto cleanup; + } + __assert(spinlock_try(&desc.lock)); + + size_t eventcount = 0; + + for (uintmax_t i = 0; i < nfds; ++i) { + file_t *file = fd_get(fdsbuff[i].fd); + if (file == NULL) { + fdsbuff[i].revents = POLLNVAL; + ++eventcount; + continue; + } + + int revents = VOP_POLL(file->vnode, timeoutms == 0 ? NULL : &desc.data[i], fdsbuff[i].events); + if (revents) { + fdsbuff[i].revents = revents; + ++eventcount; + } + + fd_release(file); + } + + if (eventcount == 0 && timeoutms != 0) { + // TODO timeout + ret.errno = poll_dowait(&desc); + if (ret.errno == 0) { + int fd = ((uintptr_t)desc.event - (uintptr_t)desc.data) / sizeof(pollfd_t); + fdsbuff[fd].revents = desc.data[fd].revents; + eventcount = 1; + } + } + + memcpy(fds, fdsbuff, fdsbuffsize); + + poll_leave(&desc); + + ret.ret = eventcount; + + cleanup: + free(fdsbuff); + + return ret; +}