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_set_cpu_state() { return 125; }
constexpr Call_arg call_id_exception_state() { return 126; } constexpr Call_arg call_id_exception_state() { return 126; }
constexpr Call_arg call_id_single_step() { return 127; } 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` * 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 * 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 * \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) 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); 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_ */ #endif /* _CORE__KERNEL__CORE_INTERFACE_H_ */

View File

@@ -572,7 +572,9 @@ void Thread::_call_pager()
{ {
/* override event route */ /* override event route */
Thread &thread = *(Thread *)user_arg_1(); 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() void Thread::_call()
{ {
/* switch over unrestricted kernel calls */ /* 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_set_cpu_state(): _call_set_cpu_state(); return;
case call_id_exception_state(): _call_exception_state(); return; case call_id_exception_state(): _call_exception_state(); return;
case call_id_single_step(): _call_single_step(); return; case call_id_single_step(): _call_single_step(); return;
case call_id_ack_pager_signal(): _call_ack_pager_signal(); return;
default: default:
Genode::raw(*this, ": unknown kernel call"); Genode::raw(*this, ": unknown kernel call");
_die(); _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() void Thread::_mmu_exception()
{ {
using namespace Genode; using namespace Genode;
using Genode::log; using Genode::log;
_become_inactive(AWAITS_RESTART);
_exception_state = MMU_FAULT; _exception_state = MMU_FAULT;
Cpu::mmu_fault(*regs, _fault); Cpu::mmu_fault(*regs, _fault);
_fault.ip = regs->ip; _fault.ip = regs->ip;
if (_fault.type == Thread_fault::UNKNOWN) { if (_fault.type == Thread_fault::UNKNOWN) {
Genode::warning(*this, " raised unhandled MMU fault ", _fault); Genode::warning(*this, " raised unhandled MMU fault ", _fault);
_die();
return; return;
} }
@@ -940,17 +976,16 @@ void Thread::_mmu_exception()
Hw::Mm::core_stack_area().size }; Hw::Mm::core_stack_area().size };
regs->for_each_return_address(stack, [&] (void **p) { regs->for_each_return_address(stack, [&] (void **p) {
log(*p); }); log(*p); });
_die();
return;
} }
if (_pager && _pager->can_submit(1)) { _signal_to_pager();
_pager->submit(1);
}
} }
void Thread::_exception() void Thread::_exception()
{ {
_become_inactive(AWAITS_RESTART);
_exception_state = EXCEPTION; _exception_state = EXCEPTION;
if (_type != USER) { if (_type != USER) {
@@ -958,12 +993,7 @@ void Thread::_exception()
_die(); _die();
} }
if (_pager && _pager->can_submit(1)) { _signal_to_pager();
_pager->submit(1);
} else {
Genode::raw(*this, " could not send signal to pager on exception");
_die();
}
} }

View File

@@ -173,7 +173,15 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo
size_t _ipc_rcv_caps { 0 }; size_t _ipc_rcv_caps { 0 };
Genode::Native_utcb *_utcb { nullptr }; Genode::Native_utcb *_utcb { nullptr };
Pd *_pd { 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 { }; Thread_fault _fault { };
State _state; State _state;
Signal_handler _signal_handler { *this }; Signal_handler _signal_handler { *this };
@@ -221,6 +229,11 @@ class Kernel::Thread : private Kernel::Object, public Cpu_context, private Timeo
*/ */
void _die(); 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 * 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_set_cpu_state();
void _call_exception_state(); void _call_exception_state();
void _call_single_step(); void _call_single_step();
void _call_ack_pager_signal();
template <typename T> template <typename T>
void _call_new(auto &&... args) void _call_new(auto &&... args)

View File

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

View File

@@ -31,6 +31,7 @@
namespace Core { namespace Core {
class Platform; class Platform;
class Platform_thread;
/** /**
* Interface used by generic region_map code * Interface used by generic region_map code
@@ -111,6 +112,7 @@ class Core::Pager_object : private Object_pool<Pager_object>::Entry,
Affinity::Location _location; Affinity::Location _location;
Cpu_session_capability _cpu_session_cap; Cpu_session_capability _cpu_session_cap;
Thread_capability _thread_cap; Thread_capability _thread_cap;
Platform_thread *_pager_thread { nullptr };
/** /**
* User-level signal handler registered for this pager object via * 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 { }; Signal_context_capability _exception_sigh { };
/*
* Noncopyable
*/
Pager_object(const Pager_object&) = delete;
Pager_object& operator=(const Pager_object&) = delete;
public: public:
/** /**
@@ -167,7 +175,8 @@ class Core::Pager_object : private Object_pool<Pager_object>::Entry,
* *
* \param receiver signal receiver that receives the page faults * \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 * 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 print(Output &out) const;
void with_pager(auto const &fn)
{
if (_pager_thread) fn(*_pager_thread);
}
/****************** /******************
** Pure virtual ** ** 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; using namespace Kernel;
thread_pager(*_kobj, Capability_space::capid(pager.cap())); po.with_pager([&] (Platform_thread &pt) {
_pager = &pager; 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())); 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 restart();
void fault_resolved(Untyped_capability, bool);
/** /**
* Pause this thread * Pause this thread
*/ */