[kernel] Track capability reference counts
First pass at reference-counting capabilities.
This commit is contained in:
@@ -20,6 +20,8 @@ cap_table::create(obj::kobject *target, j6_cap_t caps)
|
||||
{
|
||||
j6_handle_t new_handle = make_handle(m_count);
|
||||
|
||||
util::scoped_lock lock {m_lock};
|
||||
|
||||
m_caps.insert({
|
||||
new_handle,
|
||||
j6_handle_invalid,
|
||||
@@ -29,12 +31,17 @@ cap_table::create(obj::kobject *target, j6_cap_t caps)
|
||||
target,
|
||||
});
|
||||
|
||||
if (target)
|
||||
target->handle_retain();
|
||||
|
||||
return new_handle;
|
||||
}
|
||||
|
||||
j6_handle_t
|
||||
cap_table::derive(j6_handle_t base, j6_cap_t caps)
|
||||
{
|
||||
util::scoped_lock lock {m_lock};
|
||||
|
||||
capability *existing = m_caps.find(base);
|
||||
if (!existing)
|
||||
return j6_handle_invalid;
|
||||
@@ -52,11 +59,43 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps)
|
||||
existing->object,
|
||||
});
|
||||
|
||||
if (existing->object)
|
||||
existing->object->handle_retain();
|
||||
|
||||
return new_handle;
|
||||
}
|
||||
|
||||
capability *
|
||||
cap_table::find(j6_handle_t id)
|
||||
cap_table::find_without_retain(j6_handle_t 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,16 @@ public:
|
||||
/// Create a new capability from an existing one.
|
||||
j6_handle_t derive(j6_handle_t id, j6_cap_t caps);
|
||||
|
||||
/// Get the capability referenced by a handle
|
||||
capability * find(j6_handle_t id);
|
||||
/// Get the capability referenced by a handle.
|
||||
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:
|
||||
using map_type = util::inplace_map<j6_handle_t, capability, j6_handle_invalid>;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <new>
|
||||
|
||||
#include <util/no_construct.h>
|
||||
|
||||
#include "assert.h"
|
||||
@@ -119,28 +121,44 @@ process::thread_exited(thread *th)
|
||||
void
|
||||
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
|
||||
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
|
||||
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
|
||||
process::list_handles(j6_handle_descriptor *handles, size_t len)
|
||||
{
|
||||
util::scoped_lock lock {m_handles_lock};
|
||||
|
||||
size_t count = 0;
|
||||
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");
|
||||
if (!cap) continue;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
/// Definition of process kobject types
|
||||
|
||||
#include <j6/cap_flags.h>
|
||||
#include <util/map.h>
|
||||
#include <util/node_map.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
@@ -114,6 +113,7 @@ private:
|
||||
util::vector<thread*> m_threads;
|
||||
|
||||
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 };
|
||||
state m_state;
|
||||
|
||||
@@ -184,10 +184,22 @@ for id, scope, method in syscalls.methods:
|
||||
cog.outl()
|
||||
|
||||
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)});""")
|
||||
|
||||
else:
|
||||
cog.outl(f""" profiler<syscall_profiles, {id}> profile {{"{name}"}};""")
|
||||
cog.outl(f""" return {name}({", ".join(args)});""")
|
||||
cog.outl(" j6_status_t _retval;")
|
||||
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("\n")
|
||||
]]]*/
|
||||
|
||||
@@ -30,7 +30,7 @@ j6_status_t get_handle(j6_handle_t id, j6_cap_t caps, T *&object)
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
capability *capdata = g_cap_table.find(id);
|
||||
capability *capdata = g_cap_table.retain(id);
|
||||
if (!capdata || capdata->type != T::type)
|
||||
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;
|
||||
}
|
||||
|
||||
capability *capdata = g_cap_table.find(id);
|
||||
capability *capdata = g_cap_table.retain(id);
|
||||
if (!capdata)
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user