nova: fix cleanup handling in server object
As first step the rpc object must be freed up so that the kernel object (portal) vanishes. Then the object must be removed from the internal object pool list so that the object can't be obtained anymore. And then the cleanup call can be performed (_leave_server_object) since now all names to the rpc_object are gone. Doing it in different order (as before the commit) there is a very very little chance (but the bomb test triggers it occasionally) that the rpc_object can be obtained again by an incoming IPC - even it is already scheduled for removal. Fixes partly #549
This commit is contained in:
committed by
Norman Feske
parent
f50d816555
commit
28f7cf5dc2
@@ -38,7 +38,7 @@ Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj)
|
||||
Untyped_capability ec_cap, ep_cap;
|
||||
|
||||
/* _ec_sel is invalid until thread gets started */
|
||||
if (tid().ec_sel != ~0UL)
|
||||
if (tid().ec_sel != Native_thread::INVALID_INDEX)
|
||||
ec_cap = Native_capability(tid().ec_sel);
|
||||
else
|
||||
ec_cap = _thread_cap;
|
||||
@@ -56,8 +56,11 @@ Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj)
|
||||
|
||||
void Rpc_entrypoint::_dissolve(Rpc_object_base *obj)
|
||||
{
|
||||
/* Avoid any incoming IPC early, keep local cap solely */
|
||||
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), false);
|
||||
/* de-announce object from cap_session */
|
||||
_cap_session->free(obj->cap());
|
||||
|
||||
/* avoid any incoming IPC */
|
||||
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), true);
|
||||
|
||||
/* make sure nobody is able to find this object */
|
||||
remove_locked(obj);
|
||||
@@ -74,14 +77,9 @@ void Rpc_entrypoint::_dissolve(Rpc_object_base *obj)
|
||||
/* wait until nobody is inside dispatch */
|
||||
obj->acquire();
|
||||
|
||||
/* De-announce object from cap_session */
|
||||
_cap_session->free(obj->cap());
|
||||
|
||||
/* Revoke also local cap finally */
|
||||
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), true);
|
||||
|
||||
/* free cap selector */
|
||||
cap_selector_allocator()->free(obj->cap().local_name(), 0);
|
||||
/* XXX we need cap ref counting to avoid reuse bug which triggers */
|
||||
//cap_selector_allocator()->free(obj->cap().local_name(), 0);
|
||||
}
|
||||
|
||||
void Rpc_entrypoint::_activation_entry()
|
||||
@@ -158,24 +156,27 @@ void Rpc_entrypoint::entry()
|
||||
}
|
||||
|
||||
|
||||
void Rpc_entrypoint::_leave_server_object(Rpc_object_base *obj)
|
||||
void Rpc_entrypoint::_leave_server_object(Rpc_object_base *)
|
||||
{
|
||||
{
|
||||
Lock::Guard lock_guard(_curr_obj_lock);
|
||||
using namespace Nova;
|
||||
|
||||
if (obj == _curr_obj)
|
||||
cancel_blocking();
|
||||
}
|
||||
|
||||
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(
|
||||
Thread_base::myself()->utcb());
|
||||
Utcb *utcb = reinterpret_cast<Utcb *>(Thread_base::myself()->utcb());
|
||||
/* don't call ourself */
|
||||
if (utcb != reinterpret_cast<Nova::Utcb *>(&_context->utcb)) {
|
||||
utcb->msg[0] = 0xdead;
|
||||
utcb->set_msg_word(1);
|
||||
if (uint8_t res = Nova::call(obj->cap().local_name()))
|
||||
PERR("could not clean up entry point - %u", res);
|
||||
}
|
||||
if (utcb == reinterpret_cast<Utcb *>(this->utcb()))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Required outside of core. E.g. launchpad needs it to forcefully kill
|
||||
* a client which blocks on a session opening request where the service
|
||||
* is not up yet.
|
||||
*/
|
||||
cancel_blocking();
|
||||
|
||||
utcb->msg[0] = 0xdead;
|
||||
utcb->set_msg_word(1);
|
||||
if (uint8_t res = call(_cap.local_name()))
|
||||
PERR("%8p - could not clean up entry point of thread 0x%p - res %u",
|
||||
utcb, this->utcb(), res);
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +212,7 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
|
||||
* setup in the early bootstrap phase of core. In core the thread
|
||||
* is created 'manually'.
|
||||
*/
|
||||
if (_tid.ec_sel == ~0UL) {
|
||||
if (_tid.ec_sel == Native_thread::INVALID_INDEX) {
|
||||
/* create new pager object and assign it to the new thread */
|
||||
_pager_cap = env()->rm_session()->add_client(_thread_cap);
|
||||
if (!_pager_cap.valid())
|
||||
@@ -257,6 +258,13 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
|
||||
*/
|
||||
Thread_base::start();
|
||||
|
||||
/* create cleanup portal */
|
||||
_cap = _cap_session->alloc(Native_capability(_tid.ec_sel),
|
||||
(addr_t)_activation_entry);
|
||||
if (!_cap.valid())
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
|
||||
/* prepare portal receive window of new thread */
|
||||
_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)&_context->utcb);
|
||||
|
||||
if (start_on_construction)
|
||||
@@ -275,4 +283,13 @@ Rpc_entrypoint::~Rpc_entrypoint()
|
||||
while (Rpc_object_base *obj = Pool::first())
|
||||
_dissolve(obj);
|
||||
}
|
||||
|
||||
if (!_cap.valid())
|
||||
return;
|
||||
|
||||
/* de-announce object from cap_session */
|
||||
_cap_session->free(_cap);
|
||||
|
||||
Nova::revoke(Nova::Obj_crd(_cap.local_name(), 0), true);
|
||||
cap_selector_allocator()->free(_cap.local_name(), 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user