summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2024-05-11 20:24:17 +0200
committerdzwdz2024-05-11 20:24:17 +0200
commit9047f1e3f502658de12808015179ab3881a4b03f (patch)
tree4b7f19f3152ab1ed19bc83e2213c679f41110e86
parent1e9887d904280c43c5a92570a07627689c89b48f (diff)
kernel: refactor handle management out of proc.c
-rw-r--r--src/kernel/handleset.c128
-rw-r--r--src/kernel/handleset.h37
-rw-r--r--src/kernel/proc.c101
-rw-r--r--src/kernel/proc.h22
-rw-r--r--src/kernel/syscalls.c46
-rw-r--r--src/kernel/vfs/request.c7
6 files changed, 200 insertions, 141 deletions
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 <camellia/errno.h>
+#include <camellia/flags.h>
+#include <kernel/handleset.h>
+#include <kernel/malloc.h>
+#include <kernel/panic.h>
+
+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 <kernel/handle.h>
+
+#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 <kernel/arch/generic.h>
-#include <kernel/handle.h>
+#include <kernel/handleset.h>
#include <kernel/types.h>
#include <stdbool.h>
-#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);