From 84c31a7ea12eca35ee58a72a94d1f81bb8a03d29 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Thu, 12 Sep 2013 00:48:27 +0200 Subject: [PATCH] hw: destruct signal receivers ref #589 --- base-hw/include/kernel/syscalls.h | 155 +++++----- base-hw/include/signal_session/client.h | 3 + .../include/signal_session/signal_session.h | 19 +- base-hw/src/base/signal/signal.cc | 43 ++- .../core/include/signal_session_component.h | 46 ++- base-hw/src/core/kernel.cc | 235 +++++++++------ base-hw/src/core/kernel/signal_receiver.cc | 25 ++ base-hw/src/core/kernel/signal_receiver.h | 272 +++++++++++++----- base-hw/src/core/kernel/thread.h | 92 +++--- base-hw/src/core/signal_session_component.cc | 51 +++- base-hw/src/core/target.inc | 1 + 11 files changed, 632 insertions(+), 310 deletions(-) create mode 100644 base-hw/src/core/kernel/signal_receiver.cc diff --git a/base-hw/include/kernel/syscalls.h b/base-hw/include/kernel/syscalls.h index d1a37e246..928279a2c 100644 --- a/base-hw/include/kernel/syscalls.h +++ b/base-hw/include/kernel/syscalls.h @@ -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 diff --git a/base-hw/include/signal_session/client.h b/base-hw/include/signal_session/client.h index 9bc93ba48..3cdc55eb1 100644 --- a/base-hw/include/signal_session/client.h +++ b/base-hw/include/signal_session/client.h @@ -46,6 +46,9 @@ namespace Genode unsigned const imprint) { return call(r, imprint); } + void free_receiver(Signal_receiver_capability cap) { + call(cap); } + void free_context(Signal_context_capability cap) { call(cap); } }; diff --git a/base-hw/include/signal_session/signal_session.h b/base-hw/include/signal_session/signal_session.h index d2290e7d3..989678181 100644 --- a/base-hw/include/signal_session/signal_session.h +++ b/base-hw/include/signal_session/signal_session.h @@ -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); }; } diff --git a/base-hw/src/base/signal/signal.cc b/base-hw/src/base/signal/signal.cc index 6cb1b948c..a10629602 100644 --- a/base-hw/src/base/signal/signal.cc +++ b/base-hw/src/base/signal/signal.cc @@ -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"); +} diff --git a/base-hw/src/core/include/signal_session_component.h b/base-hw/src/core/include/signal_session_component.h index 566437e3e..9634b3882 100644 --- a/base-hw/src/core/include/signal_session_component.h +++ b/base-hw/src/core/include/signal_session_component.h @@ -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_pool; + typedef Object_pool Receiver_pool; + typedef Object_pool 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)); } }; diff --git a/base-hw/src/core/kernel.cc b/base-hw/src/core/kernel.cc index c8d377020..e8579848f 100644 --- a/base-hw/src/core/kernel.cc +++ b/base-hw/src/core/kernel.cc @@ -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, 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(); diff --git a/base-hw/src/core/kernel/signal_receiver.cc b/base-hw/src/core/kernel/signal_receiver.cc new file mode 100644 index 000000000..6ff47eef4 --- /dev/null +++ b/base-hw/src/core/kernel/signal_receiver.cc @@ -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 + + +void Kernel::Signal_context::_deliverable() +{ + if (!_submits) return; + _receiver->_add_deliverable(this); +} + + +Kernel::Signal_context::~Signal_context() { _receiver->_context_killed(this); } diff --git a/base-hw/src/core/kernel/signal_receiver.h b/base-hw/src/core/kernel/signal_receiver.h index 725b70a22..a3fadc981 100644 --- a/base-hw/src/core/kernel/signal_receiver.h +++ b/base-hw/src/core/kernel/signal_receiver.h @@ -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 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 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 + public Object, + public Signal_context_killer { friend class Signal_context; + friend class Context_killer; private: @@ -195,14 +231,19 @@ class Kernel::Signal_receiver template class Fifo : public Genode::Fifo { }; Fifo _handlers; - Fifo _deliverable; + Fifo _deliver; + Fifo _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_ */ diff --git a/base-hw/src/core/kernel/thread.h b/base-hw/src/core/kernel/thread.h index 19207ed42..f4c7fcbc5 100644 --- a/base-hw/src/core/kernel/thread.h +++ b/base-hw/src/core/kernel/thread.h @@ -81,7 +81,10 @@ class Kernel::Thread public Object, 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_ */ diff --git a/base-hw/src/core/signal_session_component.cc b/base-hw/src/core/signal_session_component.cc index ddf3ec7fe..93169d9cb 100644 --- a/base-hw/src/core/signal_session_component.cc +++ b/base-hw/src/core/signal_session_component.cc @@ -13,7 +13,6 @@ /* Genode includes */ #include -#include #include /* 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(c); + return reinterpret_cap_cast(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(); diff --git a/base-hw/src/core/target.inc b/base-hw/src/core/target.inc index 3f528af96..9300d6a3e 100644 --- a/base-hw/src/core/target.inc +++ b/base-hw/src/core/target.inc @@ -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 \