hw: destruct signal receivers

ref #589
This commit is contained in:
Martin Stein 2013-09-12 00:48:27 +02:00 committed by Norman Feske
parent e07781dc1c
commit 84c31a7ea1
11 changed files with 632 additions and 310 deletions

View File

@ -73,6 +73,7 @@ namespace Kernel
/* asynchronous signalling */
NEW_SIGNAL_RECEIVER = 20,
KILL_SIGNAL_RECEIVER = 33,
NEW_SIGNAL_CONTEXT = 21,
KILL_SIGNAL_CONTEXT = 30,
AWAIT_SIGNAL = 22,
@ -438,105 +439,129 @@ namespace Kernel
/**
* Create a kernel object that acts as receiver for asynchronous signals
*
* \param dst physical base of an appropriate portion of memory
* that is thereupon allocated to the kernel
* \param p appropriate memory donation for the kernel object
*
* \return ID of the new kernel object
* \retval >0 kernel name of the new signal receiver
* \retval 0 failed
*
* Restricted to core threads. Regaining of the supplied memory is not
* supported by now.
* Restricted to core threads.
*/
inline unsigned new_signal_receiver(void * dst) {
return syscall(NEW_SIGNAL_RECEIVER, (Syscall_arg)dst); }
/**
* Create a kernel object that acts as a distinct signal type at a receiver
*
* \param dst physical base of an appropriate portion of memory
* that is thereupon allocated to the kernel
* \param receiver_id ID of the receiver kernel-object that shall
* provide the new signal context
* \param imprint Every signal, one receives at the new context,
* will hold this imprint. This enables the receiver
* to interrelate signals with the context.
*
* \return ID of the new kernel object
*
* Core-only syscall. Regaining of the supplied memory is not
* supported by now.
*/
inline unsigned new_signal_context(void * dst, unsigned receiver_id,
unsigned imprint)
inline unsigned new_signal_receiver(addr_t const p)
{
return syscall(NEW_SIGNAL_CONTEXT, (Syscall_arg)dst,
(Syscall_arg)receiver_id, (Syscall_arg)imprint);
return syscall(NEW_SIGNAL_RECEIVER, p);
}
/**
* Wait for occurence of at least one signal at any context of a receiver
* Create a kernel object that acts as a signal context at a receiver
*
* \param receiver_id ID of the targeted receiver kernel-object
* \param p appropriate memory donation for the kernel object
* \param receiver kernel name of targeted signal receiver
* \param imprint userland name of the new signal context
*
* When this call returns, an instance of 'Signal::Data' is located at the
* base of the callers UTCB. It's granted that every occurence of a signal
* is provided through this function, exactly till it gets delivered through
* this function. If multiple threads listen at the same receiver, and/or
* multiple contexts of the receiver trigger simultanously, there is no
* assertion about wich thread receives, and from wich context. But
* deliveries belonging to the same context are serialized through
* 'ack_signal', to enable synchronization in 'kill_signal'.
* \retval >0 kernel name of the new signal context
* \retval 0 failed
*
* Restricted to core threads.
*/
inline void await_signal(unsigned receiver_id) {
syscall(AWAIT_SIGNAL, (Syscall_arg)receiver_id); }
inline unsigned new_signal_context(addr_t const p,
unsigned const receiver,
unsigned const imprint)
{
return syscall(NEW_SIGNAL_CONTEXT, p, receiver, imprint);
}
/**
* Get summarized state of all contexts of a signal receiver
* Wait for the occurence of any context of a receiver
*
* \param receiver_id ID of the targeted receiver kernel-object
* \param receiver kernel name of the targeted signal receiver
*
* \retval 0 suceeded
* \retval -1 failed
*
* If this call returns 0, an instance of 'Signal::Data' is located at the
* base of the callers UTCB. Every occurence of a signal is provided
* through this function until it gets delivered through this function.
* If multiple threads listen at the same receiver, and/or
* multiple contexts of the receiver trigger simultanously, there is no
* assertion about wich thread receives, and from wich context. A context
* that delivered once doesn't deliver again unless its last delivery has
* been acknowledged via 'ack_signal'.
*/
inline bool signal_pending(unsigned receiver_id) {
return syscall(SIGNAL_PENDING, (Syscall_arg)receiver_id); }
inline int await_signal(unsigned const receiver)
{
return syscall(AWAIT_SIGNAL, receiver);
}
/**
* Return wether any context of a receiver is pending
*
* \param receiver kernel name of the targeted signal receiver
*
* \retval 0 none of the contexts is pending or the receiver doesn't exist
* \retval 1 a context of the signal receiver is pending
*/
inline bool signal_pending(unsigned const receiver)
{
return syscall(SIGNAL_PENDING, receiver);
}
/**
* Trigger a specific signal context
*
* \param context_id ID of the targeted context kernel-object
* \param num how often the context shall be triggered by this call
* \param context kernel name of the targeted signal context
* \param num how often the context shall be triggered by this call
*
* \retval 0 suceeded
* \retval -1 failed
*/
inline void submit_signal(unsigned context_id, int num) {
syscall(SUBMIT_SIGNAL, (Syscall_arg)context_id, (Syscall_arg)num); }
inline int submit_signal(unsigned const context, unsigned const num)
{
return syscall(SUBMIT_SIGNAL, context, num);
}
/**
* Acknowledge the processing of the last signal of a signal context
* Acknowledge the processing of the last delivery of a signal context
*
* \param context_id kernel name of the targeted signal context
*
* Should be called after all signal objects, that reference the targeted
* signal context in userland are destructed. The signal context wont
* deliver a new signal until the old signal is acknowledged.
* \param context kernel name of the targeted signal context
*/
inline void ack_signal(unsigned context_id) {
syscall(ACK_SIGNAL, (Syscall_arg)context_id); }
inline void ack_signal(unsigned const context)
{
syscall(ACK_SIGNAL, context);
}
/**
* Destruct a signal context
*
* \param context_id kernel name of the targeted signal context
* \param context kernel name of the targeted signal context
*
* \return wether the context could be destructed
*
* Blocks the caller until the last delivered signal of the targeted
* context is acknowledged. Then the context gets destructed, losing
* all submits that were not delivered when this syscall occured.
* \retval 0 suceeded
* \retval -1 failed
*
* Restricted to core threads.
*/
inline bool kill_signal_context(unsigned context_id) {
return syscall(KILL_SIGNAL_CONTEXT, (Syscall_arg)context_id); }
inline int kill_signal_context(unsigned const context)
{
return syscall(KILL_SIGNAL_CONTEXT, context);
}
/**
* Destruct a signal receiver
*
* \param receiver kernel name of the targeted signal receiver
*
* \retval 0 suceeded
* \retval -1 failed
*
* Restricted to core threads.
*/
inline int kill_signal_receiver(unsigned const receiver)
{
return syscall(KILL_SIGNAL_RECEIVER, receiver);
}
/**
* Create a new virtual-machine that is stopped initially

View File

@ -46,6 +46,9 @@ namespace Genode
unsigned const imprint) {
return call<Rpc_alloc_context>(r, imprint); }
void free_receiver(Signal_receiver_capability cap) {
call<Rpc_free_receiver>(cap); }
void free_context(Signal_context_capability cap) {
call<Rpc_free_context>(cap); }
};

View File

@ -80,10 +80,19 @@ namespace Genode
alloc_context(Signal_receiver_capability r,
unsigned const imprint) = 0;
/**
* Free a signal receiver
*
* \param cap capability of targeted signal receiver
*
* \throw Exception
*/
virtual void free_receiver(Signal_receiver_capability cap) = 0;
/**
* Free a signal context
*
* \param cap capability of signal-context to release
* \param cap capability of targeted signal context
*
* \throw Exception
*/
@ -97,15 +106,21 @@ namespace Genode
GENODE_RPC_THROW(Rpc_alloc_receiver, Signal_receiver_capability,
alloc_receiver, GENODE_TYPE_LIST(Out_of_metadata,
Exception));
GENODE_RPC_THROW(Rpc_alloc_context, Signal_context_capability,
alloc_context, GENODE_TYPE_LIST(Out_of_metadata,
Exception), Signal_receiver_capability, unsigned);
GENODE_RPC_THROW(Rpc_free_receiver, void, free_receiver,
GENODE_TYPE_LIST(Exception),
Signal_receiver_capability);
GENODE_RPC_THROW(Rpc_free_context, void, free_context,
GENODE_TYPE_LIST(Exception),
Signal_context_capability);
GENODE_RPC_INTERFACE(Rpc_alloc_receiver, Rpc_alloc_context,
Rpc_free_context);
Rpc_free_receiver, Rpc_free_context);
};
}

