mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kernel] Add (wip) futex syscalls
Add the syscalls j6_futex_wait and j6_futex_wake. Currently marking this as WIP as they need more testing. Added to support futexes: - vm_area and vm_space support for looking up physical address for a virtual address - libj6 mutex implementation using futex system calls
This commit is contained in:
@@ -57,6 +57,7 @@ kernel = module("kernel",
|
||||
"syscalls/mailbox.cpp",
|
||||
"syscalls/object.cpp",
|
||||
"syscalls/process.cpp",
|
||||
"syscalls/futex.cpp",
|
||||
"syscalls/system.cpp",
|
||||
"syscalls/thread.cpp",
|
||||
"syscalls/vm_area.cpp",
|
||||
|
||||
@@ -86,7 +86,7 @@ vm_area_fixed::resize(size_t size)
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_fixed::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
@@ -106,11 +106,16 @@ vm_area_untracked::~vm_area_untracked()
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_untracked::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset > m_size)
|
||||
return false;
|
||||
|
||||
if (!alloc) {
|
||||
phys = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return frame_allocator::get().allocate(1, &phys);
|
||||
}
|
||||
|
||||
@@ -136,9 +141,12 @@ vm_area_open::~vm_area_open()
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_open::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
if (alloc)
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
else
|
||||
return page_tree::find(m_mapped, offset, phys);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -171,7 +179,7 @@ vm_area_guarded::return_section(uintptr_t addr)
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset >= m_stacks.end())
|
||||
return false;
|
||||
@@ -181,7 +189,7 @@ vm_area_guarded::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
if ((offset >> 12) % m_pages == 0)
|
||||
return false;
|
||||
|
||||
return vm_area_open::get_page(offset, phys);
|
||||
return vm_area_open::get_page(offset, phys, alloc);
|
||||
}
|
||||
|
||||
vm_area_ring::vm_area_ring(size_t size, vm_flags flags) :
|
||||
@@ -193,11 +201,11 @@ vm_area_ring::vm_area_ring(size_t size, vm_flags flags) :
|
||||
vm_area_ring::~vm_area_ring() {}
|
||||
|
||||
bool
|
||||
vm_area_ring::get_page(uintptr_t offset, uintptr_t &phys)
|
||||
vm_area_ring::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
{
|
||||
if (offset > m_bufsize)
|
||||
offset -= m_bufsize;
|
||||
return vm_area_open::get_page(offset, phys);
|
||||
return vm_area_open::get_page(offset, phys, alloc);
|
||||
}
|
||||
|
||||
} // namespace obj
|
||||
|
||||
@@ -71,8 +71,10 @@ public:
|
||||
/// Get the physical page for the given offset
|
||||
/// \arg offset The offset into the VMA
|
||||
/// \arg phys [out] Receives the physical page address, if any
|
||||
/// \arg alloc If true, and this is a valid address with no frame,
|
||||
/// allocate one if applicable
|
||||
/// \returns True if there should be a page at the given offset
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) = 0;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) = 0;
|
||||
|
||||
protected:
|
||||
/// A VMA is not deleted until both no handles remain AND it's not
|
||||
@@ -106,7 +108,7 @@ public:
|
||||
virtual ~vm_area_fixed();
|
||||
|
||||
virtual size_t resize(size_t size) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
|
||||
private:
|
||||
uintptr_t m_start;
|
||||
@@ -124,7 +126,7 @@ public:
|
||||
vm_area_open(size_t size, vm_flags flags);
|
||||
virtual ~vm_area_open();
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
|
||||
/// Tell this VMA about an existing mapping that did not originate
|
||||
/// from get_page.
|
||||
@@ -147,7 +149,7 @@ public:
|
||||
virtual ~vm_area_untracked();
|
||||
|
||||
virtual bool add_to(vm_space *space) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -175,7 +177,7 @@ public:
|
||||
/// Return a section address to the available pool
|
||||
void return_section(uintptr_t addr);
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
|
||||
private:
|
||||
size_t m_pages;
|
||||
@@ -196,7 +198,7 @@ public:
|
||||
vm_area_ring(size_t size, vm_flags flags);
|
||||
virtual ~vm_area_ring();
|
||||
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys) override;
|
||||
virtual bool get_page(uintptr_t offset, uintptr_t &phys, bool alloc = true) override;
|
||||
|
||||
private:
|
||||
size_t m_bufsize;
|
||||
|
||||
84
src/kernel/syscalls/futex.cpp
Normal file
84
src/kernel/syscalls/futex.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <j6/errors.h>
|
||||
#include <util/node_map.h>
|
||||
#include <util/spinlock.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "objects/process.h"
|
||||
#include "objects/thread.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
using namespace obj;
|
||||
|
||||
namespace syscalls {
|
||||
|
||||
struct futex
|
||||
{
|
||||
uintptr_t address;
|
||||
wait_queue queue;
|
||||
|
||||
futex() = default;
|
||||
futex(futex &&other) :
|
||||
address {other.address},
|
||||
queue {std::move(other.queue)} {}
|
||||
|
||||
futex & operator=(futex &&other) {
|
||||
address = other.address;
|
||||
queue = std::move(other.queue);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
inline uint64_t & get_map_key(futex &f) { return f.address; }
|
||||
|
||||
util::node_map<uintptr_t, futex> g_futexes;
|
||||
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;
|
||||
|
||||
uintptr_t address = reinterpret_cast<uintptr_t>(value);
|
||||
vm_space &space = process::current().space();
|
||||
uintptr_t phys = space.find_physical(address);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
lock.release();
|
||||
f.queue.wait();
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
futex_wake(const uint32_t *value, size_t count)
|
||||
{
|
||||
uintptr_t address = reinterpret_cast<uintptr_t>(value);
|
||||
vm_space &space = process::current().space();
|
||||
uintptr_t phys = space.find_physical(address);
|
||||
|
||||
util::scoped_lock lock {g_futexes_lock};
|
||||
|
||||
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 (f->queue.empty())
|
||||
g_futexes.erase(phys);
|
||||
}
|
||||
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
} // namespace syscalls
|
||||
@@ -367,3 +367,17 @@ vm_space::copy(vm_space &source, vm_space &dest, const void *from, void *to, siz
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
vm_space::find_physical(uintptr_t virt)
|
||||
{
|
||||
uintptr_t base = 0;
|
||||
obj::vm_area *area = get(virt, &base);
|
||||
if (!area)
|
||||
return 0;
|
||||
|
||||
uintptr_t phys = 0;
|
||||
area->get_page(virt - base, phys, false);
|
||||
return phys;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,12 @@ public:
|
||||
/// \returnd The number of bytes copied
|
||||
static size_t copy(vm_space &source, vm_space &dest, const void *from, void *to, size_t length);
|
||||
|
||||
/// Get the physical address of a virtual address from this space
|
||||
/// \arg vrit The virtual address
|
||||
/// \returns The physical address mapped to that virtual address,
|
||||
/// or 0 for none.
|
||||
uintptr_t find_physical(uintptr_t virt);
|
||||
|
||||
private:
|
||||
friend class obj::vm_area;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ j6 = module("j6",
|
||||
sources = [
|
||||
"init.cpp",
|
||||
"init.s",
|
||||
"mutex.cpp",
|
||||
"protocol_ids.cpp",
|
||||
"syscalls.s.cog",
|
||||
"sysconf.cpp.cog",
|
||||
@@ -17,6 +18,7 @@ j6 = module("j6",
|
||||
"j6/errors.h",
|
||||
"j6/flags.h",
|
||||
"j6/init.h",
|
||||
"j6/mutex.hh",
|
||||
"j6/protocols.h",
|
||||
"j6/protocols/service_locator.h",
|
||||
"j6/syscalls.h.cog",
|
||||
|
||||
42
src/libraries/j6/j6/mutex.hh
Normal file
42
src/libraries/j6/j6/mutex.hh
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
/// \file mutex.hh
|
||||
/// High level mutex interface
|
||||
|
||||
// The kernel depends on libj6 for some shared code,
|
||||
// but should not include the user-specific code.
|
||||
#ifndef __j6kernel
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
class mutex
|
||||
{
|
||||
public:
|
||||
mutex() : m_state(0) {}
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
uint32_t m_state;
|
||||
};
|
||||
|
||||
class scoped_lock
|
||||
{
|
||||
public:
|
||||
inline scoped_lock(mutex &m) : m_mutex {m} {
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
inline ~scoped_lock() {
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
mutex &m_mutex;
|
||||
};
|
||||
|
||||
} // namespace j6
|
||||
|
||||
#endif // __j6kernel
|
||||
36
src/libraries/j6/mutex.cpp
Normal file
36
src/libraries/j6/mutex.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// The kernel depends on libj6 for some shared code,
|
||||
// but should not include the user-specific code.
|
||||
#ifndef __j6kernel
|
||||
|
||||
#include <j6/mutex.hh>
|
||||
#include <j6/syscalls.h>
|
||||
|
||||
namespace j6 {
|
||||
|
||||
void
|
||||
mutex::lock()
|
||||
{
|
||||
uint32_t lock = 0;
|
||||
if ((lock = __sync_val_compare_and_swap(&m_state, 0, 1)) != 0) {
|
||||
if (lock != 2)
|
||||
lock = __atomic_exchange_n(&m_state, 2, __ATOMIC_ACQ_REL);
|
||||
while (lock) {
|
||||
j6_futex_wait(&m_state, 2, 0);
|
||||
lock = __atomic_exchange_n(&m_state, 2, __ATOMIC_ACQ_REL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mutex::unlock()
|
||||
{
|
||||
if (__atomic_fetch_sub(&m_state, 1, __ATOMIC_ACQ_REL) != 1) {
|
||||
__atomic_store_n(&m_state, 0, __ATOMIC_RELEASE);
|
||||
j6_futex_wake(&m_state, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace j6
|
||||
|
||||
#endif // __j6kernel
|
||||
Reference in New Issue
Block a user