Fiasco.OC: Re-use existing capability selectors

This is an interim fix for issue #112. This patch extends the
'Capability_allocator' class with the ability to register the global
ID of a Genode capability so that the ID gets associated with a
process-local kernel capability. Whenever a Genode capability gets
unmarshalled from an IPC message, the capability-allocator is asked,
with the global ID as key, whether the kernel-cap already exists.
This significantly reduces the waste of kernel-capability slots.

To circumvent problems of having one and the same ID for different kernel
objects, the following problems had to be solved:
* Replace pseudo IDs with unique ones from core's badge allocator
* When freeing a session object, free the global ID _after_ unmapping
  the kernel object, otherwise the global ID might get re-used in some
  process and the registry will find a valid but wrong capability
  for the ID

Because core aggregates all capabilities of all different processes, its
capability registry needs much more memory compared to a regular process.
By parametrizing capability allocators differently for core and non-core
processes, the global memory overhead for capability registries is kept
at a reasonable level.
This commit is contained in:
Stefan Kalkowski 2012-02-27 17:22:42 +01:00 committed by Norman Feske
parent c29b5f7da3
commit 41eaff2cc6
18 changed files with 227 additions and 133 deletions

View File

@ -22,28 +22,30 @@
#ifndef _INCLUDE__BASE__CAP_SEL_ALLOC_H_
#define _INCLUDE__BASE__CAP_SEL_ALLOC_H_
/* Genode includes */
#include <base/stdint.h>
#include <util/avl_tree.h>
#include <base/native_types.h>
#include <base/printf.h>
#include <cpu/atomic.h>
/* Fiasco.OC includes */
namespace Fiasco {
#include <l4/sys/ipc.h>
#include <l4/sys/consts.h>
}
namespace Genode
{
class Capability_allocator
{
private:
protected:
addr_t _cap_idx;
/**
* Constructor
*/
Capability_allocator();
Capability_allocator() {}
virtual ~Capability_allocator() {}
public:
/**
* Return singleton instance of 'Capability_allocator'
*/
static Capability_allocator* allocator();
/**
* Allocate range of capability selectors
*
@ -52,7 +54,18 @@ namespace Genode
* \return first capability selector of allocated range,
* or 0 if allocation failed
*/
addr_t alloc(size_t num_caps = 1);
virtual addr_t alloc(size_t num_caps = 1) = 0;
/**
* Allocate or find a capability selector
*
* \param id Genode's global capability id we're looking for
* \return return a previously allocated cap-selector associated
* with the given id, or a new one, that is associated
* with the id from now on.
*/
virtual addr_t alloc_id(unsigned id) = 0;
/**
* Release range of capability selectors
@ -60,7 +73,152 @@ namespace Genode
* \param cap first capability selector of range
* \param num_caps_log2 number of capability selectors to free.
*/
void free(addr_t cap, size_t num_caps = 1);
virtual void free(addr_t cap, size_t num_caps = 1) = 0;
};
Capability_allocator* cap_alloc();
template <unsigned SZ>
class Capability_allocator_tpl : public Capability_allocator
{
private:
/**
* Low-level lock to protect the allocator
*
* We cannot use a normal Genode lock because this lock is used by code
* executed prior the initialization of Genode.
*/
class Alloc_lock
{
private:
int _state;
public:
enum State { LOCKED, UNLOCKED };
/**
* Constructor
*/
Alloc_lock() : _state(UNLOCKED) {}
void lock()
{
while (!Genode::cmpxchg(&_state, UNLOCKED, LOCKED))
Fiasco::l4_ipc_sleep(Fiasco::l4_ipc_timeout(0, 0, 500, 0));
}
void unlock() { _state = UNLOCKED; }
};
/**
* Node in the capability cache,
* associates global cap ids with kernel-capabilities.
*/
class Cap_node : public Avl_node<Cap_node>
{
private:
friend class Capability_allocator_tpl<SZ>;
unsigned long _id;
addr_t _kcap;
public:
Cap_node() : _id(0), _kcap(0) {}
Cap_node(unsigned long id, addr_t kcap)
: _id(id), _kcap(kcap) {}
bool higher(Cap_node *n) {
return n->_id > _id; }
Cap_node *find_by_id(unsigned long id)
{
if (_id == id) return this;
Cap_node *n = Avl_node<Cap_node>::child(id > _id);
return n ? n->find_by_id(id) : 0;
}
unsigned long id() const { return _id; }
addr_t kcap() const { return _kcap; }
bool valid() const { return _id || _kcap; }
};
addr_t _cap_idx; /* start cap-selector */
Cap_node _data[SZ]; /* cache-nodes backing store */
Avl_tree<Cap_node> _tree; /* cap cache */
Alloc_lock _lock;
public:
/**
* Constructor
*/
Capability_allocator_tpl()
: _cap_idx(Fiasco::Fiasco_capability::USER_BASE_CAP) { }
/************************************
** Capability_allocator interface **
************************************/
addr_t alloc(size_t num_caps)
{
_lock.lock();
int ret_base = _cap_idx;
_cap_idx += num_caps * Fiasco::L4_CAP_SIZE;
_lock.unlock();
return ret_base;
}
addr_t alloc_id(unsigned id)
{
_lock.lock();
Cap_node *n = _tree.first();
if (n)
n = n->find_by_id(id);
_lock.unlock();
if (n) {
return n->kcap();
}
addr_t kcap = alloc(1);
_lock.lock();
for (unsigned i = 0; i < SZ; i++)
if (!_data[i].valid()) {
_data[i]._id = id;
_data[i]._kcap = kcap;
_tree.insert(&_data[i]);
break;
}
_lock.unlock();
return kcap;
}
void free(addr_t cap, size_t num_caps)
{
_lock.lock();
for (unsigned i = 0; i < SZ; i++)
if (!_data[i]._kcap == cap) {
_tree.remove(&_data[i]);
_data[i]._kcap = 0;
_data[i]._id = 0;
break;
}
_lock.unlock();
}
};
}

