[kernel] First steps at removing channel objects

This commit does a number of things to start the transition of channels
from kernel to user space:

- Remove channel objects / syscalls from the kernel
- Add mutex type in libj6
- Add condition type in libj6
- Add a `ring` type flag for VMA syscalls to create ring buffers
- Implement a rudimentary shared memory channel using all of the above
This commit is contained in:
Justin C. Miller
2023-03-16 19:32:52 -07:00
parent ed95574c24
commit 9fa588566f
20 changed files with 323 additions and 251 deletions

View File

@@ -33,7 +33,6 @@ kernel = module("kernel",
"memory.h.cog",
"memory_bootstrap.cpp",
"msr.cpp",
"objects/channel.cpp",
"objects/event.cpp",
"objects/kobject.cpp",
"objects/mailbox.cpp",
@@ -51,7 +50,6 @@ kernel = module("kernel",
"syscall.s",
"syscall_verify.cpp.cog",
"syscalls.inc.cog",
"syscalls/channel.cpp",
"syscalls/event.cpp",
"syscalls/handle.cpp",
"syscalls/mailbox.cpp",

View File

@@ -1,89 +0,0 @@
#include <string.h>
#include "assert.h"
#include "memory.h"
#include "objects/channel.h"
#include "objects/thread.h"
#include "objects/vm_area.h"
extern obj::vm_area_guarded g_kernel_buffers;
namespace obj {
constexpr size_t buffer_bytes = mem::kernel_buffer_pages * mem::frame_size;
channel::channel() :
m_len {0},
m_data {g_kernel_buffers.get_section()},
m_closed {false},
m_buffer {reinterpret_cast<uint8_t*>(m_data), buffer_bytes},
kobject {kobject::type::channel}
{
}
channel::~channel()
{
if (!closed()) close();
}
size_t
channel::enqueue(const util::buffer &data)
{
util::scoped_lock lock {m_close_lock};
if (closed()) return 0;
size_t len = data.count;
void *buffer = nullptr;
len = m_buffer.reserve(len, &buffer);
memcpy(buffer, data.pointer, len);
m_buffer.commit(len);
if (len) {
thread *t = m_queue.pop_next();
lock.release();
if (t) t->wake();
}
return len;
}
size_t
channel::dequeue(util::buffer buffer, bool block)
{
util::scoped_lock lock {m_close_lock};
if (closed()) return 0;
void *data = nullptr;
size_t avail = m_buffer.get_block(&data);
if (!avail && block) {
thread &cur = thread::current();
m_queue.add_thread(&cur);
lock.release();
cur.block();
lock.reacquire();
avail = m_buffer.get_block(&data);
}
size_t len = buffer.count > avail ? avail : buffer.count;
memcpy(buffer.pointer, data, len);
m_buffer.consume(len);
return len;
}
void
channel::close()
{
util::scoped_lock lock {m_close_lock};
m_queue.clear();
g_kernel_buffers.return_section(m_data);
m_closed = true;
}
} // namespace obj

View File

@@ -1,54 +0,0 @@
#pragma once
/// \file channel.h
/// Definition of channel objects and related functions
#include <j6/cap_flags.h>
#include <util/bip_buffer.h>
#include <util/counted.h>
#include <util/spinlock.h>
#include "objects/kobject.h"
#include "wait_queue.h"
namespace obj {
/// Channels are uni-directional means of sending data
class channel :
public kobject
{
public:
/// Capabilities on a newly constructed channel handle
static constexpr j6_cap_t creation_caps = j6_cap_channel_all;
static constexpr kobject::type type = kobject::type::channel;
channel();
virtual ~channel();
/// Put a message into the channel
/// \arg data Buffer of data to write
/// \returns The number of bytes successfully written
size_t enqueue(const util::buffer &data);
/// Get a message from the channel, copied into a provided buffer
/// \arg buffer The buffer to copy data into
/// \arg block If true, block the calling thread until there is data
/// \returns The number of bytes copied into the provided buffer
size_t dequeue(util::buffer buffer, bool block = false);
/// Mark this channel as closed, all future calls to enqueue or
/// dequeue messages will fail with j6_status_closed.
void close();
/// Check if this channel has been closed.
inline bool closed() const { return m_closed; }
private:
size_t m_len;
uintptr_t m_data;
bool m_closed;
util::bip_buffer m_buffer;
util::spinlock m_close_lock;
wait_queue m_queue;
};
} // namespace obj

View File

@@ -13,7 +13,6 @@
#include "io.h"
#include "logger.h"
#include "msr.h"
#include "objects/channel.h"
#include "objects/process.h"
#include "objects/system.h"
#include "objects/thread.h"

View File

@@ -1,56 +0,0 @@
#include <j6/errors.h>
#include <j6/flags.h>
#include <j6/types.h>
#include <util/counted.h>
#include "objects/channel.h"
#include "syscalls/helpers.h"
using namespace obj;
namespace syscalls {
j6_status_t
channel_create(j6_handle_t *self)
{
construct_handle<channel>(self);
return j6_status_ok;
}
j6_status_t
channel_send(channel *self, void *data, size_t *data_len)
{
if (self->closed())
return j6_status_closed;
const util::buffer buffer {data, *data_len};
*data_len = self->enqueue(buffer);
return j6_status_ok;
}
j6_status_t
channel_receive(channel *self, void *data, size_t *data_len, uint64_t flags)
{
if (self->closed())
return j6_status_closed;
util::buffer buffer {data, *data_len};
const bool block = flags & j6_channel_block;
*data_len = self->dequeue(buffer, block);
return j6_status_ok;
}
j6_status_t
channel_close(channel *self)
{
if (self->closed())
return j6_status_closed;
self->close();
return j6_status_ok;
}
} // namespace syscalls

View File

@@ -3,6 +3,7 @@
#include <util/spinlock.h>
#include "clock.h"
#include "logger.h"
#include "objects/process.h"
#include "objects/thread.h"
#include "vm_space.h"
@@ -36,8 +37,13 @@ util::spinlock g_futexes_lock;
j6_status_t
futex_wait(const uint32_t *value, uint32_t expected, uint64_t timeout)
{
if (*value != expected)
return j6_status_would_block;
thread& t = thread::current();
process &p = t.parent();
if (*value != expected) {
log::spam(logs::syscall, "<%02x:%02x> futex %lx: %x != %x", p.obj_id(), t.obj_id(), value, *value, expected);
return j6_status_futex_changed;
}
uintptr_t address = reinterpret_cast<uintptr_t>(value);
vm_space &space = process::current().space();
@@ -46,15 +52,18 @@ futex_wait(const uint32_t *value, uint32_t expected, uint64_t timeout)
util::scoped_lock lock {g_futexes_lock};
futex &f = g_futexes[phys];
thread& t = thread::current();
if (timeout) {
timeout += clock::get().value();
t.set_wake_timeout(timeout);
}
log::spam(logs::syscall, "<%02x:%02x> blocking on futex %lx", p.obj_id(), t.obj_id(), value);
lock.release();
f.queue.wait();
log::spam(logs::syscall, "<%02x:%02x> woke on futex %lx", p.obj_id(), t.obj_id(), value);
return j6_status_ok;
}
@@ -69,9 +78,14 @@ futex_wake(const uint32_t *value, size_t count)
futex *f = g_futexes.find(phys);
if (f) {
for (unsigned i = 0; i < count; ++i) {
obj::thread *t = f->queue.pop_next();
t->wake();
if (count) {
for (unsigned i = 0; i < count; ++i) {
obj::thread *t = f->queue.pop_next();
if (!t) break;
t->wake();
}
} else {
f->queue.clear();
}
if (f->queue.empty())

View File

@@ -70,7 +70,7 @@ mailbox_respond(
return s;
}
bool block = flags & j6_mailbox_block;
bool block = flags & j6_flag_block;
j6_status_t s = self->receive(data, *reply_tag, block);
if (s != j6_status_ok)
return s;

View File

@@ -15,15 +15,23 @@ j6_status_t
vma_create(j6_handle_t *self, size_t size, uint32_t flags)
{
vm_flags f = vm_flags::user_mask & flags;
construct_handle<vm_area_open>(self, size, f);
if (util::bits::has(f, vm_flags::ring))
construct_handle<vm_area_ring>(self, size, f);
else
construct_handle<vm_area_open>(self, size, f);
return j6_status_ok;
}
j6_status_t
vma_create_map(j6_handle_t *self, size_t size, uintptr_t base, uint32_t flags)
{
vm_area *a = nullptr;
vm_flags f = vm_flags::user_mask & flags;
vm_area *a = construct_handle<vm_area_open>(self, size, f);
if (util::bits::has(f, vm_flags::ring))
a = construct_handle<vm_area_ring>(self, size, f);
else
a = construct_handle<vm_area_open>(self, size, f);
process::current().space().add(base, a);
return j6_status_ok;
}