mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-19 08:36:49 +00:00
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:
parent
1194652d20
commit
2728853005
repos/base-hw/src/core
@ -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_ */
|
||||
|
@ -385,6 +385,7 @@ void Thread::_call_restart_thread()
|
||||
_die();
|
||||
return;
|
||||
}
|
||||
|
||||
user_arg_0(thread._restart());
|
||||
}
|
||||
|
||||
@ -392,7 +393,10 @@ void Thread::_call_restart_thread()
|
||||
bool Thread::_restart()
|
||||
{
|
||||
assert(_state == ACTIVE || _state == AWAITS_RESTART);
|
||||
if (_state != AWAITS_RESTART) { return false; }
|
||||
|
||||
if (_state == ACTIVE && _exception_state == NO_EXCEPTION)
|
||||
return false;
|
||||
|
||||
_exception_state = NO_EXCEPTION;
|
||||
_become_active();
|
||||
return true;
|
||||
@ -572,7 +576,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 +830,25 @@ 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() ||
|
||||
thread._exception_state == NO_EXCEPTION;
|
||||
if (resolved) thread._restart();
|
||||
else thread._become_inactive(AWAITS_RESTART);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Thread::_call()
|
||||
{
|
||||
/* switch over unrestricted kernel calls */
|
||||
@ -906,6 +931,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 +940,37 @@ void Thread::_call()
|
||||
}
|
||||
|
||||
|
||||
void Thread::_signal_to_pager()
|
||||
{
|
||||
if (!_fault_context.constructed()) {
|
||||
Genode::warning(*this, " could not send signal to pager");
|
||||
_die();
|
||||
return;
|
||||
}
|
||||
|
||||
/* first signal to pager to wake it up */
|
||||
_fault_context->sc.submit(1);
|
||||
|
||||
/* only help pager thread if runnable and scheduler allows it */
|
||||
bool const help = Cpu_context::_helping_possible(_fault_context->pager)
|
||||
&& (_fault_context->pager._state == ACTIVE);
|
||||
if (help) Cpu_context::_help(_fault_context->pager);
|
||||
else _become_inactive(AWAITS_RESTART);
|
||||
}
|
||||
|
||||
|
||||
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 +985,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 +1002,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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 ≻
|
||||
};
|
||||
|
||||
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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 **
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -216,6 +216,8 @@ class Core::Platform_thread : Noncopyable
|
||||
|
||||
void restart();
|
||||
|
||||
void fault_resolved(Untyped_capability, bool);
|
||||
|
||||
/**
|
||||
* Pause this thread
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user