View File

@ -19,7 +19,6 @@
namespace Fiasco {
#include <l4/sys/consts.h>
#include <l4/sys/kdebug.h>
#include <l4/sys/task.h>
}
@ -46,10 +45,13 @@ inline void Genode::Ipc_istream::_unmarshal_capability(Genode::Native_capability
}
/* allocate new cap slot and grant cap to it out of receive window */
Genode::addr_t cap_sel = Capability_allocator::allocator()->alloc();
l4_task_map(L4_BASE_TASK_CAP, L4_BASE_TASK_CAP,
l4_obj_fpage(_rcv_msg->rcv_cap_sel(), 0, L4_FPAGE_RWX),
cap_sel | L4_ITEM_MAP | L4_MAP_ITEM_GRANT);
Genode::addr_t cap_sel = cap_alloc()->alloc_id(unique_id);
l4_msgtag_t tag = l4_task_cap_valid(L4_BASE_TASK_CAP, cap_sel);
if (!tag.label()) {
l4_task_map(L4_BASE_TASK_CAP, L4_BASE_TASK_CAP,
l4_obj_fpage(_rcv_msg->rcv_cap_sel(), 0, L4_FPAGE_RWX),
cap_sel | L4_ITEM_MAP | L4_MAP_ITEM_GRANT);
}
cap = Native_capability(cap_sel, unique_id);
}

View File

