[kernel] Track capability reference counts

First pass at reference-counting capabilities.
This commit is contained in:
Justin C. Miller
2023-01-14 15:40:18 -08:00
parent 7150e11ed0
commit e93f48e2f7
6 changed files with 94 additions and 14 deletions

View File

@@ -20,6 +20,8 @@ cap_table::create(obj::kobject *target, j6_cap_t caps)
{ {
j6_handle_t new_handle = make_handle(m_count); j6_handle_t new_handle = make_handle(m_count);
util::scoped_lock lock {m_lock};
m_caps.insert({ m_caps.insert({
new_handle, new_handle,
j6_handle_invalid, j6_handle_invalid,
@@ -29,12 +31,17 @@ cap_table::create(obj::kobject *target, j6_cap_t caps)
target, target,
}); });
if (target)
target->handle_retain();
return new_handle; return new_handle;
} }
j6_handle_t j6_handle_t
cap_table::derive(j6_handle_t base, j6_cap_t caps) cap_table::derive(j6_handle_t base, j6_cap_t caps)
{ {
util::scoped_lock lock {m_lock};
capability *existing = m_caps.find(base); capability *existing = m_caps.find(base);
if (!existing) if (!existing)
return j6_handle_invalid; return j6_handle_invalid;
@@ -52,11 +59,43 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps)
existing->object, existing->object,
}); });
if (existing->object)
existing->object->handle_retain();
return new_handle; return new_handle;
} }
capability * capability *
cap_table::find(j6_handle_t id) cap_table::find_without_retain(j6_handle_t id)
{ {
return m_caps.find(id); return m_caps.find(id);
} }
capability *
cap_table::retain(j6_handle_t id)
{
util::scoped_lock lock {m_lock};
capability *cap = m_caps.find(id);
if (!cap)
return nullptr;
++cap->holders;
return cap;
}
void
cap_table::release(j6_handle_t id)
{
util::scoped_lock lock {m_lock};
capability *cap = m_caps.find(id);
if (!cap)
return;
if (--cap->holders == 0) {
if (cap->object)
cap->object->handle_release();
m_caps.erase(id);
}
}

View File

@@ -33,8 +33,16 @@ public:
/// Create a new capability from an existing one. /// Create a new capability from an existing one.
j6_handle_t derive(j6_handle_t id, j6_cap_t caps); j6_handle_t derive(j6_handle_t id, j6_cap_t caps);
/// Get the capability referenced by a handle /// Get the capability referenced by a handle.
capability * find(j6_handle_t id); capability * retain(j6_handle_t id);
/// Release a retained capability when it is no longer being used.
void release(j6_handle_t id);
/// Get the capability referenced by a handle, without increasing
/// its refcount. WARNING: You probably want retain(), using a capability
/// (or the object it points to) without retaining it is dangerous.
capability * find_without_retain(j6_handle_t id);
private: private:
using map_type = util::inplace_map<j6_handle_t, capability, j6_handle_invalid>; using map_type = util::inplace_map<j6_handle_t, capability, j6_handle_invalid>;

View File

