mirror of
https://github.com/mmueller41/genode.git
synced 2026-01-21 12:32:56 +01: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:
committed by
Christian Helmuth
parent
84cabaf9b7
commit
3bdfb078bf
@@ -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_ */
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 ≻
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 **
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user