@ -66,7 +66,7 @@ namespace Genode {
* Constructor
*/
Msgbuf_base()
: _rcv_cap_sel_base(Capability_allocator::allocator()->alloc(MAX_CAP_ARGS))
: _rcv_cap_sel_base(cap_alloc()->alloc(MAX_CAP_ARGS))
{
rcv_reset();
snd_reset();

View File

@ -1,5 +1,5 @@
/*
* \brief Capability-selector allocator
* \brief Capability-selector allocator for non-core tasks.
* \author Stefan Kalkowski
* \date 2010-12-06
*
@ -15,93 +15,10 @@
/* Genode includes */
#include <base/cap_sel_alloc.h>
#include <base/native_types.h>
#include <base/printf.h>
#include <cpu/atomic.h>
/* Fiasco.OC includes */
namespace Fiasco {
#include <l4/sys/ipc.h>
#include <l4/sys/consts.h>
}
using namespace Genode;
/**
* First available capability selector for custom use
*
* Must be initialized by the startup code
*/
static unsigned long __first_free_cap_selector =
Fiasco::Fiasco_capability::USER_BASE_CAP;
/**
* Low-level lock to protect the allocator
*
* We cannot use a normal Genode lock because this lock is used by code
* executed prior the initialization of Genode.
*/
class Alloc_lock
Genode::Capability_allocator* Genode::cap_alloc()
{
private:
int _state;
public:
enum State { LOCKED, UNLOCKED };
/**
* Constructor
*/
Alloc_lock() : _state(UNLOCKED) {}
void lock()
{
while (!Genode::cmpxchg(&_state, UNLOCKED, LOCKED))
Fiasco::l4_ipc_sleep(Fiasco::l4_ipc_timeout(0, 0, 500, 0));
}
void unlock() { _state = UNLOCKED; }
};
/**
* Return lock used to protect capability selector allocations
*/
static Alloc_lock *alloc_lock()
{
static Alloc_lock alloc_lock_inst;
return &alloc_lock_inst;
}
addr_t Capability_allocator::alloc(size_t num_caps)
{
alloc_lock()->lock();
int ret_base = _cap_idx;
_cap_idx += num_caps * Fiasco::L4_CAP_SIZE;
alloc_lock()->unlock();
return ret_base;
}
void Capability_allocator::free(addr_t cap, size_t num_caps_log2)
{
// PWRN("Not yet implemented!");
}
Capability_allocator::Capability_allocator()
: _cap_idx(__first_free_cap_selector)
{
/* initialize lock */
alloc_lock();
}
Capability_allocator* Capability_allocator::allocator()
{
static Capability_allocator inst;
return &inst;
static Genode::Capability_allocator_tpl<8192> _alloc;
return &_alloc;
}

View File

@ -63,14 +63,14 @@ Native_capability Cap_session_component::alloc(Cap_session_component *session,
* Allocate new badge, and ipc-gate and set badge as gate-label
*/
unsigned long badge = Badge_allocator::allocator()->alloc();
Native_thread gate = Capability_allocator::allocator()->alloc();
Native_thread gate = cap_alloc()->alloc_id(badge);
l4_msgtag_t tag = l4_factory_create_gate(L4_BASE_FACTORY_CAP,
gate,
n->pt()->native_thread(),
badge);
if (l4_msgtag_has_error(tag)) {
PERR("l4_factory_create_gate failed!");
Capability_allocator::allocator()->free(gate);
cap_alloc()->free(gate);
Badge_allocator::allocator()->free(badge);
return cap;
} else
@ -117,15 +117,17 @@ void Cap_session_component::free(Native_capability cap)
return;
Capability_tree::tree()->remove(n);
Badge_allocator::allocator()->free(n->badge());
l4_msgtag_t tag = l4_task_unmap(L4_BASE_TASK_CAP,
l4_obj_fpage(cap.dst(), 0, 0),
L4_FP_ALL_SPACES | L4_FP_DELETE_OBJ);
if (l4_msgtag_has_error(tag))
PERR("destruction of ipc-gate %lx failed!", (unsigned long) cap.dst());
/* free badge _after_ invalidating all caps */
Badge_allocator::allocator()->free(n->badge());
/* free explicilty allocated cap-selector */
Capability_allocator::allocator()->free(n->gate());
cap_alloc()->free(n->gate());
destroy(platform_specific()->core_mem_alloc(), n);
}
@ -228,3 +230,10 @@ Capability_tree* Capability_tree::tree()
static Capability_tree _tree;
return &_tree;
}
Genode::Capability_allocator* Genode::cap_alloc()
{
static Genode::Capability_allocator_tpl<20*1024> _alloc;
return &_alloc;
}

View File

@ -53,7 +53,7 @@ Genode::Cpu_session_component::native_cap(Genode::Thread_capability cap)
Cpu_thread_component *thread = _lookup_thread(cap);
if (!thread) return Native_capability();
return Native_capability(thread->platform_thread()->native_thread(), -1);
return thread->platform_thread()->thread_cap();
}
@ -61,11 +61,11 @@ Genode::Native_capability Genode::Cpu_session_component::alloc_irq()
{
using namespace Fiasco;
Fiasco_capability irq_cap(Genode::Capability_allocator::allocator()->alloc());
Fiasco_capability irq_cap(Genode::cap_alloc()->alloc());
l4_msgtag_t res = l4_factory_create_irq(L4_BASE_FACTORY_CAP, irq_cap);
if (l4_error(res))
PWRN("Allocation of irq object failed!");
return Genode::Native_capability(irq_cap, -1);
return Genode::Native_capability(irq_cap, Badge_allocator::allocator()->alloc());
}