View File

@ -109,9 +109,16 @@ Signal_receiver::Signal_receiver()
}
void Signal_receiver::_platform_destructor()
{
/* release server resources of receiver */
signal_connection()->free_receiver(_cap);
}
void Signal_receiver::_unsynchronized_dissolve(Signal_context * c)
{
/* release core resources */
/* release server resources of context */
signal_connection()->free_context(c->_cap);
/* reset the context */
@ -128,7 +135,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c)
/* check if the context is already managed */
Lock::Guard contexts_guard(_contexts_lock);
Lock::Guard context_guard(c->_lock);
if (c->_receiver) throw Context_already_in_use();
if (c->_receiver) { throw Context_already_in_use(); }
/* create a kernel object that corresponds to the context */
bool session_upgraded = 0;
@ -154,13 +161,10 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c)
}
void Signal_receiver::dissolve(Signal_context *context)
void Signal_receiver::dissolve(Signal_context * const context)
{
if (context->_receiver != this)
throw Context_not_associated();
if (context->_receiver != this) { throw Context_not_associated(); }
Lock::Guard list_lock_guard(_contexts_lock);
_unsynchronized_dissolve(context);
/*
@ -180,24 +184,17 @@ bool Signal_receiver::pending() { return Kernel::signal_pending(_cap.dst()); }
Signal Signal_receiver::wait_for_signal()
{
/* await a signal */
Kernel::await_signal(_cap.dst());
Signal s(*(Signal::Data *)Thread_base::myself()->utcb());
Signal_context * const c = s.context();
/* check if the context of the signal is managed by us */
Lock::Guard context_guard(c->_lock);
if (c->_receiver != this) {
PERR("%s: Context not managed by this receiver", __PRETTY_FUNCTION__);
while (1) ;
if (Kernel::await_signal(_cap.dst())) {
PERR("failed to receive signal");
throw Exception();
}
/* check attributes of the signal and return it */
if (s.num() == 0) PWRN("Returning signal with num == 0");
/* get signal data */
Signal s(*(Signal::Data *)Thread_base::myself()->utcb());
return s;
}
void Signal_receiver::local_submit(Signal::Data signal) {
PDBG("Not implemented"); };
void Signal_receiver::_platform_destructor() { }
void Signal_receiver::local_submit(Signal::Data signal)
{
PDBG("Not implemented");
}

View File

@ -37,15 +37,22 @@ namespace Genode
private:
/**
* Maps a signal-receiver name to related core and kernel resources
*/
class Receiver;
/**
* Maps a signal-context name to related core and kernel resources
*/
class Context;
typedef Object_pool<Context> Context_pool;
typedef Object_pool<Receiver> Receiver_pool;
typedef Object_pool<Context> Context_pool;
Allocator_guard _md_alloc;
Slab _receivers_slab;
Receiver_pool _receivers;
Slab _contexts_slab;
Context_pool _contexts;
char _initial_receivers_sb [RECEIVERS_SB_SIZE];
@ -81,10 +88,43 @@ namespace Genode
Signal_context_capability
alloc_context(Signal_receiver_capability, unsigned const);
void free_receiver(Signal_receiver_capability);
void free_context(Signal_context_capability);
};
}
class Genode::Signal_session_component::Receiver : public Receiver_pool::Entry
{
public:
/**
* Constructor
*/
Receiver(Untyped_capability cap) : Entry(cap) { }
/**
* Name of signal receiver
*/
unsigned id() const { return Receiver_pool::Entry::cap().dst(); }
/**
* Size of SLAB block occupied by resources and this resource info
*/
static size_t slab_size()
{
return sizeof(Receiver) + Kernel::signal_receiver_size();
}
/**
* Base of region donated to the kernel
*/
static addr_t kernel_donation(void * const slab_addr)
{
return ((addr_t)slab_addr + sizeof(Receiver));
}
};
class Genode::Signal_session_component::Context : public Context_pool::Entry
{
public:
@ -110,9 +150,9 @@ class Genode::Signal_session_component::Context : public Context_pool::Entry
/**
* Base of region donated to the kernel
*/
static void * kernel_donation(void * const slab_addr)
static addr_t kernel_donation(void * const slab_addr)
{
return (void *)((addr_t)slab_addr + sizeof(Context));
return ((addr_t)slab_addr + sizeof(Context));
}
};

