aboutsummaryrefslogtreecommitdiff
path: root/kernel/features
diff options
context:
space:
mode:
authorMarvin Borner2021-04-17 22:59:57 +0200
committerMarvin Borner2021-04-17 22:59:57 +0200
commitcdf029471736f43776452930b7195a06ab143654 (patch)
treef8ab7dee26c589ff3666194b814d9457482ec02c /kernel/features
parent89b5b9acf6037fc1a87f9e85c64831187a47ba94 (diff)
Added I/O bus implementation for efficient IPC
This was a nice coding session. See ya tomorrow!
Diffstat (limited to 'kernel/features')
-rw-r--r--kernel/features/bus.c310
-rw-r--r--kernel/features/io.c28
-rw-r--r--kernel/features/proc.c22
3 files changed, 329 insertions, 31 deletions
diff --git a/kernel/features/bus.c b/kernel/features/bus.c
new file mode 100644
index 0000000..f8a561c
--- /dev/null
+++ b/kernel/features/bus.c
@@ -0,0 +1,310 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+
+#include <assert.h>
+#include <bus.h>
+#include <cpu.h>
+#include <crypto.h>
+#include <def.h>
+#include <errno.h>
+#include <io.h>
+#include <list.h>
+#include <mem.h>
+#include <mm.h>
+#include <stack.h>
+#include <str.h>
+
+// TODO: Consider different hashing method (possible duplicates in crc32)
+
+// Bus conns are always between *two* clients!
+struct bus_conn {
+ u32 bus; // Bus name hash
+ u32 conn;
+ struct stack *in; // Queue of bus owner
+ struct stack *out; // Queue of other client
+};
+
+struct bus_message {
+ void *data;
+ u32 conn;
+ u32 size;
+};
+
+struct bus {
+ u32 pid; // Owner
+ char name[128];
+ u32 hash;
+};
+
+PROTECTED struct list *bus_list = NULL;
+PROTECTED struct list *bus_conns = NULL;
+static u32 conn_cnt = 1; // 0 is reserved
+
+static struct bus *bus_find_bus(u32 hash)
+{
+ struct node *iterator = bus_list->head;
+ while (iterator) {
+ struct bus *bus = iterator->data;
+ if (bus->hash == hash)
+ return bus;
+
+ iterator = iterator->next;
+ }
+
+ return NULL;
+}
+
+static struct bus_conn *bus_find_conn(u32 conn)
+{
+ struct node *iterator = bus_conns->head;
+ while (iterator) {
+ struct bus_conn *bus_conn = iterator->data;
+ if (bus_conn->conn == conn)
+ return bus_conn;
+
+ iterator = iterator->next;
+ }
+
+ return NULL;
+}
+
+static struct bus *bus_find_owner(u32 pid)
+{
+ struct node *iterator = bus_list->head;
+ while (iterator) {
+ struct bus *bus = iterator->data;
+ if (bus->pid == pid)
+ return bus;
+
+ iterator = iterator->next;
+ }
+
+ return NULL;
+}
+
+static void bus_add_conn(u32 hash, u32 conn)
+{
+ struct bus_conn *bus_conn = zalloc(sizeof(*bus_conn));
+ bus_conn->bus = hash;
+ bus_conn->conn = conn;
+ bus_conn->in = stack_new();
+ bus_conn->out = stack_new();
+ list_add(bus_conns, bus_conn);
+}
+
+static res bus_register(const char *name)
+{
+ if (!memory_readable(name))
+ return -EFAULT;
+
+ u32 len = strlen_user(name);
+ if (len >= 128)
+ return -E2BIG;
+
+ u32 hash = crc32_user(0, name, len);
+ if (bus_find_bus(hash))
+ return -EBUSY;
+
+ struct bus *bus = zalloc(sizeof(*bus));
+ strlcpy_user(bus->name, name, sizeof(bus->name));
+ bus->pid = proc_current()->pid;
+ bus->hash = hash;
+ list_add(bus_list, bus);
+
+ return EOK;
+}
+
+static res bus_connect(const char *bus, u32 *conn)
+{
+ if (!memory_readable(bus) || !memory_writable(conn))
+ return -EFAULT;
+
+ u32 len = strlen_user(bus);
+ if (len >= 128)
+ return -E2BIG;
+
+ u32 hash = crc32_user(0, bus, len);
+ if (!bus_find_bus(hash))
+ return -ENOENT;
+
+ u32 conn_id = conn_cnt++;
+ bus_add_conn(hash, conn_id);
+ proc_current()->bus_conn = conn_id;
+
+ stac();
+ *conn = conn_id;
+ clac();
+
+ return EOK;
+}
+
+static res bus_send(u32 conn, void *buf, u32 count)
+{
+ if (!count)
+ return EOK;
+
+ if (!memory_readable(buf))
+ return -EFAULT;
+
+ struct bus_conn *bus_conn = bus_find_conn(conn);
+ if (!bus_conn)
+ return -ENOENT;
+
+ struct bus *bus = bus_find_bus(bus_conn->bus);
+ if (!bus)
+ return -ENOENT;
+
+ void *data = zalloc(count);
+ memcpy_user(data, buf, count);
+ struct bus_message *msg = zalloc(sizeof(*msg));
+ msg->data = data;
+ msg->conn = conn;
+ msg->size = count;
+
+ if (bus->pid == proc_current()->pid)
+ stack_push_bot(bus_conn->in, msg);
+ else
+ stack_push_bot(bus_conn->out, msg);
+
+ return count;
+}
+
+static res bus_conn_receive(struct bus_conn *bus_conn, void *buf, u32 offset, u32 count)
+{
+ struct bus *bus = bus_find_bus(bus_conn->bus);
+ if (!bus)
+ return -ENOENT;
+
+ struct bus_message *msg = NULL;
+ if (bus->pid == proc_current()->pid)
+ msg = stack_pop(bus_conn->out);
+ else
+ msg = stack_pop(bus_conn->out);
+
+ if (!msg)
+ return -EIO;
+
+ memcpy_user(buf, (u8 *)msg->data + offset, MIN(count, msg->size));
+ free(msg->data);
+ free(msg);
+ return MIN(count, msg->size);
+}
+
+static res bus_receive(void *buf, u32 offset, u32 count)
+{
+ if (!count)
+ return EOK;
+
+ if (!memory_readable(buf))
+ return -EFAULT;
+
+ struct bus *bus_owner = bus_find_owner(proc_current()->pid);
+ struct bus_conn *bus_conn = bus_find_conn(proc_current()->bus_conn);
+ if (!bus_owner && !bus_conn)
+ return -ENOENT;
+
+ // TODO: Better round-robin
+
+ if (bus_owner) {
+ struct node *iterator = bus_conns->head;
+ while (iterator) {
+ struct bus_conn *sub_bus = iterator->data;
+ if (sub_bus->bus == bus_owner->hash)
+ return bus_conn_receive(sub_bus, buf, offset, count);
+
+ iterator = iterator->next;
+ }
+ }
+
+ if (bus_conn)
+ return bus_conn_receive(bus_conn, buf, offset, count);
+
+ return -EAGAIN;
+}
+
+/**
+ * I/O integrations
+ */
+
+static res bus_control(u32 request, void *arg1, void *arg2, void *arg3)
+{
+ UNUSED(arg3);
+
+ switch (request) {
+ case IOCTL_BUS_CONNECT: {
+ return bus_connect(arg1, arg2);
+ }
+ case IOCTL_BUS_REGISTER: {
+ return bus_register(arg1);
+ }
+ default: {
+ return -EINVAL;
+ }
+ }
+}
+
+static res bus_write(void *buf, u32 offset, u32 count)
+{
+ if (offset)
+ return -EINVAL;
+
+ return bus_send(proc_current()->bus_conn, buf, count);
+}
+
+static res bus_read(void *buf, u32 offset, u32 count)
+{
+ return bus_receive(buf, offset, count);
+}
+
+static res bus_conn_ready(struct bus_conn *bus_conn)
+{
+ struct bus *bus = bus_find_bus(bus_conn->bus);
+ if (!bus)
+ return -ENOENT;
+
+ u8 ready = 0;
+ if (bus->pid == proc_current()->pid)
+ ready = !stack_empty(bus_conn->out);
+ else
+ ready = !stack_empty(bus_conn->out);
+
+ return ready ? EOK : -EAGAIN;
+}
+
+static res bus_ready(void)
+{
+ struct bus *bus_owner = bus_find_owner(proc_current()->pid);
+ struct bus_conn *bus_conn = bus_find_conn(proc_current()->bus_conn);
+ if (!bus_owner && !bus_conn)
+ return -ENOENT;
+
+ // TODO: Better round-robin
+
+ if (bus_owner) {
+ struct node *iterator = bus_conns->head;
+ while (iterator) {
+ struct bus_conn *sub_bus = iterator->data;
+ if (sub_bus->bus == bus_owner->hash)
+ return bus_conn_ready(sub_bus);
+
+ iterator = iterator->next;
+ }
+ }
+
+ if (bus_conn)
+ return bus_conn_ready(bus_conn);
+
+ return -EAGAIN;
+}
+
+CLEAR void bus_install(void)
+{
+ bus_list = list_new();
+ bus_conns = list_new();
+
+ struct io_dev *dev = zalloc(sizeof(*dev));
+ dev->control = bus_control;
+ dev->read = bus_read;
+ dev->ready = bus_ready;
+ dev->write = bus_write;
+ io_add(IO_BUS, dev);
+}
diff --git a/kernel/features/io.c b/kernel/features/io.c
index d0cca07..c48eb62 100644
--- a/kernel/features/io.c
+++ b/kernel/features/io.c
@@ -1,6 +1,7 @@
// MIT License, Copyright (c) 2021 Marvin Borner
#include <assert.h>
+#include <bus.h>
#include <cpu.h>
#include <def.h>
#include <fb.h>
@@ -83,16 +84,23 @@ res io_poll(u32 *devs)
io_remove_group(group);
group--;
return -ENOENT;
- } else if (dev->ready && dev->ready() == EOK) {
- io_remove_group(group);
- group--;
- return io;
- } else {
- struct io_listener *listener = zalloc(sizeof(*listener));
- listener->group = group;
- listener->proc = proc_current();
- list_add(io_listeners[io], listener);
}
+
+ if (dev->ready) {
+ res ready = dev->ready();
+ if (ready == EOK) {
+ io_remove_group(group);
+ group--;
+ return io;
+ } else if (ready != -EAGAIN) {
+ return ready;
+ }
+ }
+
+ struct io_listener *listener = zalloc(sizeof(*listener));
+ listener->group = group;
+ listener->proc = proc_current();
+ list_add(io_listeners[io], listener);
}
proc_state(proc_current(), PROC_BLOCKED);
@@ -161,7 +169,6 @@ void io_block(enum io_type io, struct proc *proc)
void io_unblock(enum io_type io)
{
- printf("%d\n", group_id);
assert(io_type_valid(io));
struct node *iterator = io_listeners[io]->head;
while (iterator) {
@@ -194,4 +201,5 @@ CLEAR void io_install(struct boot_info *boot)
timer_install();
fb_install(boot->vid);
+ bus_install();
}
diff --git a/kernel/features/proc.c b/kernel/features/proc.c
index c972f1b..cada97d 100644
--- a/kernel/features/proc.c
+++ b/kernel/features/proc.c
@@ -288,15 +288,7 @@ static res procfs_write(const char *path, void *buf, u32 offset, u32 count, stru
clac();
path++;
- if (!memcmp_user(path, "msg", 4)) {
- void *msg_data = malloc(count);
- memcpy_user(msg_data, buf, count);
- struct procfs_message *msg = malloc(sizeof(*msg));
- msg->data = msg_data;
- msg->size = count;
- stack_push_bot(p->messages, msg); // TODO: Use offset
- return count;
- } else if (!memcmp_user(path, "io/", 3)) {
+ if (!memcmp_user(path, "io/", 3)) {
path += 3;
enum stream_defaults id = procfs_stream(path);
if (id == STREAM_UNKNOWN)
@@ -346,18 +338,6 @@ static res procfs_read(const char *path, void *buf, u32 offset, u32 count, struc
const char *status = p->state == PROC_RUNNING ? "running" : "sleeping";
memcpy_user(buf, status + offset, count);
return count;
- } else if (!memcmp_user(path, "msg", 4)) {
- if (stack_empty(p->messages)) {
- return -EIO; // This shouldn't happen
- } else {
- struct procfs_message *msg = stack_pop(p->messages);
- if (!msg)
- return -EIO;
- memcpy_user(buf, msg->data + offset, MIN(count, msg->size));
- free(msg->data);
- free(msg);
- return MIN(count, msg->size);
- }
} else if (!memcmp_user(path, "io/", 3)) {
path += 3;
enum stream_defaults id = procfs_stream(path);