View File

@ -43,6 +43,7 @@ namespace Genode {
};
Native_task _l4_task_cap; /* L4 task capability slot */
unsigned _badge;
Native_capability _parent;
bool _parent_cap_mapped;
bool _task_cap_mapped;
@ -109,6 +110,7 @@ namespace Genode {
*******************************/
Native_task native_task() { return _l4_task_cap; }
unsigned badge() { return _badge; }
Native_thread parent_cap() { return _parent.dst(); }
};
}

View File

@ -39,6 +39,7 @@ namespace Genode {
friend class Platform_pd;
bool _core_thread;
unsigned _badge;
Native_capability _thread_cap;
Native_capability _gate_cap;
Native_capability _remote_gate_cap;

View File

@ -50,7 +50,7 @@ bool Irq_session_component::Interrupt::higher(Irq_session_component::Interrupt *
Irq_session_component::Interrupt::Interrupt()
: _cap(Capability_allocator::allocator()->alloc()), _sem(), number(0) {}
: _cap(cap_alloc()->alloc()), _sem(), number(0) {}
Native_thread Irq_session_component::Interrupt_handler::handler_cap()

View File

@ -19,5 +19,4 @@
Genode::Native_capability Genode::Pd_session_component::task_cap() {
return Native_capability(_pd.native_task(),
Badge_allocator::allocator()->alloc()); }
return Native_capability(_pd.native_task(), _pd.badge()); }

View File

@ -44,7 +44,7 @@ static addr_t core_utcb_base() {
void Platform_pd::_create_pd(bool syscall)
{
if (!_l4_task_cap.valid())
_l4_task_cap = Capability_allocator::allocator()->alloc();
_l4_task_cap = cap_alloc()->alloc();
if (syscall) {
if (!_l4_task_cap)
@ -154,6 +154,7 @@ void Platform_pd::map_task_cap()
Platform_pd::Platform_pd(bool create, Native_task task_cap)
: _l4_task_cap(task_cap),
_badge(create ? Badge_allocator::allocator()->alloc() : 0),
_parent_cap_mapped(false),
_task_cap_mapped(false)
{
@ -170,5 +171,7 @@ Platform_pd::~Platform_pd()
if (_threads[i])
_threads[i]->unbind();
}
_destroy_pd();
Badge_allocator::allocator()->free(_badge);
}

View File

@ -212,7 +212,7 @@ void Platform_thread::_create_thread()
void Platform_thread::_finalize_construction(const char *name, unsigned prio)
{
/* create irq for new thread */
_irq_cap = Capability_allocator::allocator()->alloc();
_irq_cap = cap_alloc()->alloc();
l4_msgtag_t tag = l4_factory_create_irq(L4_BASE_FACTORY_CAP, _irq_cap);
if (l4_msgtag_has_error(tag))
PWRN("creating thread's irq failed");
@ -236,8 +236,9 @@ void Platform_thread::_finalize_construction(const char *name, unsigned prio)
Platform_thread::Platform_thread(const char *name,
unsigned prio)
: _core_thread(false),
_thread_cap(Capability_allocator::allocator()->alloc(),
Badge_allocator::allocator()->alloc()),
_badge(Badge_allocator::allocator()->alloc()),
_thread_cap(cap_alloc()->alloc_id(_badge),
_badge),
_node(_thread_cap.local_name(), 0, this, _thread_cap.dst()),
_utcb(0),
_platform_pd(0),
@ -272,8 +273,9 @@ Platform_thread::Platform_thread(Native_thread cap, const char *name)
Platform_thread::Platform_thread(const char *name)
: _core_thread(true),
_thread_cap(Capability_allocator::allocator()->alloc(),
Badge_allocator::allocator()->alloc()),
_badge(Badge_allocator::allocator()->alloc()),
_thread_cap(cap_alloc()->alloc_id(_badge),
_badge),
_node(_thread_cap.local_name(), 0, this, _thread_cap.dst()),
_utcb(0),
_platform_pd(0),
@ -302,6 +304,6 @@ Platform_thread::~Platform_thread()
/* remove the thread capability */
Capability_tree::tree()->remove(&_node);
Badge_allocator::allocator()->free(_thread_cap.local_name());
Capability_allocator::allocator()->free(_thread_cap.dst());
cap_alloc()->free(_thread_cap.dst());
Badge_allocator::allocator()->free(_badge);
}

View File

@ -19,6 +19,7 @@
/* core includes */
#include <signal_session_component.h>
#include <cap_session_component.h>
namespace Fiasco {
#include <l4/sys/factory.h>
@ -68,10 +69,11 @@ Signal_source_component::Signal_source_component(Rpc_entrypoint *ep)
{
using namespace Fiasco;
Fiasco_capability irq = Capability_allocator::allocator()->alloc();
unsigned long badge = Badge_allocator::allocator()->alloc();
Fiasco_capability irq = cap_alloc()->alloc_id(badge);
l4_msgtag_t res = l4_factory_create_irq(L4_BASE_FACTORY_CAP, irq);
if (l4_error(res))
PERR("Allocation of irq object failed!");
_blocking_semaphore = Native_capability(irq, -1);
_blocking_semaphore = Native_capability(irq, badge);
}

View File

@ -28,7 +28,6 @@ SRC_CC = main.cc \
irq_session_component.cc \
signal_session_component.cc \
signal_source_component.cc \
cap_sel_alloc.cc \
dump_alloc.cc \
context_area.cc \
cap_session_component.cc \
@ -53,5 +52,4 @@ vpath dump_alloc.cc $(GEN_CORE_DIR)
vpath context_area.cc $(GEN_CORE_DIR)
vpath thread.cc $(REP_DIR)/src/base/thread
vpath thread_bootstrap.cc $(REP_DIR)/src/base/thread
vpath cap_sel_alloc.cc $(REP_DIR)/src/base/env
vpath %.cc $(REP_DIR)/src/core

View File

@ -50,7 +50,7 @@ namespace L4lx {
Genode::size_t size,
Genode::Dataspace_capability ds)
: _name(name), _size(size), _cap(ds),
_ref(Genode::Capability_allocator::allocator()->alloc()) {}
_ref(Genode::cap_alloc()->alloc()) {}
/***************

View File

@ -22,6 +22,7 @@
namespace Fiasco {
#include <l4/re/c/dataspace.h>
#include <l4/sys/err.h>
#include <l4/sys/kdebug.h>
}
using namespace Fiasco;

View File

@ -28,7 +28,7 @@ extern "C" {
l4_cap_idx_t l4re_util_cap_alloc(void)
{
l4_cap_idx_t ret = Genode::Capability_allocator::allocator()->alloc();
l4_cap_idx_t ret = Genode::cap_alloc()->alloc();
if (DEBUG)
PDBG("ret=%lx", ret);

View File

@ -65,7 +65,7 @@ l4_cap_idx_t l4lx_task_number_allocate(void)
*/
int l4lx_task_number_free(l4_cap_idx_t task)
{
Genode::Capability_allocator::allocator()->free(task);
Genode::cap_alloc()->free(task);
return 0;
}
@ -84,7 +84,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::Capability_allocator::allocator()->alloc();
*id = Genode::cap_alloc()->alloc();
return 0;
}