View File

@ -91,13 +91,6 @@ namespace Kernel
namespace Kernel
{
void deliver_signal(Signal_handler * const dst,
void * const base,
size_t const size)
{
((Thread *)dst->id())->receive_signal(base, size);
}
class Vm : public Object<Vm, MAX_VMS>,
public Execution_context
{
@ -601,15 +594,16 @@ namespace Kernel
*/
void do_new_signal_receiver(Thread * const user)
{
/* check permissions */
assert(user->pd_id() == core_id());
/* create receiver */
void * dst = (void *)user->user_arg_1();
Signal_receiver * const r = new (dst) Signal_receiver();
/* return success */
user->user_arg_0(r->id());
/* check permissions */
if (user->pd_id() != core_id()) {
PERR("permission to create signal receiver denied");
user->user_arg_0(0);
return;
}
/* create receiver */
void * p = (void *)user->user_arg_1();
Signal_receiver * const r = new (p) Signal_receiver();
user->user_arg_0(r->id());
}
@ -619,19 +613,29 @@ namespace Kernel
void do_new_signal_context(Thread * const user)
{
/* check permissions */
assert(user->pd_id() == core_id());
if (user->pd_id() != core_id()) {
PERR("not entitled to create signal context");
user->user_arg_0(0);
return;
}
/* lookup receiver */
unsigned rid = user->user_arg_2();
Signal_receiver * const r = Signal_receiver::pool()->object(rid);
assert(r);
/* create context */
void * dst = (void *)user->user_arg_1();
unsigned id = user->user_arg_2();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
PERR("invalid signal receiver");
user->user_arg_0(0);
return;
}
/* create and assign context*/
void * p = (void *)user->user_arg_1();
unsigned imprint = user->user_arg_3();
Signal_context * const c = new (dst) Signal_context(r, imprint);
/* return success */
if (r->new_context(p, imprint)) {
PERR("failed to create signal context");
user->user_arg_0(0);
return;
}
/* return context name */
Signal_context * const c = (Signal_context *)p;
user->user_arg_0(c->id());
}
@ -642,13 +646,21 @@ namespace Kernel
void do_await_signal(Thread * const user)
{
/* lookup receiver */
unsigned rid = user->user_arg_2();
Signal_receiver * const r = Signal_receiver::pool()->object(rid);
assert(r);
/* let user listen to receiver */
unsigned id = user->user_arg_1();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
PERR("invalid signal receiver");
user->user_arg_0(-1);
return;
}
/* register handler at the receiver */
user->await_signal(r);
r->add_handler(user->signal_handler());
if (r->add_handler(user)) {
PERR("failed to register handler at signal receiver");
user->user_arg_0(-1);
return;
}
user->user_arg_0(0);
}
@ -657,12 +669,15 @@ namespace Kernel
*/
void do_signal_pending(Thread * const user)
{
/* lookup receiver */
unsigned rid = user->user_arg_2();
Signal_receiver * const r = Signal_receiver::pool()->object(rid);
assert(r);
/* set return value */
/* lookup signal receiver */
unsigned id = user->user_arg_1();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
PERR("invalid signal receiver");
user->user_arg_0(0);
return;
}
/* get pending state */
user->user_arg_0(r->deliverable());
}
@ -672,15 +687,20 @@ namespace Kernel
*/
void do_submit_signal(Thread * const user)
{
/* lookup context */
Signal_context * const c =
Signal_context::pool()->object(user->user_arg_1());
/* lookup signal context */
unsigned const id = user->user_arg_1();
Signal_context * const c = Signal_context::pool()->object(id);
if(!c) {
PDBG("invalid signal-context capability");
PERR("invalid signal context");
user->user_arg_0(-1);
return;
}
/* trigger signal at context */
c->submit(user->user_arg_2());
/* trigger signal context */
if (c->submit(user->user_arg_2())) {
user->user_arg_0(-1);
return;
}
user->user_arg_0(0);
}
@ -689,11 +709,15 @@ namespace Kernel
*/
void do_ack_signal(Thread * const user)
{
/* lookup signal context */
unsigned id = user->user_arg_1();
Signal_context * const c = Signal_context::pool()->object(id);
if (!c) return;
Thread * const t = (Thread *)c->ack();
if (t) { t->kill_signal_context_done(); }
if (!c) {
PWRN("invalid signal context");
return;
}
/* acknowledge */
c->ack();
}
@ -703,15 +727,55 @@ namespace Kernel
void do_kill_signal_context(Thread * const user)
{
/* check permissions */
assert(user->pd_id() == core_id());
if (user->pd_id() != core_id()) {
PERR("not entitled to kill signal context");
user->user_arg_0(-1);
return;
}
/* lookup signal context */
unsigned id = user->user_arg_1();
Signal_context * const c = Signal_context::pool()->object(id);
if (!c) { return; }
if (c->kill((unsigned)user)) { return; }
user->kill_signal_context_blocks();
if (!c) {
user->user_arg_0(0);
return;
}
/* kill signal context */
if (c->kill(user)) {
user->user_arg_0(-1);
return;
}
user->user_arg_0(0);
}
/**
* Do specific syscall for 'user', for details see 'syscall.h'
*/
void do_kill_signal_receiver(Thread * const user)
{
/* check permissions */
if (user->pd_id() != core_id()) {
PERR("not entitled to kill signal receiver");
user->user_arg_0(-1);
return;
}
/* lookup signal receiver */
user->user_arg_0(1);
unsigned id = user->user_arg_1();
Signal_receiver * const r = Signal_receiver::pool()->object(id);
if (!r) {
user->user_arg_0(0);
return;
}
/* kill signal receiver */
if (r->kill(user)) {
user->user_arg_0(-1);
return;
}
user->user_arg_0(0);
}
/**
* Do specific syscall for 'user', for details see 'syscall.h'
*/
@ -779,38 +843,39 @@ namespace Kernel
{
switch (user->user_arg_0())
{
case NEW_THREAD: do_new_thread(user); return;
case DELETE_THREAD: do_delete_thread(user); return;
case START_THREAD: do_start_thread(user); return;
case PAUSE_THREAD: do_pause_thread(user); return;
case RESUME_THREAD: do_resume_thread(user); return;
case RESUME_FAULTER: do_resume_faulter(user); return;
case GET_THREAD: do_get_thread(user); return;
case CURRENT_THREAD_ID: do_current_thread_id(user); return;
case YIELD_THREAD: do_yield_thread(user); return;
case READ_THREAD_STATE: do_read_thread_state(user); return;
case WRITE_THREAD_STATE: do_write_thread_state(user); return;
case REQUEST_AND_WAIT: do_request_and_wait(user); return;
case REPLY: do_reply(user); return;
case WAIT_FOR_REQUEST: do_wait_for_request(user); return;
case SET_PAGER: do_set_pager(user); return;
case UPDATE_PD: do_update_pd(user); return;
case UPDATE_REGION: do_update_region(user); return;
case NEW_PD: do_new_pd(user); return;
case ALLOCATE_IRQ: do_allocate_irq(user); return;
case AWAIT_IRQ: do_await_irq(user); return;
case FREE_IRQ: do_free_irq(user); return;
case PRINT_CHAR: do_print_char(user); return;
case NEW_SIGNAL_RECEIVER: do_new_signal_receiver(user); return;
case NEW_SIGNAL_CONTEXT: do_new_signal_context(user); return;
case KILL_SIGNAL_CONTEXT: do_kill_signal_context(user); return;
case AWAIT_SIGNAL: do_await_signal(user); return;
case SUBMIT_SIGNAL: do_submit_signal(user); return;
case SIGNAL_PENDING: do_signal_pending(user); return;
case ACK_SIGNAL: do_ack_signal(user); return;
case NEW_VM: do_new_vm(user); return;
case RUN_VM: do_run_vm(user); return;
case PAUSE_VM: do_pause_vm(user); return;
case NEW_THREAD: do_new_thread(user); return;
case DELETE_THREAD: do_delete_thread(user); return;
case START_THREAD: do_start_thread(user); return;
case PAUSE_THREAD: do_pause_thread(user); return;
case RESUME_THREAD: do_resume_thread(user); return;
case RESUME_FAULTER: do_resume_faulter(user); return;
case GET_THREAD: do_get_thread(user); return;
case CURRENT_THREAD_ID: do_current_thread_id(user); return;
case YIELD_THREAD: do_yield_thread(user); return;
case READ_THREAD_STATE: do_read_thread_state(user); return;
case WRITE_THREAD_STATE: do_write_thread_state(user); return;
case REQUEST_AND_WAIT: do_request_and_wait(user); return;
case REPLY: do_reply(user); return;
case WAIT_FOR_REQUEST: do_wait_for_request(user); return;
case SET_PAGER: do_set_pager(user); return;
case UPDATE_PD: do_update_pd(user); return;
case UPDATE_REGION: do_update_region(user); return;
case NEW_PD: do_new_pd(user); return;
case ALLOCATE_IRQ: do_allocate_irq(user); return;
case AWAIT_IRQ: do_await_irq(user); return;
case FREE_IRQ: do_free_irq(user); return;
case PRINT_CHAR: do_print_char(user); return;
case NEW_SIGNAL_RECEIVER: do_new_signal_receiver(user); return;
case NEW_SIGNAL_CONTEXT: do_new_signal_context(user); return;
case KILL_SIGNAL_CONTEXT: do_kill_signal_context(user); return;
case KILL_SIGNAL_RECEIVER: do_kill_signal_receiver(user); return;
case AWAIT_SIGNAL: do_await_signal(user); return;
case SUBMIT_SIGNAL: do_submit_signal(user); return;
case SIGNAL_PENDING: do_signal_pending(user); return;
case ACK_SIGNAL: do_ack_signal(user); return;
case NEW_VM: do_new_vm(user); return;
case RUN_VM: do_run_vm(user); return;
case PAUSE_VM: do_pause_vm(user); return;
default:
PERR("invalid syscall");
user->crash();

View File

@ -0,0 +1,25 @@
/*
* \brief Kernel backend for asynchronous inter-process communication
* \author Martin Stein
* \date 2012-11-30
*/
/*
* Copyright (C) 2012-2013 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.
*/
/* core includes */
#include <kernel/signal_receiver.h>
void Kernel::Signal_context::_deliverable()
{
if (!_submits) return;
_receiver->_add_deliverable(this);
}
Kernel::Signal_context::~Signal_context() { _receiver->_context_killed(this); }

View File

@ -25,10 +25,20 @@
namespace Kernel
{
/**
* Enables external components to act as a signal handler
* Ability to receive from signal receivers
*/
class Signal_handler;
/**
* Ability to destruct signal contexts
*/
class Signal_context_killer;
/**
* Ability to destruct signal receivers
*/
class Signal_receiver_killer;
/**
* Signal types that are assigned to a signal receiver each
*/
@ -38,17 +48,6 @@ namespace Kernel
* Combines signal contexts to an entity that handlers can listen to
*/
class Signal_receiver;
/**
* Signal delivery backend
*
* \param dst destination
* \param base signal-data base
* \param size signal-data size
*/
void deliver_signal(Signal_handler * const dst,
void * const base,
size_t const size);
}
class Kernel::Signal_handler
@ -59,22 +58,56 @@ class Kernel::Signal_handler
typedef Genode::Fifo_element<Signal_handler> Fifo_element;
Fifo_element _fe;
unsigned const _id;
Fifo_element _handlers_fe;
/**
* Signal delivery backend
*
* \param base signal-data base
* \param size signal-data size
*/
virtual void _signal_handler(void * const base, size_t const size) = 0;
public:
/**
* Constructor
*/
Signal_handler(unsigned id) : _fe(this), _id(id) { }
Signal_handler() : _handlers_fe(this) { }
};
class Kernel::Signal_context_killer
{
friend class Signal_context;
/***************
** Accessors **
***************/
private:
unsigned id() { return _id; }
/**
* Notice that the destruction is pending
*/
virtual void _signal_context_kill_pending() = 0;
/**
* Notice that pending destruction is done
*/
virtual void _signal_context_kill_done() = 0;
};
class Kernel::Signal_receiver_killer
{
friend class Signal_receiver;
private:
/**
* Notice that the destruction is pending
*/
virtual void _signal_receiver_kill_pending() = 0;
/**
* Notice that pending destruction is done
*/
virtual void _signal_receiver_kill_done() = 0;
};
class Kernel::Signal_context
@ -87,17 +120,18 @@ class Kernel::Signal_context
typedef Genode::Fifo_element<Signal_context> Fifo_element;
Fifo_element _fe;
Fifo_element _deliver_fe;
Fifo_element _contexts_fe;
Signal_receiver * const _receiver;
unsigned const _imprint;
unsigned _submits;
bool _ack;
unsigned _killer;
Signal_context_killer * _killer;
/**
* Tell receiver about the submits of the context if any
*/
inline void _deliverable();
void _deliverable();
/**
* Called by receiver when all submits have been delivered
@ -108,85 +142,87 @@ class Kernel::Signal_context
_ack = 0;
}
public:
/**
* Destructor
*/
~Signal_context();
/**
* Constructor
*
* \param r receiver that the context is assigned to
* \param imprint userland identification of the context
*/
Signal_context(Signal_receiver * const r, unsigned const imprint)
:
_fe(this), _receiver(r), _imprint(imprint), _submits(0), _ack(1),
_killer(0)
_deliver_fe(this), _contexts_fe(this), _receiver(r),
_imprint(imprint), _submits(0), _ack(1), _killer(0)
{ }
public:
/**
* Submit the signal
*
* \param n number of submits
*
* \retval 0 succeeded
* \retval -1 failed
*/
void submit(unsigned const n)
int submit(unsigned const n)
{
if (_submits >= (unsigned)~0 - n) {
PERR("overflow at signal-submit count");
return;
}
if (_killer) {
PERR("signal context already in destruction");
return;
}
if (_killer || _submits >= (unsigned)~0 - n) { return -1; }
_submits += n;
if (!_ack) { return; }
_deliverable();
if (_ack) { _deliverable(); }
return 0;
}
/**
* Acknowledge delivery of signal
*
* \retval 0 no kill request finished
* \retval > 0 name of finished kill request
*/
unsigned ack()
void ack()
{
if (_ack) {
PERR("unexpected signal acknowledgment");
return 0;
}
if (_ack) { return; }
if (!_killer) {
_ack = 1;
_deliverable();
return 0;
return;
}
this->~Signal_context();
return _killer;
_killer->_signal_context_kill_done();
}
/**
* Destruct context or prepare to do it as soon as delivery is done
*
* \param killer name of the kill request
* \param killer object that shall receive progress reports
*
* \retval 1 destruction is done
* \retval 0 destruction is initiated, will be done with the next ack
* \retval 0 succeeded
* \retval -1 failed
*/
bool kill(unsigned const killer)
int kill(Signal_context_killer * const k)
{
/* FIXME: aggregate or avoid multiple kill requests */
if (_killer) {
PERR("multiple kill requests");
while (1) { }
if (_killer) { return -1; }
/* destruct directly if there is no unacknowledged delivery */
if (_ack) {
this->~Signal_context();
return 0;
}
_killer = killer;
if (!_ack) { return 0; }
this->~Signal_context();
return 1;
/* wait for delivery acknowledgement */
_killer = k;
_killer->_signal_context_kill_pending();
return 0;
}
};
class Kernel::Signal_receiver
:
public Object<Signal_receiver, MAX_SIGNAL_RECEIVERS>
public Object<Signal_receiver, MAX_SIGNAL_RECEIVERS>,
public Signal_context_killer
{
friend class Signal_context;
friend class Context_killer;
private:
@ -195,14 +231,19 @@ class Kernel::Signal_receiver
template <typename T> class Fifo : public Genode::Fifo<T> { };
Fifo<Signal_handler::Fifo_element> _handlers;
Fifo<Signal_context::Fifo_element> _deliverable;
Fifo<Signal_context::Fifo_element> _deliver;
Fifo<Signal_context::Fifo_element> _contexts;
unsigned _context_kills;
Signal_receiver_killer * _killer;
/**
* Recognize that context 'c' has submits to deliver
*/
void _add_deliverable(Signal_context * const c)
{
if (!c->_fe.is_enqueued()) _deliverable.enqueue(&c->_fe);
if (!c->_deliver_fe.is_enqueued()) {
_deliver.enqueue(&c->_deliver_fe);
}
_listen();
}
@ -214,53 +255,128 @@ class Kernel::Signal_receiver
while (1)
{
/* check if there are deliverable signal */
if (_deliverable.empty()) return;
Signal_context * const c = _deliverable.dequeue()->object();
if (_deliver.empty()) return;
Signal_context * const c = _deliver.dequeue()->object();
/* if there is no handler re-enqueue context and exit */
if (_handlers.empty()) {
_deliverable.enqueue(&c->_fe);
_deliver.enqueue(&c->_deliver_fe);
return;
}
/* delivery from context to handler */
Signal_handler * const h = _handlers.dequeue()->object();
Signal::Data data((Genode::Signal_context *)c->_imprint,
c->_submits);
deliver_signal(h, &data, sizeof(data));
h->_signal_handler(&data, sizeof(data));
c->_delivered();
}
}
/**
* Notice that a context of the receiver has been killed
*
* \param c killed context
*/
void _context_killed(Signal_context * const c)
{
if (c->_deliver_fe.is_enqueued()) {
_deliver.remove(&c->_deliver_fe);
}
_contexts.remove(&c->_contexts_fe);
}
/***************************
** Signal_context_killer **
***************************/
void _signal_context_kill_pending() { _context_kills++; }
void _signal_context_kill_done()
{
_context_kills--;
if (!_context_kills && _killer) {
this->~Signal_receiver();
_killer->_signal_receiver_kill_done();
}
}
public:
/**
* Let a handler wait for signals of the receiver
* Constructor
*/
void add_handler(Signal_handler * const h)
/**
* Let a handler 'h' wait for signals of the receiver
*
* \retval 0 succeeded
* \retval -1 failed
*/
int add_handler(Signal_handler * const h)
{
_handlers.enqueue(&h->_fe);
if (_killer) { return -1; }
_handlers.enqueue(&h->_handlers_fe);
_listen();
return 0;
}
/**
* Stop a handler from waiting for signals of the receiver
* Stop a handler 'h' from waiting for signals of the receiver
*/
void remove_handler(Signal_handler * const h)
{
_handlers.remove(&h->_fe);
_handlers.remove(&h->_handlers_fe);
}
/**
* Create a context that is assigned to the receiver
*
* \retval 0 succeeded
* \retval -1 failed
*/
int new_context(void * p, unsigned imprint)
{
if (_killer) { return -1; }
new (p) Signal_context(this, imprint);
Signal_context * const c = (Signal_context *)p;
_contexts.enqueue(&c->_contexts_fe);
return 0;
}
/**
* Return wether any of the contexts of this receiver is deliverable
*/
bool deliverable() { return !_deliverable.empty(); }
bool deliverable() { return !_deliver.empty(); }
/**
* Destruct receiver or prepare to do it as soon as delivery is done
*
* \param killer object that shall receive progress reports
*
* \retval 0 succeeded
* \retval -1 failed
*/
int kill(Signal_receiver_killer * const k)
{
if (_killer) { return -1; }
/* start killing at all contexts of the receiver */
Signal_context * c = _contexts.dequeue()->object();
while (c) {
c->kill(this);
c = _contexts.dequeue()->object();
}
/* destruct directly if no context kill is pending */
if (!_context_kills) {
this->~Signal_receiver();
return 0;
}
/* wait for pending context kills */
_killer = k;
_killer->_signal_receiver_kill_pending();
return 0;
}
};
void Kernel::Signal_context::_deliverable()
{
if (!_submits) return;
_receiver->_add_deliverable(this);
}
#endif /* _KERNEL__SIGNAL_RECEIVER_ */

