hw: implement helping of pager threads

Instead of blocking in case of exceptions and MMU faults, delegate
the faulter's scheduling context to the assigned pager thread.

Fix genodelabs/genode#5318
This commit is contained in:
Stefan Kalkowski
2025-01-10 15:15:21 +01:00
committed by Christian Helmuth
parent 84cabaf9b7
commit 3bdfb078bf
7 changed files with 125 additions and 33 deletions

View File

@@ -66,6 +66,7 @@ namespace Kernel {
constexpr Call_arg call_id_set_cpu_state() { return 125; }
constexpr Call_arg call_id_exception_state() { return 126; }
constexpr Call_arg call_id_single_step() { return 127; }
constexpr Call_arg call_id_ack_pager_signal() { return 128; }
/**
* Invalidate TLB entries for the `pd` in region `addr`, `sz`
@@ -147,13 +148,16 @@ namespace Kernel {
/**
* Set or unset the handler of an event that can be triggered by a thread
*
* \param thread pointer to thread kernel object
* \param thread reference to thread kernel object
* \param pager reference to pager kernel object
* \param signal_context_id capability id of the page-fault handler
*/
inline void thread_pager(Thread & thread,
inline void thread_pager(Thread &thread,
Thread &pager,
capid_t const signal_context_id)
{
call(call_id_thread_pager(), (Call_arg)&thread, signal_context_id);
call(call_id_thread_pager(), (Call_arg)&thread, (Call_arg)&pager,
signal_context_id);
}
@@ -202,6 +206,18 @@ namespace Kernel {
{
call(call_id_single_step(), (Call_arg)&thread, (Call_arg)&on);
}
/**
* Acknowledge a signal transmitted to a pager
*
* \param context signal context to acknowledge
* \param thread reference to faulting thread kernel object
* \param resolved whether fault got resolved
*/
inline void ack_pager_signal(capid_t const context, Thread &thread, bool resolved)
{
call(call_id_ack_pager_signal(), context, (Call_arg)&thread, resolved);
}
}
#endif /* _CORE__KERNEL__CORE_INTERFACE_H_ */

View File

@@ -572,7 +572,9 @@ void Thread::_call_pager()
{
/* override event route */
Thread &thread = *(Thread *)user_arg_1();
thread._pager = pd().cap_tree().find<Signal_context>((Kernel::capid_t)user_arg_2());
Thread &pager = *(Thread *)user_arg_2();
Signal_context &sc = *pd().cap_tree().find<Signal_context>((Kernel::capid_t)user_arg_3());
thread._fault_context.construct(pager, sc);
}
@@ -824,6 +826,24 @@ void Thread::_call_single_step() {
}
void Thread::_call_ack_pager_signal()
{
Signal_context * const c = pd().cap_tree().find<Signal_context>((Kernel::capid_t)user_arg_1());
if (!c)
Genode::raw(*this, ": cannot ack unknown signal context");
else
c->ack();
Thread &thread = *(Thread*)user_arg_2();
thread.helping_finished();
bool resolved = user_arg_3();
if (resolved) thread._restart();
else thread._become_inactive(AWAITS_RESTART);
}
void Thread::_call()
{
/* switch over unrestricted kernel calls */
@@ -906,6 +926,7 @@ void Thread::_call()
case call_id_set_cpu_state(): _call_set_cpu_state(); return;
case call_id_exception_state(): _call_exception_state(); return;
case call_id_single_step(): _call_single_step(); return;
case call_id_ack_pager_signal(): _call_ack_pager_signal(); return;
default:
Genode::raw(*this, ": unknown kernel call");
_die();
@@ -914,18 +935,33 @@ void Thread::_call()
}
void Thread::_signal_to_pager()
{
if (!_fault_context.constructed()) {
Genode::warning(*this, " could not send signal to pager");
_die();
return;
}
bool const help = Cpu_context::_helping_possible(_fault_context->pager);
if (help) Cpu_context::_help(_fault_context->pager);
else _become_inactive(AWAITS_RESTART);
_fault_context->sc.submit(1);
}
void Thread::_mmu_exception()
{
using namespace Genode;
using Genode::log;
_become_inactive(AWAITS_RESTART);
_exception_state = MMU_FAULT;
Cpu::mmu_fault(*regs, _fault);
_fault.ip = regs->ip;
if (_fault.type == Thread_fault::UNKNOWN) {
Genode::warning(*this, " raised unhandled MMU fault ", _fault);
_die();
return;
}
@@ -940,17 +976,16 @@ void Thread::_mmu_exception()
Hw::Mm::core_stack_area().size };
regs->for_each_return_address(stack, [&] (void **p) {
log(*p); });
_die();
return;
}
if (_pager && _pager->can_submit(1)) {
_pager->submit(1);
}
_signal_to_pager();
}
void Thread::_exception()
{
_become_inactive(AWAITS_RESTART);
_exception_state = EXCEPTION;
if (_type != USER) {
@@ -958,12 +993,7 @@ void Thread::_exception()
_die();
}
if (_pager && _pager->can_submit(1)) {
_pager->submit(1);
} else {
Genode::raw(*this, " could not send signal to pager on exception");
_die();
}
_signal_to_pager();
}

