Fiasco.OC: several capability ref-counter fixes.

This commit fixes several issues that were triggered e.g. by the
'noux_tool_chain' run-script (fix #208 in part). The following problems
are tackled:
* Don't reference count capability selectors within a task that are actually
  controlled by core (all beneath 0x200000), because it's undecideable which
  "version" of a capability selector we currently use, e.g. a thread gets
  destroyed and a new one gets created immediately some other thread might
  have a Native_capability pointing to the already destroyed thread's gate
  capability-slot, that is now a new valid one (the one of the new thread)
* In core we cannot invalidate and remove a capability from the so called
  Cap_map before each reference to it is destroyed, so don't do this in
  Cap_session_component::free, but only reference-decrement within there,
  the actual removal can only be done in Cap_map::remove. Because core also
  has to invalidate a capability to be removed in all protection-domains
  we have to implement a core specific Cap_map::remove method
* When a capability gets inserted into the Cap_map, and we detect an old
  invalid entry with the dame id in the tree, don't just overmap that
  invalid entry (as there exist remaining references to it), but just remove
  it from the tree and allocate an new entry.
* Use the Cap_session_component interface to free a Pager_object when it
  gets dissolved, as its also used for allocation
This commit is contained in:
Stefan Kalkowski 2012-08-29 15:52:18 +02:00 committed by Norman Feske
parent b71c1649d6
commit a5ea6765d1
19 changed files with 148 additions and 139 deletions

View File

@ -58,7 +58,7 @@ namespace Genode {
** Cap_index_allocator interface **
***********************************/
Cap_index* alloc(size_t cnt)
Cap_index* alloc_range(size_t cnt)
{
Lock_guard<Spin_lock> guard(_lock);
@ -80,7 +80,7 @@ namespace Genode {
return 0;
}
Cap_index* alloc(addr_t addr, size_t cnt)
Cap_index* alloc(addr_t addr)
{
Lock_guard<Spin_lock> guard(_lock);
@ -89,17 +89,11 @@ namespace Genode {
* address in capability space
*/
T* obj = reinterpret_cast<T*>(kcap_to_idx(addr));
T* ret = obj;
/* check whether the consecutive entries are in range and unused */
for (size_t i = 0; i < cnt; i++, obj++) {
if (obj < &_indices[0] || obj >= &_indices[SZ])
throw Index_out_of_bounds();
if (obj->used())
throw Region_conflict();
new (obj) T();
}
return ret;
if (obj < &_indices[0] || obj >= &_indices[SZ])
throw Index_out_of_bounds();
return new (obj) T();
}
void free(Cap_index* idx, size_t cnt)
@ -121,6 +115,9 @@ namespace Genode {
Cap_index* kcap_to_idx(addr_t kcap) {
return &_indices[kcap >> Fiasco::L4_CAP_SHIFT]; }
bool static_idx(Cap_index *idx) {
return ((T*)idx) < &_indices[START_IDX]; }
};
}

View File

@ -103,20 +103,17 @@ namespace Genode
* \return pointer to first allocated object, or zero if
* out of entries
*/
virtual Cap_index* alloc(size_t cnt) = 0;
virtual Cap_index* alloc_range(size_t cnt) = 0;
/**
* Allocate a range of Cap_index objects at a specific
* Allocate a Cap_index object at a specific
* point in the capability space
*
* \param kcap address in capability space
* \param cnt number of objects to allocate
* \throw Index_out_of_bounds if address is out of scope
* \throw Region_conflict if capability space entry is used
* \return pointer to first allocated object,
* or zero if out of entries
* \return pointer to allocated object
*/
virtual Cap_index* alloc(addr_t kcap, size_t cnt) = 0;
virtual Cap_index* alloc(addr_t kcap) = 0;
/**
* Free a range of Cap_index objects
@ -141,6 +138,14 @@ namespace Genode
* \param kcap the address in the capability space
*/
virtual Cap_index* kcap_to_idx(addr_t kcap) = 0;
/**
* Returns whether a Cap_index object is from the region
* controlled by core, or not.
*
* \param idx pointer to the Cap_index object in question
*/
virtual bool static_idx(Cap_index *idx) = 0;
};
@ -226,9 +231,7 @@ namespace Genode
* Create and insert a new Cap_index with a specific capability id,
* and location in capability space
*
* Allocation of the Cap_index is done via the global
* Cap_index_allocator, which might throw exceptions that aren't
* caught by this method
* A previously existent entry with the same id gets removed!
*
* \param id the global capability id
* \param kcap address in capability space
@ -239,7 +242,10 @@ namespace Genode
/**
* Create and insert a new Cap_index with a specific capability id
* and map from given kcap to newly allocated one
* and map from given kcap to newly allocated one,
* if the an entry with the same id exists already,
* it is returned if it points to the same kernel-object,
* or gets overridden if it's already invalid.
*
* Allocation of the Cap_index is done via the global
* Cap_index_allocator, which might throw exceptions that aren't
@ -247,12 +253,14 @@ namespace Genode
*
* \param id the global capability id
* \return pointer to the new Cap_index object, or zero
* when allocation failed
* when allocation failed, or when a valid entry
* with the same id exists and it's kernel-object
* differs to the one given by kcap
*/
Cap_index* insert_map(int id, addr_t kcap);
/**
* Remove a Cap_index object
* Remove (resp. invalidate) a Cap_index object
*
* \param i pointer to Cap_index object to remove
*/

View File

@ -68,7 +68,7 @@ namespace Genode {
* Constructor
*/
Msgbuf_base()
: _rcv_idx_base(cap_idx_alloc()->alloc(MAX_CAP_ARGS)), _label(0)
: _rcv_idx_base(cap_idx_alloc()->alloc_range(MAX_CAP_ARGS)), _label(0)
{
rcv_reset();
snd_reset();

View File

@ -1,11 +1,12 @@
SRC_CC = env.cc context_area.cc cap_map.cc cap_alloc.cc \
reload_parent_cap.cc spin_lock.cc
reload_parent_cap.cc spin_lock.cc cap_map_remove.cc
LIBS = ipc heap log_console lock
INC_DIR += $(REP_DIR)/src/base/lock $(BASE_DIR)/src/base/lock
vpath env.cc $(BASE_DIR)/src/base/env
vpath context_area.cc $(BASE_DIR)/src/base/env
vpath cap_map.cc $(REP_DIR)/src/base/env
vpath cap_map_remove.cc $(REP_DIR)/src/base/env
vpath cap_alloc.cc $(REP_DIR)/src/base/env
vpath spin_lock.cc $(REP_DIR)/src/base/env
vpath reload_parent_cap.cc $(BASE_DIR)/src/base/env

View File

@ -54,6 +54,10 @@ Genode::addr_t Genode::Cap_index::kcap() {
Genode::uint8_t Genode::Cap_index::inc()
{
/* con't ref-count index that are controlled by core */
if (cap_idx_alloc()->static_idx(this))
return 1;
spinlock_lock(&_cap_index_spinlock);
Genode::uint8_t ret = ++_ref_cnt;
spinlock_unlock(&_cap_index_spinlock);
@ -63,6 +67,10 @@ Genode::uint8_t Genode::Cap_index::inc()
Genode::uint8_t Genode::Cap_index::dec()
{
/* con't ref-count index that are controlled by core */
if (cap_idx_alloc()->static_idx(this))
return 1;
spinlock_lock(&_cap_index_spinlock);
Genode::uint8_t ret = --_ref_cnt;
spinlock_unlock(&_cap_index_spinlock);
@ -76,14 +84,9 @@ Genode::uint8_t Genode::Cap_index::dec()
Genode::Cap_index* Genode::Capability_map::find(int id)
{
using namespace Genode;
Genode::Lock_guard<Spin_lock> guard(_lock);
Lock_guard<Spin_lock> guard(_lock);
Cap_index* i = 0;
if (_tree.first())
i = _tree.first()->find_by_id(id);
return i;
return _tree.first() ? _tree.first()->find_by_id(id) : 0;
}
@ -96,7 +99,7 @@ Genode::Cap_index* Genode::Capability_map::insert(int id)
ASSERT(!_tree.first() || !_tree.first()->find_by_id(id),
"Double insertion in cap_map()!");
Cap_index *i = cap_idx_alloc()->alloc(1);
Cap_index *i = cap_idx_alloc()->alloc_range(1);
if (i) {
i->id(id);
_tree.insert(i);
@ -111,10 +114,12 @@ Genode::Cap_index* Genode::Capability_map::insert(int id, addr_t kcap)
Lock_guard<Spin_lock> guard(_lock);
ASSERT(!_tree.first() || !_tree.first()->find_by_id(id),
"Double insertion in cap_map()!");
/* remove potentially existent entry */
Cap_index *i = _tree.first() ? _tree.first()->find_by_id(id) : 0;
if (i)
_tree.remove(i);
Cap_index *i = cap_idx_alloc()->alloc(kcap, 1);
i = cap_idx_alloc()->alloc(kcap);
if (i) {
i->id(id);
_tree.insert(i);
@ -130,11 +135,8 @@ Genode::Cap_index* Genode::Capability_map::insert_map(int id, addr_t kcap)
Lock_guard<Spin_lock> guard(_lock);
Cap_index* i = 0;
/* check whether capability id exists */
if (_tree.first())
i = _tree.first()->find_by_id(id);
Cap_index *i = _tree.first() ? _tree.first()->find_by_id(id) : 0;
/* if we own the capability already check whether it's the same */
if (i) {
@ -147,18 +149,23 @@ Genode::Cap_index* Genode::Capability_map::insert_map(int id, addr_t kcap)
tag = l4_task_cap_valid(L4_BASE_TASK_CAP, i->kcap());
if (l4_msgtag_label(tag))
return 0;
else
/* it's invalid so remove it from the tree */
_tree.remove(i);
} else
/* they are equal so just return the one in the map */
return i;
} else {
/* the capability doesn't exists in the map so allocate a new one */
i = cap_idx_alloc()->alloc(1);
if (!i)
return 0;
i->id(id);
_tree.insert(i);
}
/* the capability doesn't exists in the map so allocate a new one */
i = cap_idx_alloc()->alloc_range(1);
if (!i)
return 0;
/* set it's id and insert it into the tree */
i->id(id);
_tree.insert(i);
/* map the given cap to our registry entry */
l4_task_map(L4_BASE_TASK_CAP, L4_BASE_TASK_CAP,
l4_obj_fpage(kcap, 0, L4_FPAGE_RWX),
@ -167,21 +174,6 @@ Genode::Cap_index* Genode::Capability_map::insert_map(int id, addr_t kcap)
}
void Genode::Capability_map::remove(Genode::Cap_index* i)
{
using namespace Genode;
Lock_guard<Spin_lock> guard(_lock);
if (i) {
Cap_index* e = _tree.first() ? _tree.first()->find_by_id(i->id()) : 0;
if (e == i)
_tree.remove(i);
cap_idx_alloc()->free(i, 1);
}
}
Genode::Capability_map* Genode::cap_map()
{
static Genode::Capability_map map;

30
base-foc/src/base/env/cap_map_remove.cc vendored Normal file
View File

@ -0,0 +1,30 @@
/*
* \brief Mapping of Genode's capability names to kernel capabilities.
* \author Stefan Kalkowski
* \date 2010-12-06
*
* This is a Fiasco.OC-specific addition to the process enviroment.
*/
/*
* Copyright (C) 2010-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <base/cap_map.h>
void Genode::Capability_map::remove(Genode::Cap_index* i)
{
using namespace Genode;
Lock_guard<Spin_lock> guard(_lock);
if (i) {
Cap_index* e = _tree.first() ? _tree.first()->find_by_id(i->id()) : 0;
if (e == i)
_tree.remove(i);
cap_idx_alloc()->free(i, 1);
}
}

View File

@ -192,7 +192,7 @@ void Ipc_istream::_wait()
Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg)
:
Ipc_unmarshaller(&rcv_msg->buf[0], rcv_msg->size()),
Native_capability(cap_map()->find(Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE])),
Native_capability((Cap_index*)Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE]),
_rcv_msg(rcv_msg)
{
_read_offset = sizeof(l4_mword_t);

View File

@ -113,5 +113,6 @@ void Ipc_pager::acknowledge_wakeup()
Ipc_pager::Ipc_pager()
: Native_capability(cap_map()->find(Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE])), _badge(0) { }
: Native_capability((Cap_index*)Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE]),
_badge(0) { }

View File

@ -160,7 +160,13 @@ Pager_entrypoint::Pager_entrypoint(Cap_session *cap_session,
}
void Pager_entrypoint::dissolve(Pager_object *obj) { remove(obj); }
void Pager_entrypoint::dissolve(Pager_object *obj)
{
/* cleanup at cap session */
_cap_session->free(obj->Object_pool<Pager_object>::Entry::cap());
remove(obj);
}
Pager_capability Pager_entrypoint::manage(Pager_object *obj)

View File

@ -29,9 +29,9 @@ void Thread_base::_deinit_platform_thread()
{
using namespace Fiasco;
int id = l4_utcb_tcr_u(_context->utcb)->user[UTCB_TCR_BADGE];
Cap_index *i = (Cap_index*)l4_utcb_tcr_u(_context->utcb)->user[UTCB_TCR_BADGE];
cap_map()->remove(i);
env()->cpu_session()->kill_thread(_thread_cap);
cap_map()->remove(cap_map()->find(id));
}
@ -57,18 +57,9 @@ void Thread_base::start()
_tid = state.kcap;
_context->utcb = state.utcb;
try {
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_BADGE] = state.id;
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_THREAD_OBJ] = (addr_t)this;
/* there might be leaks in the application */
cap_map()->remove(cap_map()->find(state.id));
/* we need to manually increase the reference counter here */
cap_map()->insert(state.id, state.kcap)->inc();
} catch(Cap_index_allocator::Region_conflict) {
PERR("could not insert id %x", state.id);
}
Cap_index *i = cap_map()->insert(state.id, state.kcap);
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_BADGE] = (unsigned long) i;
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_THREAD_OBJ] = (addr_t)this;
/* register initial IP and SP at core */
addr_t thread_sp = (addr_t)&_context->stack[-4];

View File

@ -71,21 +71,6 @@ void Cap_mapping::map(Native_thread_id task)
}
void Cap_mapping::unmap()
{
using namespace Fiasco;
if (!local)
return;
l4_msgtag_t tag = l4_task_unmap(L4_BASE_TASK_CAP,
l4_obj_fpage(local->kcap(), 0, L4_FPAGE_RWX),
L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ);
if (l4_msgtag_has_error(tag))
PERR("unmapping cap failed");
}
Cap_mapping::Cap_mapping(bool alloc, Native_thread_id r)
: local(alloc ? _get_cap() : 0), remote(r)
{
@ -105,7 +90,6 @@ Cap_mapping::Cap_mapping(Core_cap_index* i, Native_thread_id r)
Cap_mapping::~Cap_mapping()
{
if (local) {
unmap();
cap_map()->remove(local);
}
}
@ -164,6 +148,7 @@ Native_capability Cap_session_component::alloc(Cap_session_component *session,
} catch (Cap_id_allocator::Out_of_ids) {
PERR("Out of IDs");
}
return cap;
}
@ -189,15 +174,7 @@ void Cap_session_component::free(Native_capability cap)
if (idx->session() != this)
return;
l4_msgtag_t tag = l4_task_unmap(L4_BASE_TASK_CAP,
l4_obj_fpage(idx->kcap(), 0, L4_FPAGE_RWX),
L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ);
if (l4_msgtag_has_error(tag))
PERR("destruction of ipc-gate %lx failed!", (unsigned long) idx->kcap());
unsigned long id = idx->id();
cap_map()->remove(idx);
platform_specific()->cap_id_alloc()->free(id);
idx->dec();
}
@ -232,6 +209,32 @@ void Cap_id_allocator::free(unsigned long id)
}
void Genode::Capability_map::remove(Genode::Cap_index* i)
{
using namespace Genode;
using namespace Fiasco;
Lock_guard<Spin_lock> guard(_lock);
if (i) {
Core_cap_index* e = static_cast<Core_cap_index*>(_tree.first() ? _tree.first()->find_by_id(i->id()) : 0);
if (e == i) {
l4_msgtag_t tag = l4_task_unmap(L4_BASE_TASK_CAP,
l4_obj_fpage(i->kcap(), 0, L4_FPAGE_RWX),
L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ);
if (l4_msgtag_has_error(tag))
PERR("destruction of ipc-gate %lx failed!", (unsigned long) i->kcap());
platform_specific()->cap_id_alloc()->free(i->id());
_tree.remove(i);
}
cap_idx_alloc()->free(i, 1);
}
}
Genode::Cap_index_allocator* cap_idx_alloc()
{
static Genode::Cap_index_allocator_tpl<Core_cap_index, 20*1024> _alloc;

View File

@ -55,11 +55,6 @@ namespace Genode {
* \param task capability of task to map to
*/
void map(Native_task task);
/**
* Unmap all child mappings
*/
void unmap();
};
}

View File

@ -50,7 +50,7 @@ void Thread_base::start()
pt->pager(platform_specific()->core_pager());
_context->utcb = pt->utcb();
l4_utcb_tcr_u(pt->utcb())->user[UTCB_TCR_BADGE] = pt->gate().local->id();
l4_utcb_tcr_u(pt->utcb())->user[UTCB_TCR_BADGE] = (unsigned long) pt->gate().local;
l4_utcb_tcr_u(pt->utcb())->user[UTCB_TCR_THREAD_OBJ] = (addr_t)this;
pt->start((void *)_thread_start, _context->stack);

View File

@ -28,19 +28,10 @@ enum { MAIN_THREAD_CAP_ID = 1 };
static void main_thread_bootstrap() {
using namespace Genode;
/**
* Unfortunately ldso calls this function twice. So the second time when
* inserting the main thread's gate-capability an exception would be raised.
* At least on ARM we got problems when raising an exception that early,
* that's why we first check if the cap is already registered before
* inserting it.
*/
Cap_index *idx = cap_map()->find(MAIN_THREAD_CAP_ID);
if (!idx) {
Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE] = MAIN_THREAD_CAP_ID;
Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_THREAD_OBJ] = 0;
cap_map()->insert(MAIN_THREAD_CAP_ID, Fiasco::MAIN_THREAD_CAP)->inc();
}
Cap_index *i
= cap_map()->insert(MAIN_THREAD_CAP_ID, Fiasco::MAIN_THREAD_CAP);
Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_BADGE] = (unsigned long) i;
Fiasco::l4_utcb_tcr()->user[Fiasco::UTCB_TCR_THREAD_OBJ] = 0;
}

