mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
nova: fix thread in page fault and getting paused
If a thread caused a page fault and later on get be paused, then it left the recall handler immediately due to the pause call instead of staying in this handler. Add some (complicated) state machine to detect and handle the case. Still not waterproof, especially server threads may never get recalled if they never get a IPC from the outside. Fixes #478
This commit is contained in:
parent
2c983f3c89
commit
816b4e0479
@ -57,9 +57,27 @@ namespace Genode {
|
||||
{
|
||||
struct Thread_state thread;
|
||||
addr_t sel_client_ec;
|
||||
bool valid;
|
||||
bool dead;
|
||||
bool singlestep;
|
||||
enum {
|
||||
VALID = 0x1U,
|
||||
DEAD = 0x2U,
|
||||
SINGLESTEP = 0x4U,
|
||||
CLIENT_CANCEL = 0x8U,
|
||||
};
|
||||
uint8_t _status;
|
||||
|
||||
/* convenience function to access pause/recall state */
|
||||
inline bool is_valid() { return _status & VALID; }
|
||||
inline void mark_valid() { _status |= VALID; }
|
||||
inline void mark_invalid() { _status &= ~VALID; }
|
||||
|
||||
inline bool is_client_cancel() { return _status & CLIENT_CANCEL; }
|
||||
inline void mark_client_cancel() { _status |= CLIENT_CANCEL; }
|
||||
inline void unmark_client_cancel() { _status &= ~CLIENT_CANCEL; }
|
||||
|
||||
inline void mark_dead() { _status |= DEAD; }
|
||||
inline bool is_dead() { return _status & DEAD; }
|
||||
|
||||
inline bool singlestep() { return _status & SINGLESTEP; }
|
||||
} _state;
|
||||
|
||||
Thread_capability _thread_cap;
|
||||
@ -152,10 +170,8 @@ namespace Genode {
|
||||
*/
|
||||
Native_capability notify_sm()
|
||||
{
|
||||
if (_state.valid)
|
||||
return Native_capability::invalid_cap();
|
||||
if (_state.dead)
|
||||
return Native_capability::invalid_cap();
|
||||
if (_state.is_valid() || _state.is_dead())
|
||||
return Native_capability();
|
||||
|
||||
return Native_capability(sm_state_notify());
|
||||
}
|
||||
@ -165,7 +181,8 @@ namespace Genode {
|
||||
*/
|
||||
int copy_thread_state(Thread_state * state_dst)
|
||||
{
|
||||
if (!state_dst || !_state.valid) return -1;
|
||||
if (!state_dst || !_state.is_valid())
|
||||
return -1;
|
||||
|
||||
*state_dst = _state.thread;
|
||||
|
||||
@ -181,7 +198,13 @@ namespace Genode {
|
||||
uint8_t client_recall();
|
||||
void client_set_ec(addr_t ec) { _state.sel_client_ec = ec; }
|
||||
|
||||
void single_step(bool on) { _state.singlestep = on; }
|
||||
inline void single_step(bool on)
|
||||
{
|
||||
if (on)
|
||||
_state._status |= _state.SINGLESTEP;
|
||||
else
|
||||
_state._status &= ~_state.SINGLESTEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember thread cap so that rm_session can tell thread that
|
||||
|
@ -46,7 +46,7 @@ Utcb * Pager_object::_check_handler(Thread_base *&myself, Pager_object *&obj)
|
||||
PERR("unexpected exception-fault for non-existing pager object,"
|
||||
" going to sleep forever");
|
||||
|
||||
if (obj) obj->_state.dead = true;
|
||||
if (obj) obj->_state.mark_dead();
|
||||
sleep_forever();
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ void Pager_object::_page_fault_handler()
|
||||
|
||||
revoke(Obj_crd(obj->exc_pt_sel_client(), NUM_INITIAL_PT_LOG2), false);
|
||||
|
||||
obj->_state.dead = true;
|
||||
obj->_state.mark_dead();
|
||||
}
|
||||
|
||||
if (ret == 1) {
|
||||
@ -117,7 +117,7 @@ void Pager_object::_exception_handler(addr_t portal_id)
|
||||
"'%s'", fault_ip, portal_id, client_name);
|
||||
|
||||
Nova::revoke(Obj_crd(portal_id, 0));
|
||||
obj->_state.dead = true;
|
||||
obj->_state.mark_dead();
|
||||
}
|
||||
|
||||
utcb->set_msg_word(0);
|
||||
@ -141,26 +141,30 @@ void Pager_object::_recall_handler()
|
||||
obj->_state.thread.eflags = utcb->flags;
|
||||
obj->_state.thread.trapno = PT_SEL_RECALL;
|
||||
|
||||
obj->_state.valid = true;
|
||||
obj->_state.mark_valid();
|
||||
|
||||
if (sm_ctrl(obj->sm_state_notify(), SEMAPHORE_UP) != NOVA_OK)
|
||||
PWRN("notify failed");
|
||||
if (obj->_state.is_client_cancel())
|
||||
if (sm_ctrl(obj->sm_state_notify(), SEMAPHORE_UP) != NOVA_OK)
|
||||
PWRN("notify failed");
|
||||
|
||||
if (sm_ctrl(obj->exc_pt_sel() + SM_SEL_EC, SEMAPHORE_DOWNZERO) != NOVA_OK)
|
||||
PWRN("blocking recall handler failed");
|
||||
do {
|
||||
if (sm_ctrl(obj->exc_pt_sel() + SM_SEL_EC, SEMAPHORE_DOWNZERO) != NOVA_OK)
|
||||
PWRN("blocking recall handler failed");
|
||||
} while (obj->_state.is_client_cancel());
|
||||
|
||||
obj->_state.valid = false;
|
||||
obj->_state.mark_invalid();
|
||||
|
||||
bool singlestep_state = obj->_state.thread.eflags & 0x100UL;
|
||||
if (obj->_state.singlestep && !singlestep_state) {
|
||||
if (obj->_state.singlestep() && !singlestep_state) {
|
||||
utcb->flags = obj->_state.thread.eflags | 0x100UL;
|
||||
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
|
||||
} else
|
||||
if (!obj->_state.singlestep && singlestep_state) {
|
||||
if (!obj->_state.singlestep() && singlestep_state) {
|
||||
utcb->flags = obj->_state.thread.eflags & ~0x100UL;
|
||||
utcb->mtd = Nova::Mtd(Mtd::EFL).value();
|
||||
} else
|
||||
utcb->mtd = 0;
|
||||
|
||||
utcb->set_msg_word(0);
|
||||
|
||||
reply(myself->stack_top());
|
||||
@ -229,11 +233,21 @@ void Pager_object::_invoke_handler()
|
||||
}
|
||||
|
||||
|
||||
void Pager_object::wake_up() { cancel_blocking(); }
|
||||
void Pager_object::wake_up()
|
||||
{
|
||||
_state.unmark_client_cancel();
|
||||
|
||||
cancel_blocking();
|
||||
}
|
||||
|
||||
|
||||
void Pager_object::client_cancel_blocking()
|
||||
{
|
||||
if (_state.is_client_cancel())
|
||||
return;
|
||||
|
||||
_state.mark_client_cancel();
|
||||
|
||||
uint8_t res = sm_ctrl(exc_pt_sel_client() + SM_SEL_EC, SEMAPHORE_UP);
|
||||
if (res != NOVA_OK)
|
||||
PWRN("cancel blocking failed");
|
||||
@ -288,9 +302,7 @@ Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
|
||||
addr_t pd_sel = __core_pd_sel;
|
||||
_pt_cleanup = cap_selector_allocator()->alloc(1);
|
||||
_client_exc_pt_sel = cap_selector_allocator()->alloc(NUM_INITIAL_PT_LOG2);
|
||||
_state.valid = false;
|
||||
_state.dead = false;
|
||||
_state.singlestep = false;
|
||||
_state._status = 0;
|
||||
_state.sel_client_ec = Native_thread::INVALID_INDEX;
|
||||
|
||||
if (_pt_cleanup == Native_thread::INVALID_INDEX ||
|
||||
|
Loading…
Reference in New Issue
Block a user