From 816b4e04792cd6b56e71a841f586ea6d3cc908df Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Thu, 26 Sep 2013 15:16:01 +0200 Subject: [PATCH] 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 --- base-nova/include/base/pager.h | 41 +++++++++++++++++++++++------- base-nova/src/base/pager/pager.cc | 42 ++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/base-nova/include/base/pager.h b/base-nova/include/base/pager.h index e3bca4b7c4..ddd58f2c4b 100644 --- a/base-nova/include/base/pager.h +++ b/base-nova/include/base/pager.h @@ -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 diff --git a/base-nova/src/base/pager/pager.cc b/base-nova/src/base/pager/pager.cc index d45ab7073d..ee45556aac 100644 --- a/base-nova/src/base/pager/pager.cc +++ b/base-nova/src/base/pager/pager.cc @@ -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 ||