From 9047f1e3f502658de12808015179ab3881a4b03f Mon Sep 17 00:00:00 2001 From: dzwdz Date: Sat, 11 May 2024 20:24:17 +0200 Subject: kernel: refactor handle management out of proc.c --- src/kernel/handleset.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++ src/kernel/handleset.h | 37 ++++++++++++++ src/kernel/proc.c | 101 +++---------------------------------- src/kernel/proc.h | 22 +------- src/kernel/syscalls.c | 46 ++++++++--------- src/kernel/vfs/request.c | 7 +-- 6 files changed, 200 insertions(+), 141 deletions(-) create mode 100644 src/kernel/handleset.c create mode 100644 src/kernel/handleset.h (limited to 'src') diff --git a/src/kernel/handleset.c b/src/kernel/handleset.c new file mode 100644 index 0000000..b52eaa0 --- /dev/null +++ b/src/kernel/handleset.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include + +HandleSet * +hs_init(void) +{ + HandleSet *hs = kzalloc(sizeof *hs, "handles"); + hs->refcount = 1; + return hs; +} + +HandleSet * +hs_copy(HandleSet *from) +{ + HandleSet *hs = hs_init(); + assert(from); + for (hid_t i = 0; i < HANDLE_MAX; i++) { + hs->h[i] = from->h[i]; + if (hs->h[i]) { + hs->h[i]->refcount++; + } + } + return hs; +} + +void +hs_unref(HandleSet *hs) +{ + assert(hs); + assert(hs->refcount != 0); + hs->refcount--; + if (hs->refcount == 0) { + for (hid_t i = 0; i < HANDLE_MAX; i++) { + hs_close(hs, i); + } + kfree(hs); + } +} + +hid_t +hs_findfree(HandleSet *hs, hid_t start) +{ + if (start < 0) start = 0; + for (hid_t i = start; i < HANDLE_MAX; i++) { + if (hs->h[i] == NULL) { + return i; + } + } + return -1; +} + +Handle * +hs_get(HandleSet *hs, hid_t id) +{ + if (id == HANDLE_NULLFS) { + // TODO get rid of this stupid hack + static Handle h = (Handle){ + .type = HANDLE_FS_FRONT, + .backend = NULL, + .refcount = 2, /* never free */ + }; + return &h; + } else if (0 <= id && id < HANDLE_MAX) { + return hs->h[id]; + } else { + return NULL; + } +} + +hid_t +hs_hinit(HandleSet *hs, enum handle_type type, Handle **hp) +{ + hid_t hid = hs_findfree(hs, 1); + if (hid < 0) return -1; + hs->h[hid] = handle_init(type); + if (hp) *hp = hs->h[hid]; + return hid; +} + +hid_t +hs_dup(HandleSet *hs, hid_t from, hid_t to, int flags) +{ + Handle *fromh, **toh; + + if (to < 0 || (flags & DUP_SEARCH)) { + to = hs_findfree(hs, to); + if (to < 0) return -EMFILE; + } else if (to >= HANDLE_MAX) { + return -EBADF; + } + + if (to == from) return to; + toh = &hs->h[to]; + fromh = hs_get(hs, from); + + if (*toh) handle_close(*toh); + *toh = fromh; + if (fromh) fromh->refcount++; + + return to; +} + +Handle * +hs_take(HandleSet *hs, hid_t hid) +{ + if (hid < 0 || hid >= HANDLE_MAX) { + return hs_get(hs, hid); // TODO can't this cause HANDLE_NULLFS to be freed? + } + Handle *h = hs->h[hid]; + hs->h[hid] = NULL; + return h; +} + +hid_t +hs_put(HandleSet *hs, Handle *h) +{ + assert(h); + hid_t hid = hs_findfree(hs, 1); + if (hid < 0) { + handle_close(h); + return hid; + } + hs->h[hid] = h; + return hid; +} diff --git a/src/kernel/handleset.h b/src/kernel/handleset.h new file mode 100644 index 0000000..6cdeee8 --- /dev/null +++ b/src/kernel/handleset.h @@ -0,0 +1,37 @@ +#pragma once +#include + +#define HANDLE_MAX 16 + +typedef struct { + uint64_t refcount; + Handle *h[HANDLE_MAX]; +} HandleSet; + +/** initialize HandleSets with a refcount of 1 */ +HandleSet *hs_init(void); +HandleSet *hs_copy(HandleSet *from); + +void hs_unref(HandleSet *hs); +/** finds the first free handle >= start. returns -1 on failure */ +hid_t hs_findfree(HandleSet *hs, hid_t start); + +Handle *hs_get(HandleSet *hs, hid_t id); + +/** creates a new handle. returns -1 on failure */ +hid_t hs_hinit(HandleSet *hs, enum handle_type type, Handle **hp); + +hid_t hs_dup(HandleSet *hs, hid_t from, hid_t to, int flags); + +static inline void +hs_close(HandleSet *hs, hid_t hid) { + hs_dup(hs, -1, hid, 0); +} + +/** Gets a handle and removes the set's reference to it, without decreasing + * the refcount. Meant to be used together with hs_put. */ +Handle *hs_take(HandleSet *hs, hid_t hid); +/* Put a handle into a set, taking the ownership away from the caller. + * Doesn't increase the refcount on success, decreases it on failure. */ +hid_t hs_put(HandleSet *hs, Handle *h); + diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 8182a54..194353a 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -24,7 +24,7 @@ Proc *proc_seed(void *data, size_t datalen) { proc_first->pages = pagedir_new(); proc_first->mount = vfs_mount_seed(); proc_first->globalid = next_pid++; - proc_first->_handles = kzalloc(sizeof(Handle) * HANDLE_MAX, "init fds"); + proc_first->hs = hs_init(); proc_first->pns = proc_first; proc_first->localid = 1; @@ -95,20 +95,10 @@ Proc *proc_fork(Proc *parent, int flags) { child->mount->refs++; if (flags & FORK_SHAREHANDLE) { - if (!parent->handles_refcount) { - parent->handles_refcount = kmalloc(sizeof *parent->handles_refcount, "fd refs"); - *parent->handles_refcount = 1; - } - *parent->handles_refcount += 1; - child->handles_refcount = parent->handles_refcount; - child->_handles = parent->_handles; + child->hs = parent->hs; + parent->hs->refcount++; } else { - child->_handles = kzalloc(sizeof(Handle) * HANDLE_MAX, "handles"); - for (hid_t h = 0; h < HANDLE_MAX; h++) { - child->_handles[h] = parent->_handles[h]; - if (child->_handles[h]) - child->_handles[h]->refcount++; - } + child->hs = hs_copy(parent->hs); } return child; @@ -253,12 +243,8 @@ void proc_kill(Proc *p, int ret) { timer_deschedule(p); } - if (unref(p->handles_refcount)) { - for (hid_t hid = 0; hid < HANDLE_MAX; hid++) - proc_handle_close(p, hid); - kfree(p->_handles); - } - p->_handles = NULL; + hs_unref(p->hs); + p->hs = NULL; vfs_mount_remref(p->mount); p->mount = NULL; @@ -484,81 +470,6 @@ Proc *proc_next(Proc *root, Proc *p) { return p->sibling; } -hid_t proc_find_free_handle(Proc *proc, hid_t start_at) { - if (start_at < 0) { - start_at = 0; - } - for (hid_t hid = start_at; hid < HANDLE_MAX; hid++) { - if (proc->_handles[hid] == NULL) - return hid; - } - return -1; -} - -Handle *proc_handle_get(Proc *p, hid_t id) { - if (id == HANDLE_NULLFS) { - static Handle h = (Handle){ - .type = HANDLE_FS_FRONT, - .backend = NULL, - .refcount = 2, /* never free */ - }; - return &h; - } else if (0 <= id && id < HANDLE_MAX) { - return p->_handles[id]; - } else { - return NULL; - } -} - -hid_t proc_handle_init(Proc *p, enum handle_type type, Handle **hs) { - hid_t hid = proc_find_free_handle(p, 1); - if (hid < 0) return -1; - p->_handles[hid] = handle_init(type); - if (hs) *hs = p->_handles[hid]; - return hid; -} - -hid_t proc_handle_dup(Proc *p, hid_t from, hid_t to, int flags) { - Handle *fromh, **toh; - - if (to < 0 || (flags & DUP_SEARCH)) { - to = proc_find_free_handle(p, to); - if (to < 0) return -EMFILE; - } else if (to >= HANDLE_MAX) { - return -EBADF; - } - - if (to == from) return to; - toh = &p->_handles[to]; - fromh = proc_handle_get(p, from); - - if (*toh) handle_close(*toh); - *toh = fromh; - if (fromh) fromh->refcount++; - - return to; -} - -Handle *proc_hid_take(Proc *p, hid_t hid) { - if (hid < 0 || hid >= HANDLE_MAX) { - return proc_handle_get(p, hid); - } - Handle *h = p->_handles[hid]; - p->_handles[hid] = NULL; - return h; -} - -hid_t proc_handle_put(Proc *p, Handle *h) { - assert(h); - hid_t hid = proc_find_free_handle(p, 1); - if (hid < 0) { - handle_close(h); - return hid; - } - p->_handles[hid] = h; - return hid; -} - void proc_setstate(Proc *p, enum proc_state state) { assert(p->state != PS_FREED); if (state == PS_FREED) { diff --git a/src/kernel/proc.h b/src/kernel/proc.h index 24206fb..f4b6b97 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -1,11 +1,9 @@ #pragma once #include -#include +#include #include #include -#define HANDLE_MAX 16 - /* legal transitions described by proc_setstate */ enum proc_state { PS_RUNNING, @@ -66,8 +64,7 @@ struct Proc { }; VfsMount *mount; - Handle **_handles; /* points to Handle *[HANDLE_MAX] */ - uint64_t *handles_refcount; /* works just like pages_refcount */ + HandleSet *hs; // TODO pids should be 64bit. also typedef pid_t uint32_t globalid; /* only for internal use, don't expose to userland */ @@ -130,21 +127,6 @@ _Noreturn void proc_switch_any(void); Proc *proc_next(Proc *root, Proc *p); -hid_t proc_find_free_handle(Proc *proc, hid_t start_at); -Handle *proc_handle_get(Proc *, hid_t); -hid_t proc_handle_init(Proc *, enum handle_type, Handle **); -hid_t proc_handle_dup(Proc *p, hid_t from, hid_t to, int flags); -static inline void proc_handle_close(Proc *p, hid_t hid) { - proc_handle_dup(p, -1, hid, 0); -} - -/* Gets a handle and removes the process' reference to it, without decreasing the refcount. - * Meant to be used together with proc_handle_put. */ -Handle *proc_hid_take(Proc *, hid_t); -/* Put a handle in a process, taking the ownership away from the caller. - * Doesn't increase the refcount on success, decreases it on failure. */ -hid_t proc_handle_put(Proc *, Handle *); - void proc_setstate(Proc *, enum proc_state); size_t pcpy_to(Proc *p, __user void *dst, const void *src, size_t len); diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 2a9a7b5..da6b48f 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -31,7 +31,7 @@ long _sys_fork(int flags, hid_t __user *fs_front) { if (flags & FORK_NEWFS) { Handle *h; - hid_t hid = proc_handle_init(proc_cur, HANDLE_FS_FRONT, &h); + hid_t hid = hs_hinit(proc_cur->hs, HANDLE_FS_FRONT, &h); if (hid < 0) { child->noreap = true; proc_kill(child, -EMFILE); @@ -124,7 +124,7 @@ long _sys_mount(hid_t hid, const char __user *path, long len) { len--; } - Handle *handle = proc_handle_get(proc_cur, hid); + Handle *handle = hs_get(proc_cur->hs, hid); if (!handle || handle->type != HANDLE_FS_FRONT) goto fail; backend = handle->backend; @@ -155,7 +155,7 @@ fail: } hid_t _sys_dup(hid_t from, hid_t to, int flags) { - SYSCALL_RETURN(proc_handle_dup(proc_cur, from, to, flags)); + SYSCALL_RETURN(hs_dup(proc_cur->hs, from, to, flags)); } static long simple_vfsop( @@ -165,7 +165,7 @@ static long simple_vfsop( assert(vfsop == VFSOP_READ || vfsop == VFSOP_WRITE || vfsop == VFSOP_GETSIZE); - Handle *h = proc_handle_get(proc_cur, hid); + Handle *h = hs_get(proc_cur->hs, hid); if (!h) { SYSCALL_RETURN(-EBADF); } @@ -226,31 +226,31 @@ long _sys_getsize(hid_t hid) { } long _sys_remove(hid_t hid) { - Handle *h = proc_handle_get(proc_cur, hid); + Handle *h = hs_get(proc_cur->hs, hid); if (!h) SYSCALL_RETURN(-EBADF); if (h->type != HANDLE_FILE) { - proc_handle_close(proc_cur, hid); + hs_close(proc_cur->hs, hid); SYSCALL_RETURN(-ENOSYS); } if (!h->writeable) { - proc_handle_close(proc_cur, hid); + hs_close(proc_cur->hs, hid); SYSCALL_RETURN(-EACCES); } vfsreq_create((VfsReq) { - .type = VFSOP_REMOVE, - .id = h->file_id, - .caller = proc_cur, - .backend = h->backend, - }); - proc_handle_close(proc_cur, hid); + .type = VFSOP_REMOVE, + .id = h->file_id, + .caller = proc_cur, + .backend = h->backend, + }); + hs_close(proc_cur->hs, hid); return -1; // dummy } long _sys_close(hid_t hid) { - if (!proc_handle_get(proc_cur, hid)) { + if (!hs_get(proc_cur->hs, hid)) { SYSCALL_RETURN(-EBADF); } - proc_handle_close(proc_cur, hid); + hs_close(proc_cur->hs, hid); SYSCALL_RETURN(0); } @@ -274,7 +274,7 @@ hid_t _sys_fs_wait(char __user *buf, long max_len, struct ufs_request __user *re } long _sys_fs_respond(hid_t hid, const void __user *buf, long ret, int flags) { - Handle *h = proc_handle_get(proc_cur, hid); + Handle *h = hs_get(proc_cur->hs, hid); if (!h || h->type != HANDLE_FS_REQ) SYSCALL_RETURN(-EBADF); VfsReq *req = h->req; if (req) { @@ -291,7 +291,7 @@ long _sys_fs_respond(hid_t hid, const void __user *buf, long ret, int flags) { vfsreq_finish(req, (void __user *)buf, ret, flags, proc_cur); } h->req = NULL; - proc_handle_close(proc_cur, hid); + hs_close(proc_cur->hs, hid); SYSCALL_RETURN(0); } @@ -335,11 +335,11 @@ long _sys_pipe(hid_t __user user_ends[2], int flags) { hid_t ends[2]; Handle *rend, *wend; - ends[0] = proc_handle_init(proc_cur, HANDLE_PIPE, &rend); - ends[1] = proc_handle_init(proc_cur, HANDLE_PIPE, &wend); + ends[0] = hs_hinit(proc_cur->hs, HANDLE_PIPE, &rend); + ends[1] = hs_hinit(proc_cur->hs, HANDLE_PIPE, &wend); if (ends[0] < 0 || ends[1] < 0) { - proc_handle_close(proc_cur, ends[0]); - proc_handle_close(proc_cur, ends[1]); + hs_close(proc_cur->hs, ends[0]); + hs_close(proc_cur->hs, ends[1]); SYSCALL_RETURN(-EMFILE); } wend->pipe.sister = rend; @@ -421,7 +421,7 @@ hid_t _sys_getprocfs(int flags) { proc_ns_create(proc_cur); Handle *h; - hid_t hid = proc_handle_init(proc_cur, HANDLE_FS_FRONT, &h); + hid_t hid = hs_hinit(proc_cur->hs, HANDLE_FS_FRONT, &h); if (hid < 0) { SYSCALL_RETURN(-EMFILE); } @@ -446,7 +446,7 @@ hid_t _sys_getnull(int flags) { SYSCALL_RETURN(-ENOSYS); } - hid_t hid = proc_handle_init(proc_cur, HANDLE_NULL, NULL); + hid_t hid = hs_hinit(proc_cur->hs, HANDLE_NULL, NULL); SYSCALL_RETURN((0 <= hid) ? hid : -EMFILE); } diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c index 21eecb8..0c763b3 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -59,7 +59,8 @@ void vfsreq_finish(VfsReq *req, char __user *stored, long ret, } else { /* delegating - moving a handle to the caller */ assert(handler); - h = proc_hid_take(handler, ret); + h = hs_take(handler->hs, ret); + // TODO!!! no null check h->readable = h->readable && OPEN_READABLE(req->flags); h->writeable = h->writeable && OPEN_WRITEABLE(req->flags); } @@ -67,7 +68,7 @@ void vfsreq_finish(VfsReq *req, char __user *stored, long ret, if (h) { // TODO write tests for caller getting killed while opening a file if (!req->caller) panic_unimplemented(); - ret = proc_handle_put(req->caller, h); + ret = hs_put(req->caller->hs, h); if (ret < 0) ret = -EMFILE; } else { ret = -1; @@ -148,7 +149,7 @@ static void vfs_backend_user_accept(VfsReq *req) { } Handle *h; - hid_t hid = proc_handle_init(handler, HANDLE_FS_REQ, &h); + hid_t hid = hs_hinit(handler->hs, HANDLE_FS_REQ, &h); if (hid < 0) panic_unimplemented(); h->req = req; proc_setstate(handler, PS_RUNNING); -- cgit v1.2.3