View File

@ -81,7 +81,10 @@ class Kernel::Thread
public Object<Thread, MAX_THREADS>,
public Execution_context,
public Ipc_node,
public Irq_receiver
public Irq_receiver,
public Signal_context_killer,
public Signal_receiver_killer,
public Signal_handler
{
private:
@ -93,7 +96,7 @@ class Kernel::Thread
AWAIT_RESUMPTION,
AWAIT_IRQ,
AWAIT_SIGNAL,
AWAIT_SIGNAL_CONTEXT_DESTRUCT,
AWAIT_SIGNAL_CONTEXT_KILL,
CRASHED,
};
@ -105,7 +108,6 @@ class Kernel::Thread
Native_utcb * _phys_utcb;
Native_utcb * _virt_utcb;
Signal_receiver * _signal_receiver;
Signal_handler _signal_handler;
/**
* Resume execution
@ -116,6 +118,47 @@ class Kernel::Thread
_state = SCHEDULED;
}
/***************************
** Signal_context_killer **
***************************/
void _signal_context_kill_pending()
{
cpu_scheduler()->remove(this);
_state = AWAIT_SIGNAL_CONTEXT_KILL;
}
void _signal_context_kill_done()
{
if (_state != AWAIT_SIGNAL_CONTEXT_KILL) {
PDBG("ignore unexpected signal-context destruction");
return;
}
user_arg_0(0);
_schedule();
}
/********************
** Signal_handler **
********************/
void _signal_handler(void * const base, size_t const size)
{
assert(_state == AWAIT_SIGNAL && size <= phys_utcb()->size());
Genode::memcpy(phys_utcb()->base(), base, size);
_schedule();
}
/****************************
** Signal_receiver_killer **
****************************/
void _signal_receiver_kill_pending() { PERR("not implemented"); }
void _signal_receiver_kill_done() { PERR("not implemented"); }
/**************
** Ipc_node **
@ -161,7 +204,7 @@ class Kernel::Thread
:
_platform_thread(platform_thread), _state(AWAIT_START),
_pager(0), _pd_id(0), _phys_utcb(0), _virt_utcb(0),
_signal_receiver(0), _signal_handler((unsigned)this)
_signal_receiver(0)
{ }
/**
@ -280,10 +323,10 @@ class Kernel::Thread
return 0;
case AWAIT_SIGNAL:
PDBG("cancel signal receipt");
_signal_receiver->remove_handler(signal_handler());
_signal_receiver->remove_handler(this);
_schedule();
return 0;
case AWAIT_SIGNAL_CONTEXT_DESTRUCT:
case AWAIT_SIGNAL_CONTEXT_KILL:
PDBG("cancel signal context destruction");
_schedule();
return 0;
@ -361,41 +404,6 @@ class Kernel::Thread
_signal_receiver = receiver;
}
/**
* Let the thread receive signal data
*
* \param base signal-data base
* \param size signal-data size
*/
void receive_signal(void * const base, size_t const size)
{
assert(_state == AWAIT_SIGNAL && size <= phys_utcb()->size());
Genode::memcpy(phys_utcb()->base(), base, size);
_schedule();
}
/**
* Destructing a signal context blocks the thread for now
*/
void kill_signal_context_blocks()
{
cpu_scheduler()->remove(this);
_state = AWAIT_SIGNAL_CONTEXT_DESTRUCT;
}
/**
* A signal-context destruction that blocked the thread is done
*/
void kill_signal_context_done()
{
if (_state != AWAIT_SIGNAL_CONTEXT_DESTRUCT) {
PDBG("ignore unexpected signal-context destruction");
return;
}
user_arg_0(1);
_schedule();
}
/***********************
** Execution_context **
@ -441,8 +449,6 @@ class Kernel::Thread
unsigned pd_id() const { return _pd_id; }
Native_utcb * phys_utcb() const { return _phys_utcb; }
Signal_handler * signal_handler() { return &_signal_handler; }
};
#endif /* _CORE__KERNEL__THREAD_H_ */

