Deadlock fix in rm_session on NOVA platform

Rm_client is derived from Pager_object. If the Pager_object is also
derived from Thread_base (which is the case for NOVA) then the
Rm_client object must be destructed without holding the rm_session_object
lock. The native platform specific Thread_base implementation has to take
care that all in-flight page handling requests are finished before
destruction. On NOVA it is done by doing an IPC to the pager thread.
(performed in Pager_object::dissolve() in base-nova). The
called thread than executes its operation until end which also requires
in some cases to take the rm_session_object lock.

Since _client_slab insertion/deletion also must be performed
synchronized but can't be protected by the rm_session_object lock
because of the described dead_lock situation, we have
to use a synchronized allocator object to perform insertion and
deletion of Rm_clients.
This commit is contained in:
Alexander Boettcher 2012-07-03 14:45:52 +02:00 committed by Norman Feske
parent 8ee4442108
commit 4ece3b3c77
2 changed files with 28 additions and 9 deletions

View File

@ -22,6 +22,7 @@
#include <base/pager.h> #include <base/pager.h>
#include <base/allocator_avl.h> #include <base/allocator_avl.h>
#include <base/allocator_guard.h> #include <base/allocator_guard.h>
#include <base/sync_allocator.h>
#include <base/signal.h> #include <base/signal.h>
#include <base/rpc_server.h> #include <base/rpc_server.h>
#include <util/list.h> #include <util/list.h>
@ -258,8 +259,9 @@ namespace Genode {
}; };
Tslab<Rm_client, 1024> _client_slab; /* backing store for typedef Synchronized_allocator<Tslab<Rm_client, 1024> > Client_slab_alloc;
client structures */ Client_slab_alloc _client_slab; /* backing store for
client structures, synchronized */
Tslab<Rm_region_ref, 1024> _ref_slab; /* backing store for Tslab<Rm_region_ref, 1024> _ref_slab; /* backing store for
region list */ region list */
Allocator_avl_tpl<Rm_region> _map; /* region map for attach, Allocator_avl_tpl<Rm_region> _map; /* region map for attach,

View File

@ -683,9 +683,28 @@ Rm_session::State Rm_session_component::state()
void Rm_session_component::dissolve(Rm_client *cl) void Rm_session_component::dissolve(Rm_client *cl)
{ {
Lock::Guard lock_guard(_lock); {
_pager_ep->dissolve(cl); Lock::Guard lock_guard(_lock);
_clients.remove(cl); _pager_ep->dissolve(cl);
_clients.remove(cl);
}
/*
* Rm_client is derived from Pager_object. If the Pager_object is also
* derived from Thread_base then the Rm_client object must be
* destructed without holding the rm_session_object lock. The native
* platform specific Thread_base implementation has to take care that
* all in-flight page handling requests are finished before
* destruction. (Either by waiting until the end of or by
* <deadlock free> cancellation of the last in-flight request.
* This operation can also require taking the rm_session_object lock.
*
* Since _client_slab insertion/deletion also must be performed
* synchronized but can't be protected by the rm_session_object lock
* because of the described potential dead_lock situation, we have
* to use a synchronized allocator object to perform insertion and
* deletion of Rm_clients.
*/
destroy(&_client_slab, cl); destroy(&_client_slab, cl);
} }
@ -728,13 +747,11 @@ Rm_session_component::~Rm_session_component()
} }
/* remove all clients */ /* remove all clients */
while (Rm_client *cl = _client_slab.first_object()) { while (Rm_client *cl = _client_slab.raw()->first_object()) {
_pager_ep->dissolve(cl);
_lock.unlock(); _lock.unlock();
cl->dissolve_from_faulting_rm_session(); cl->dissolve_from_faulting_rm_session();
this->dissolve(cl);
_lock.lock(); _lock.lock();
_clients.remove(cl);
destroy(&_client_slab, cl);
} }
/* detach all regions */ /* detach all regions */