View File

@ -25,7 +25,7 @@ int main(int argc, char **argv)
enum { COUNT = 1000 };
Cap_index* idx = cap_idx_alloc()->alloc(COUNT);
Cap_index* idx = cap_idx_alloc()->alloc_range(COUNT);
Native_thread_id tid = env()->ram_session_cap().dst();
/* try the first 1000 local name IDs */

View File

@ -50,7 +50,7 @@ namespace L4lx {
Genode::size_t size,
Genode::Dataspace_capability ds)
: _name(name), _size(size), _cap(ds),
_ref(Genode::cap_idx_alloc()->alloc(1)->kcap()) {}
_ref(Genode::cap_idx_alloc()->alloc_range(1)->kcap()) {}
/***************

View File

@ -88,16 +88,10 @@ namespace L4lx {
_tid = state.kcap;
_context->utcb = state.utcb;
try {
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_BADGE] = state.id;
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_THREAD_OBJ] = (addr_t)this;
l4_utcb_tcr_u(state.utcb)->user[0] = state.kcap; /* L4X_UTCB_TCR_ID */
/* we need to manually increase the reference counter here */
cap_map()->insert(state.id, state.kcap)->inc();
} catch(Cap_index_allocator::Region_conflict) {
PERR("could not insert id %x", state.id);
}
Cap_index *i = cap_map()->insert(state.id, state.kcap);
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_BADGE] = (unsigned long) i;
l4_utcb_tcr_u(state.utcb)->user[UTCB_TCR_THREAD_OBJ] = (addr_t)this;
l4_utcb_tcr_u(state.utcb)->user[0] = state.kcap; /* L4X_UTCB_TCR_ID */
/* register initial IP and SP at core */
addr_t stack = (addr_t)&_context->stack[-4];

View File

@ -28,7 +28,7 @@ extern "C" {
l4_cap_idx_t l4re_util_cap_alloc(void)
{
l4_cap_idx_t ret = Genode::cap_idx_alloc()->alloc(1)->kcap();
l4_cap_idx_t ret = Genode::cap_idx_alloc()->alloc_range(1)->kcap();
if (DEBUG)
PDBG("ret=%lx", ret);

View File

@ -85,7 +85,7 @@ int l4lx_task_number_free(l4_cap_idx_t task)
int l4lx_task_get_new_task(l4_cap_idx_t parent_id,
l4_cap_idx_t *id)
{
*id = Genode::cap_idx_alloc()->alloc(1)->kcap();
*id = Genode::cap_idx_alloc()->alloc_range(1)->kcap();
return 0;
}