@@ -1,3 +1,5 @@
#include <new>
#include <util/no_construct.h> #include <util/no_construct.h>
#include "assert.h" #include "assert.h"
@@ -119,28 +121,44 @@ process::thread_exited(thread *th)
void void
process::add_handle(j6_handle_t handle) process::add_handle(j6_handle_t handle)
{ {
m_handles.add(handle); capability *c = g_cap_table.retain(handle);
kassert(c, "Trying to add a non-existant handle to a process!");
if (c) {
util::scoped_lock lock {m_handles_lock};
m_handles.add(handle);
}
} }
bool bool
process::remove_handle(j6_handle_t id) process::remove_handle(j6_handle_t handle)
{ {
return m_handles.remove(id); util::scoped_lock lock {m_handles_lock};
bool removed = m_handles.remove(handle);
lock.release();
if (removed)
g_cap_table.release(handle);
return removed;
} }
bool bool
process::has_handle(j6_handle_t id) process::has_handle(j6_handle_t handle)
{ {
return m_handles.contains(id); util::scoped_lock lock {m_handles_lock};
return m_handles.contains(handle);
} }
size_t size_t
process::list_handles(j6_handle_descriptor *handles, size_t len) process::list_handles(j6_handle_descriptor *handles, size_t len)
{ {
util::scoped_lock lock {m_handles_lock};
size_t count = 0; size_t count = 0;
for (j6_handle_t handle : m_handles) { for (j6_handle_t handle : m_handles) {
capability *cap = g_cap_table.find(handle); capability *cap = g_cap_table.find_without_retain(handle);
kassert(cap, "Found process handle that wasn't in the cap table"); kassert(cap, "Found process handle that wasn't in the cap table");
if (!cap) continue; if (!cap) continue;

View File

@@ -3,7 +3,6 @@
/// Definition of process kobject types /// Definition of process kobject types
#include <j6/cap_flags.h> #include <j6/cap_flags.h>
#include <util/map.h>
#include <util/node_map.h> #include <util/node_map.h>
#include <util/vector.h> #include <util/vector.h>
@@ -114,6 +113,7 @@ private:
util::vector<thread*> m_threads; util::vector<thread*> m_threads;
util::node_set<j6_handle_t, j6_handle_invalid, heap_allocated> m_handles; util::node_set<j6_handle_t, j6_handle_invalid, heap_allocated> m_handles;
util::spinlock m_handles_lock;
enum class state : uint8_t { running, exited }; enum class state : uint8_t { running, exited };
state m_state; state m_state;

View File

@@ -184,10 +184,22 @@ for id, scope, method in syscalls.methods:
cog.outl() cog.outl()
if "noreturn" in method.options: if "noreturn" in method.options:
if handles:
cog.outl(f""" static_assert(false, "Cannot define a `noreturn` syscall that holds handles.");""")
cog.outl(f""" {name}({", ".join(args)});""") cog.outl(f""" {name}({", ".join(args)});""")
else: else:
cog.outl(f""" profiler<syscall_profiles, {id}> profile {{"{name}"}};""") cog.outl(" j6_status_t _retval;")
cog.outl(f""" return {name}({", ".join(args)});""") cog.outl(" {")
cog.outl(f""" profiler<syscall_profiles, {id}> profile {{"{name}"}};""")
cog.outl(f""" _retval = {name}({", ".join(args)});""")
cog.outl(" }\n")
for type, arg, caps in handles:
cog.outl(f" release_handle({arg});")
cog.outl(f""" return _retval;""")
cog.outl("}") cog.outl("}")
cog.outl("\n") cog.outl("\n")
]]]*/ ]]]*/

View File

@@ -30,7 +30,7 @@ j6_status_t get_handle(j6_handle_t id, j6_cap_t caps, T *&object)
return j6_status_ok; return j6_status_ok;
} }
capability *capdata = g_cap_table.find(id); capability *capdata = g_cap_table.retain(id);
if (!capdata || capdata->type != T::type) if (!capdata || capdata->type != T::type)
return j6_err_invalid_arg; return j6_err_invalid_arg;
@@ -56,7 +56,7 @@ inline j6_status_t get_handle<obj::kobject>(j6_handle_t id, j6_cap_t caps, obj::
return j6_status_ok; return j6_status_ok;
} }
capability *capdata = g_cap_table.find(id); capability *capdata = g_cap_table.retain(id);
if (!capdata) if (!capdata)
return j6_err_invalid_arg; return j6_err_invalid_arg;
@@ -68,4 +68,7 @@ inline j6_status_t get_handle<obj::kobject>(j6_handle_t id, j6_cap_t caps, obj::
return j6_status_ok; return j6_status_ok;
} }
inline void release_handle(j6_handle_t id) { g_cap_table.release(id); }
inline void release_handle(j6_handle_t *id) { g_cap_table.release(*id); }
} // namespace syscalls } // namespace syscalls