View File

@@ -173,7 +173,15 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo
size_t _ipc_rcv_caps { 0 };
Genode::Native_utcb *_utcb { nullptr };
Pd *_pd { nullptr };
Signal_context *_pager { nullptr };
struct Fault_context
{
Thread &pager;
Signal_context &sc;
};
Genode::Constructible<Fault_context> _fault_context {};
Thread_fault _fault { };
State _state;
Signal_handler _signal_handler { *this };
@@ -221,6 +229,11 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo
*/
void _die();
/**
* In case of fault, signal to pager, and help or block
*/
void _signal_to_pager();
/**
* Handle an exception thrown by the memory management unit
*/
@@ -296,6 +309,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo
void _call_set_cpu_state();
void _call_exception_state();
void _call_single_step();
void _call_ack_pager_signal();
template <typename T>
void _call_new(auto &&... args)

View File

@@ -19,6 +19,7 @@
/* base-internal includes */
#include <base/internal/capability_space.h>
#include <base/internal/native_thread.h>
using namespace Core;
@@ -71,13 +72,15 @@ void Pager_object::wake_up()
}
void Pager_object::start_paging(Kernel_object<Kernel::Signal_receiver> & receiver)
void Pager_object::start_paging(Kernel_object<Kernel::Signal_receiver> &receiver,
Platform_thread &pager_thread)
{
using Object = Kernel_object<Kernel::Signal_context>;
using Entry = Object_pool<Pager_object>::Entry;
create(*receiver, (unsigned long)this);
Entry::cap(Object::_cap);
_pager_thread = &pager_thread;
}
@@ -110,24 +113,23 @@ Pager_object::Pager_object(Cpu_session_capability cpu_session_cap,
void Pager_entrypoint::Thread::entry()
{
Untyped_capability cap;
while (1) {
if (cap.valid()) Kernel::ack_signal(Capability_space::capid(cap));
/* receive fault */
if (Kernel::await_signal(Capability_space::capid(_kobj.cap()))) continue;
if (Kernel::await_signal(Capability_space::capid(_kobj.cap())))
continue;
Pager_object *po = *(Pager_object**)Thread::myself()->utcb()->data();
cap = po->cap();
if (!po)
continue;
if (!po) continue;
Untyped_capability cap = po->cap();
/* fetch fault data */
Platform_thread * const pt = (Platform_thread *)po->badge();
if (!pt) {
warning("failed to get platform thread of faulter");
Kernel::ack_signal(Capability_space::capid(cap));
continue;
}
@@ -138,19 +140,25 @@ void Pager_entrypoint::Thread::entry()
"pd='", pt->pd().label(), "', "
"thread='", pt->label(), "', "
"ip=", Hex(pt->state().cpu.ip));
pt->fault_resolved(cap, false);
continue;
}
_fault = pt->fault_info();
/* try to resolve fault directly via local region managers */
if (po->pager(*this) == Pager_object::Pager_result::STOP)
if (po->pager(*this) == Pager_object::Pager_result::STOP) {
pt->fault_resolved(cap, false);
continue;
}
/* apply mapping that was determined by the local region managers */
{
Locked_ptr<Address_space> locked_ptr(pt->address_space());
if (!locked_ptr.valid()) continue;
if (!locked_ptr.valid()) {
pt->fault_resolved(cap, false);
continue;
}
Hw::Address_space * as = static_cast<Hw::Address_space*>(&*locked_ptr);
@@ -173,8 +181,7 @@ void Pager_entrypoint::Thread::entry()
1UL << _mapping.size_log2, flags);
}
/* let pager object go back to no-fault state */
po->wake_up();
pt->fault_resolved(cap, true);
}
}
@@ -206,7 +213,8 @@ Pager_capability Pager_entrypoint::manage(Pager_object &o)
if (cpu >= _cpus) {
error("Invalid location of pager object ", cpu);
} else {
o.start_paging(_threads[cpu]._kobj);
o.start_paging(_threads[cpu]._kobj,
*_threads[cpu].native_thread().platform_thread);
_threads[cpu].insert(&o);
}

