base: fix deadlock in core_env on base-nova

During a ram_session->free call in 'core' the lock in core_env.h is taken.
Then in the ram_session::_free_ds implementation the dissolve function for the
dataspace is called. base-nova tries to make sure that the ds is not
accessible anymore by any kind of parallel incoming IPC by performing a
cleanup IPC. Unfortunately the dataspace_session implementation uses the very
same allocator in 'core' and may require to obtain the same lock as taken in
ram_session->free. This leads to a spurious deadlock on base-nova.

The actual free_ds implementation is mostly thread safe, since all used objects
inside there are already locked. The only missing piece is the _payload
variable. By changing the _payload variable in a atomic fashion there is no
need to lock the whole ram_session->free call which avoids deadlocks on
base-nova.

Fixes #549
This commit is contained in:
Alexander Boettcher 2013-01-18 15:10:56 +01:00 committed by Norman Feske
parent edd30b56a2
commit 7868156b19
3 changed files with 11 additions and 9 deletions

View File

@ -77,7 +77,6 @@ namespace Genode {
void free(Ram_dataspace_capability ds)
{
Lock::Guard lock_guard(_lock);
RAM_SESSION_IMPL::free(ds);
}

View File

@ -19,6 +19,7 @@
#include <base/tslab.h>
#include <base/rpc_server.h>
#include <base/allocator_guard.h>
#include <base/sync_allocator.h>
/* core includes */
#include <dataspace_component.h>
@ -38,7 +39,7 @@ namespace Genode {
enum { SBS = 1024 }; /* slab block size */
typedef Tslab<Dataspace_component, SBS> Ds_slab;
typedef Synchronized_allocator<Tslab<Dataspace_component, SBS> > Ds_slab;
Rpc_entrypoint *_ds_ep;
Rpc_entrypoint *_ram_session_ep;

View File

@ -39,12 +39,12 @@ void Ram_session_component::_free_ds(Dataspace_component *ds)
size_t ds_size = ds->size();
/* destroy native shared memory representation */
_revoke_ram_ds(ds);
/* tell entry point to forget the dataspace */
_ds_ep->dissolve(ds);
/* destroy native shared memory representation */
_revoke_ram_ds(ds);
/* XXX: remove dataspace from all RM sessions */
/* free physical memory that was backing the dataspace */
@ -54,6 +54,7 @@ void Ram_session_component::_free_ds(Dataspace_component *ds)
destroy(&_ds_slab, ds);
/* adjust payload */
Lock::Guard lock_guard(_ref_members_lock);
_payload -= ds_size;
}
@ -181,9 +182,6 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, bool cache
*/
_clear_ds(ds);
/* keep track of the used quota for actual payload */
_payload += ds_size;
if (verbose)
PDBG("ds_size=%zd, used_quota=%zd quota_limit=%zd",
ds_size, used_quota(), _quota_limit);
@ -193,6 +191,10 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, bool cache
/* create native shared memory representation of dataspace */
_export_ram_ds(ds);
Lock::Guard lock_guard(_ref_members_lock);
/* keep track of the used quota for actual payload */
_payload += ds_size;
return static_cap_cast<Ram_dataspace>(result);
}
@ -258,7 +260,7 @@ Ram_session_component::Ram_session_component(Rpc_entrypoint *ds_ep,
Ram_session_component::~Ram_session_component()
{
/* destroy all dataspaces */
for (Dataspace_component *ds; (ds = _ds_slab.first_object()); _free_ds(ds));
for (Dataspace_component *ds; (ds = _ds_slab.raw()->first_object()); _free_ds(ds));
if (_payload != 0)
PWRN("Remaining payload of %zd in ram session to destroy", _payload);