[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:
Justin C. Miller
2023-02-26 11:32:30 -08:00
parent 0c777bc62f
commit ed95574c24
10 changed files with 222 additions and 14 deletions

View File

@@ -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",

View File

@@ -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

View File

@@ -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;

View 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

View File

@@ -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;
}

View File

@@ -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;