View File

@@ -31,6 +31,7 @@
namespace Core {
class Platform;
class Platform_thread;
/**
* Interface used by generic region_map code
@@ -111,6 +112,7 @@ class Core::Pager_object : private Object_pool<Pager_object>::Entry,
Affinity::Location _location;
Cpu_session_capability _cpu_session_cap;
Thread_capability _thread_cap;
Platform_thread *_pager_thread { nullptr };
/**
* User-level signal handler registered for this pager object via
@@ -118,6 +120,12 @@ class Core::Pager_object : private Object_pool<Pager_object>::Entry,
*/
Signal_context_capability _exception_sigh { };
/*
* Noncopyable
*/
Pager_object(const Pager_object&) = delete;
Pager_object& operator=(const Pager_object&) = delete;
public:
/**
@@ -167,7 +175,8 @@ class Core::Pager_object : private Object_pool<Pager_object>::Entry,
*
* \param receiver signal receiver that receives the page faults
*/
void start_paging(Kernel_object<Kernel::Signal_receiver> & receiver);
void start_paging(Kernel_object<Kernel::Signal_receiver> &receiver,
Platform_thread &pager_thread);
/**
* Called when a page-fault finally could not be resolved
@@ -176,6 +185,11 @@ class Core::Pager_object : private Object_pool<Pager_object>::Entry,
void print(Output &out) const;
void with_pager(auto const &fn)
{
if (_pager_thread) fn(*_pager_thread);
}
/******************
** Pure virtual **

View File

@@ -187,12 +187,14 @@ void Platform_thread::start(void * const ip, void * const sp)
}
void Platform_thread::pager(Pager_object &pager)
void Platform_thread::pager(Pager_object &po)
{
using namespace Kernel;
thread_pager(*_kobj, Capability_space::capid(pager.cap()));
_pager = &pager;
po.with_pager([&] (Platform_thread &pt) {
thread_pager(*_kobj, *pt._kobj,
Capability_space::capid(po.cap())); });
_pager = &po;
}
@@ -238,3 +240,9 @@ void Platform_thread::restart()
{
Kernel::restart_thread(Capability_space::capid(_kobj.cap()));
}
void Platform_thread::fault_resolved(Untyped_capability cap, bool resolved)
{
Kernel::ack_pager_signal(Capability_space::capid(cap), *_kobj, resolved);
}

View File

@@ -216,6 +216,8 @@ class Core::Platform_thread : Noncopyable
void restart();
void fault_resolved(Untyped_capability, bool);
/**
* Pause this thread
*/