View File

@ -13,7 +13,6 @@
/* Genode includes */
#include <base/printf.h>
#include <base/sleep.h>
#include <kernel/syscalls.h>
/* core includes */
@ -31,7 +30,7 @@ void * operator new (size_t, void * p) { return p; }
Signal_session_component::Signal_session_component(Allocator * const md,
size_t const ram_quota) :
_md_alloc(md, ram_quota),
_receivers_slab(Kernel::signal_receiver_size(), RECEIVERS_SB_SIZE,
_receivers_slab(Receiver::slab_size(), RECEIVERS_SB_SIZE,
(Slab_block *)&_initial_receivers_sb, &_md_alloc),
_contexts_slab(Context::slab_size(), CONTEXTS_SB_SIZE,
(Slab_block *)&_initial_contexts_sb, &_md_alloc)
@ -48,21 +47,51 @@ Signal_session_component::~Signal_session_component()
Signal_receiver_capability Signal_session_component::alloc_receiver()
{
/* allocate resources for receiver */
size_t const s = Kernel::signal_receiver_size();
void * p;
if (!_receivers_slab.alloc(s, &p)) {
PERR("failed to allocate signal receiver");
if (!_receivers_slab.alloc(Receiver::slab_size(), &p)) {
PERR("failed to allocate signal-receiver resources");
throw Out_of_metadata();
}
/* create kernel object for receiver */
unsigned const id = Kernel::new_signal_receiver(p);
if (!id) {
addr_t donation = Receiver::kernel_donation(p);
unsigned const id = Kernel::new_signal_receiver(donation);
if (!id)
{
/* clean up */
_receivers_slab.free(p, Receiver::slab_size());
PERR("failed to create signal receiver");
throw Exception();
}
/* remember receiver ressources */
Native_capability cap(id, id);
Receiver * const r = new (p) Receiver(cap);
_receivers.insert(r);
/* return receiver capability */
Native_capability c(id, id);
return reinterpret_cap_cast<Signal_receiver>(c);
return reinterpret_cap_cast<Signal_receiver>(cap);
}
void Signal_session_component::free_receiver(Signal_receiver_capability cap)
{
/* lookup ressource info */
Receiver * const r = _receivers.lookup_and_lock(cap);
if (!r) {
PERR("unknown signal receiver");
throw Exception();
}
/* release kernel resources */
if (Kernel::kill_signal_receiver(r->id()))
{
/* clean-up */
r->release();
PERR("failed to kill signal receiver");
throw Exception();
}
/* release core resources */
_receivers.remove_locked(r);
r->~Receiver();
_receivers_slab.free(r, Receiver::slab_size());
}
@ -77,7 +106,7 @@ Signal_session_component::alloc_context(Signal_receiver_capability r,
throw Out_of_metadata();
}
/* create kernel object for context */
void * donation = Context::kernel_donation(p);
addr_t donation = Context::kernel_donation(p);
unsigned const id = Kernel::new_signal_context(donation, r.dst(), imprint);
if (!id)
{
@ -104,7 +133,7 @@ void Signal_session_component::free_context(Signal_context_capability cap)
throw Exception();
}
/* release kernel resources */
if (!Kernel::kill_signal_context(c->id()))
if (Kernel::kill_signal_context(c->id()))
{
/* clean-up */
c->release();

View File

@ -47,6 +47,7 @@ SRC_CC += console.cc \
trace_session_component.cc \
thread.cc \
kernel.cc \
kernel/signal_receiver.cc \
rm_session_support.cc \
kernel_support.cc \
trustzone.cc \