From 438b8be2fa2b3fcad2a6addccb2df234865f54b3 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 18 Feb 2013 13:58:09 +0100 Subject: [PATCH] base-hw: destroy signal contexts, generic signal.h fix #641 --- base-hw/include/base/signal.h | 236 ------------------ base-hw/include/kernel/syscalls.h | 40 ++- base-hw/include/signal_session/client.h | 3 + .../include/signal_session/signal_session.h | 12 +- base-hw/lib/mk/base-common.mk | 2 +- base-hw/src/base/signal/signal.cc | 135 +++++++--- .../core/include/signal_session_component.h | 1 + base-hw/src/core/kernel.cc | 149 ++++++++--- base-hw/src/core/kernel/thread.h | 7 +- base-hw/src/core/signal_session_component.cc | 6 + 10 files changed, 276 insertions(+), 315 deletions(-) delete mode 100644 base-hw/include/base/signal.h diff --git a/base-hw/include/base/signal.h b/base-hw/include/base/signal.h deleted file mode 100644 index 834a0850b7..0000000000 --- a/base-hw/include/base/signal.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * \brief Delivery and reception of asynchronous notifications on HW-core - * \author Martin Stein - * \date 2012-05-05 - */ - -/* - * 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. - */ - -#ifndef _INCLUDE__BASE__SIGNAL_H__ -#define _INCLUDE__BASE__SIGNAL_H__ - -/* Genode includes */ -#include -#include -#include - -namespace Genode -{ - /** - * A bunch of asynchronuosly triggered events that target the same context - * - * Because a signal can trigger asynchronously at a context, - * the kernel accumulates them and provides them as such a bunch, - * once the receiver indicates that he is ready to receive. - */ - class Signal - { - unsigned _imprint; /* receiver-local signal-context pointer */ - int _num; /* how often this signal has been triggered */ - - public: - - /** - * Construct valid signal - */ - Signal(unsigned const imprint, int const num) : - _imprint(imprint), _num(num) { } - - /*************** - ** Accessors ** - ***************/ - - Signal_context * context() { return (Signal_context *)_imprint; } - - int num() { return _num; } - }; - - - typedef List Context_list; - - - /** - * A specific signal type that a transmitter can target when it submits - * - * One receiver might handle multiple signal contexts, - * but a signal context is owned by exactly one signal receiver. - */ - class Signal_context : public Context_list::Element - { - friend class Signal_receiver; - - Signal_receiver * _receiver; /* receiver that manages us */ - Lock _lock; /* serialize object access */ - Signal_context_capability _cap; /* holds the name of our context - * kernel-object as 'dst' */ - - public: - - /** - * Construct context that is not yet managed by a receiver - */ - Signal_context() : _receiver(0), _lock(Lock::UNLOCKED) { } - - /** - * Destructor - */ - virtual ~Signal_context() { } - - /* solely needed to enable one to type a capability with us */ - GENODE_RPC_INTERFACE(); - }; - - - /** - * To submit signals to one specific context - * - * Multiple transmitters can submit to the same context. - */ - class Signal_transmitter - { - /* names the targeted context kernel-object with its 'dst' field */ - Signal_context_capability _context; - - public: - - /** - * Constructor - */ - Signal_transmitter(Signal_context_capability const c = - Signal_context_capability()) - : _context(c) { } - - /** - * Trigger a signal 'num' times at the context we target - */ - void submit(int const num = 1) - { Kernel::submit_signal(_context.dst(), num); } - - /*************** - ** Accessors ** - ***************/ - - void context(Signal_context_capability const c) { _context = c; } - }; - - - /** - * Manage multiple signal contexts and receive signals targeted to them - */ - class Signal_receiver - { - Context_list _contexts; /* contexts that we manage */ - Lock _contexts_lock; /* serialize access to - * 'contexts' */ - Signal_receiver_capability _cap; /* holds name of our receiver - * kernel-object as 'dst' */ - - /** - * Let a context 'c' no longer be managed by us - * - * Doesn't serialize any member access. - */ - void _unsync_dissolve(Signal_context * const c); - - public: - - class Context_already_in_use : public Exception { }; - class Context_not_associated : public Exception { }; - - /** - * Constructor - */ - Signal_receiver(); - - /** - * Destructor - */ - ~Signal_receiver(); - - /** - * Let a context 'c' be managed by us - * - * \return Capability wich names the context kernel-object in its - * 'dst' field . Can be used as target for transmitters. - */ - Signal_context_capability manage(Signal_context * const c); - - /** - * If any of our signal contexts is pending - */ - bool pending(); - - /** - * Let a context 'c' no longer be managed by us - */ - void dissolve(Signal_context * const c); - - /** - * Block on any signal that is triggered at one of our contexts - */ - Signal wait_for_signal(); - }; - - - /** - * Abstract interface to be implemented by signal dispatchers - */ - struct Signal_dispatcher_base : Signal_context - { - virtual void dispatch(unsigned num) = 0; - }; - - - /** - * Adapter for directing signals to member functions - * - * This utility associates member functions with signals. It is intended to - * be used as a member variable of the class that handles incoming signals - * of a certain type. The constructor takes a pointer-to-member to the - * signal handling function as argument. If a signal is received at the - * common signal reception code, this function will be invoked by calling - * 'Signal_dispatcher_base::dispatch'. - * - * \param T type of signal-handling class - */ - template - class Signal_dispatcher : private Signal_dispatcher_base, - public Signal_context_capability - { - private: - - T &obj; - void (T::*member) (unsigned); - Signal_receiver &sig_rec; - - public: - - /** - * Constructor - * - * \param sig_rec signal receiver to associate the signal - * handler with - * \param obj,member object and member function to call when - * the signal occurs - */ - Signal_dispatcher(Signal_receiver &sig_rec, - T &obj, void (T::*member)(unsigned)) - : - Signal_context_capability(sig_rec.manage(this)), - obj(obj), member(member), - sig_rec(sig_rec) - { } - - ~Signal_dispatcher() { sig_rec.dissolve(this); } - - void dispatch(unsigned num) { (obj.*member)(num); } - }; -} - -#endif /* _INCLUDE__BASE__SIGNAL_H__ */ - diff --git a/base-hw/include/kernel/syscalls.h b/base-hw/include/kernel/syscalls.h index 1df16066a4..49ffdadd07 100644 --- a/base-hw/include/kernel/syscalls.h +++ b/base-hw/include/kernel/syscalls.h @@ -71,9 +71,11 @@ namespace Kernel /* asynchronous signalling */ NEW_SIGNAL_RECEIVER = 20, NEW_SIGNAL_CONTEXT = 21, + KILL_SIGNAL_CONTEXT = 30, AWAIT_SIGNAL = 22, SUBMIT_SIGNAL = 23, SIGNAL_PENDING = 27, + ACK_SIGNAL = 29, /* vm specific */ NEW_VM = 24, @@ -455,13 +457,14 @@ namespace Kernel * * \param receiver_id ID of the targeted receiver kernel-object * - * When this call returns, an instance of 'Signal' is located at the base - * of the callers UTCB. It holds information about wich context was - * triggered how often. It is 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 trigger simultanously there is no assertion about - * wich thread receives the 'Signal' instance of wich 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'. */ inline void await_signal(unsigned receiver_id) { syscall(AWAIT_SIGNAL, (Syscall_arg)receiver_id); } @@ -485,6 +488,29 @@ namespace Kernel inline void submit_signal(unsigned context_id, int num) { syscall(SUBMIT_SIGNAL, (Syscall_arg)context_id, (Syscall_arg)num); } + /** + * Acknowledge the processing of the last signal 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. + */ + inline void ack_signal(unsigned context_id) { + syscall(ACK_SIGNAL, (Syscall_arg)context_id); } + + /** + * Destruct a signal context + * + * \param context_id kernel name of the targeted signal context + * + * 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. + */ + inline void kill_signal_context(unsigned context_id) { + syscall(KILL_SIGNAL_CONTEXT, (Syscall_arg)context_id); } /** * 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 5a8e4f6409..9bc93ba48f 100644 --- a/base-hw/include/signal_session/client.h +++ b/base-hw/include/signal_session/client.h @@ -45,6 +45,9 @@ namespace Genode alloc_context(Signal_receiver_capability const r, unsigned const imprint) { return call(r, imprint); } + + 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 c4a109bd24..fbaddfbd9a 100644 --- a/base-hw/include/signal_session/signal_session.h +++ b/base-hw/include/signal_session/signal_session.h @@ -78,6 +78,13 @@ namespace Genode alloc_context(Signal_receiver_capability const r, unsigned const imprint) = 0; + /** + * Free signal-context + * + * \param cap capability of signal-context to release + */ + virtual void free_context(Signal_context_capability cap) = 0; + /********************* ** RPC declaration ** *********************/ @@ -87,8 +94,11 @@ namespace Genode GENODE_RPC_THROW(Rpc_alloc_context, Signal_context_capability, alloc_context, GENODE_TYPE_LIST(Out_of_metadata), Signal_receiver_capability, unsigned); + GENODE_RPC(Rpc_free_context, void, free_context, + Signal_context_capability); - GENODE_RPC_INTERFACE(Rpc_alloc_receiver, Rpc_alloc_context); + GENODE_RPC_INTERFACE(Rpc_alloc_receiver, Rpc_alloc_context, + Rpc_free_context); }; } diff --git a/base-hw/lib/mk/base-common.mk b/base-hw/lib/mk/base-common.mk index c45b6a0003..8bd793f72e 100644 --- a/base-hw/lib/mk/base-common.mk +++ b/base-hw/lib/mk/base-common.mk @@ -16,7 +16,7 @@ SRC_CC += process/process.cc SRC_CC += elf/elf_binary.cc SRC_CC += console/console.cc SRC_CC += lock/lock.cc -SRC_CC += signal/signal.cc +SRC_CC += signal/signal.cc signal/common.cc SRC_CC += server/server.cc server/common.cc SRC_CC += thread/thread_bootstrap_empty.cc diff --git a/base-hw/src/base/signal/signal.cc b/base-hw/src/base/signal/signal.cc index e66e688e6c..01fdd1481a 100644 --- a/base-hw/src/base/signal/signal.cc +++ b/base-hw/src/base/signal/signal.cc @@ -30,11 +30,60 @@ static Signal_connection * signal_connection() } +/************ + ** Signal ** + ************/ + +void Signal::_dec_ref_and_unlock() +{ + if (_data.context) { + Lock::Guard lock_guard(_data.context->_lock); + _data.context->_ref_cnt--; + + /* + * We must ack a signal context to receive the next one, + * so new signals are received only when ref_cnt = 0. + */ + if (_data.context->_ref_cnt == 0) + Kernel::ack_signal(_data.context->_cap.dst()); + } +} + + +void Signal::_inc_ref() +{ + if (_data.context) { + Lock::Guard lock_guard(_data.context->_lock); + _data.context->_ref_cnt++; + } +} + + +Signal::Signal(Signal::Data data) : _data(data) +{ + /* + * We assume that a kernel signal-context doesn't deliver + * multiple signals simultaneously. + */ + if (_data.context) _data.context->_ref_cnt = 1; +} + + +/************************ + ** Signal transmitter ** + ************************/ + +void Signal_transmitter::submit(unsigned cnt) +{ + /* submits to invalid signal contexts get ignored */ + Kernel::submit_signal(_context.dst(), cnt); +} + + /********************* ** Signal_receiver ** *********************/ - Signal_receiver::Signal_receiver() { /* create a kernel object that corresponds to the receiver */ @@ -59,16 +108,27 @@ Signal_receiver::Signal_receiver() } -Signal_receiver::~Signal_receiver() +void Signal_receiver::_unsynchronized_dissolve(Signal_context * c) { - /* dissolve all contexts that are managed by us */ - Lock::Guard contexts_guard(_contexts_lock); - while (1) { - Signal_context * const c = _contexts.first(); - if (!c) break; - Lock::Guard context_guard(c->_lock); - _unsync_dissolve(c); - } + /* + * We first destroy the kernel object. This also ensures + * that no delivered but unacked signals of this context exist + * in userland anymore. + */ + Kernel::kill_signal_context(c->_cap.dst()); + + /* + * Now we can tell core to regain the memory of the + * destructed kernel object. + */ + signal_connection()->free_context(c->_cap); + + /* reset the context */ + c->_receiver = 0; + c->_cap = Signal_context_capability(); + + /* forget the context */ + _contexts.remove(&c->_receiver_le); } @@ -97,17 +157,40 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c) } /* assign the context to us */ c->_receiver = this; - _contexts.insert(c); + _contexts.insert(&c->_receiver_le); return c->_cap; } +void Signal_receiver::dissolve(Signal_context *context) +{ + if (context->_receiver != this) + throw Context_not_associated(); + + Lock::Guard list_lock_guard(_contexts_lock); + + _unsynchronized_dissolve(context); + + /* + * We assume that dissolve is always called before the context destructor. + * On other platforms a 'context->_destroy_lock' is locked and unlocked at + * this point to block until all remaining signals of this context get + * destructed and prevent the context from beeing destructed to early. + * However on this platform we don't have to wait because + * 'kill_signal_context' in '_unsynchronized_dissolve' already does it. + */ +} + + +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 *)Thread_base::myself()->utcb(); - Signal_context * c = s.context(); + 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); @@ -121,28 +204,6 @@ Signal Signal_receiver::wait_for_signal() } -bool Signal_receiver::pending() { return Kernel::signal_pending(_cap.dst()); } - - -void Signal_receiver::dissolve(Signal_context * const c) -{ - /* check if the context is managed by us */ - Lock::Guard contexts_guard(_contexts_lock); - Lock::Guard context_guard(c->_lock); - if (c->_receiver != this) throw Context_not_associated(); - - /* unassign the context */ - _unsync_dissolve(c); -} - - -void Signal_receiver::_unsync_dissolve(Signal_context * const c) -{ - /* reset the context */ - c->_receiver = 0; - c->_cap = Signal_context_capability(); - - /* forget the context */ - _contexts.remove(c); -} +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 a12c7d349b..e11129d445 100644 --- a/base-hw/src/core/include/signal_session_component.h +++ b/base-hw/src/core/include/signal_session_component.h @@ -61,6 +61,7 @@ namespace Genode Signal_context_capability alloc_context(Signal_receiver_capability const r, unsigned const imprint); + void free_context(Signal_context_capability context_cap); }; } diff --git a/base-hw/src/core/kernel.cc b/base-hw/src/core/kernel.cc index a97b5feff8..d595e60ab5 100644 --- a/base-hw/src/core/kernel.cc +++ b/base-hw/src/core/kernel.cc @@ -487,9 +487,9 @@ void Kernel::Thread::await_signal() } -void Kernel::Thread::receive_signal(Signal const s) +void Kernel::Thread::received_signal() { - *(Signal *)phys_utcb()->base() = s; + assert(_state == AWAIT_IRQ); _activate(); } @@ -554,25 +554,52 @@ namespace Kernel { friend class Signal_receiver; - Signal_receiver * const _receiver; /* the receiver that owns us */ - unsigned const _imprint; /* every of our signals gets signed with */ - unsigned _number; /* how often we got triggered */ + Signal_receiver * const _receiver; /* the receiver that owns us */ + unsigned const _imprint; /* every outgoing signals gets + * signed with this */ + unsigned _submits; /* accumul. undelivered submits */ + bool _await_ack; /* delivery ack pending */ + Thread * _killer; /* awaits our destruction if >0 */ + + /** + * Utility to deliver all remaining submits + */ + void _deliver(); public: /** * Constructor */ - Signal_context(Signal_receiver * const r, - unsigned const imprint) - : _receiver(r), _imprint(imprint), _number(0) { } + Signal_context(Signal_receiver * const r, unsigned const imprint) : + _receiver(r), _imprint(imprint), + _submits(0), _await_ack(0), _killer(0) { } /** - * Trigger this context + * Submit the signal * - * \param number how often this call triggers us at once + * \param n number of submits */ - void trigger_signal(unsigned const number); + void submit(unsigned const n); + + /** + * Acknowledge delivery of signal + */ + void ack(); + + /** + * Destruct or prepare to do it at next call of 'ack' + */ + void kill(Thread * const killer) + { + assert(!_killer); + _killer = killer; + if (_await_ack) { + _killer->kill_signal_context_blocks(); + return; + } + this->~Signal_context(); + } }; /** @@ -602,10 +629,13 @@ namespace Kernel } /* awake a listener and transmit signal info to it */ Thread * const t = _listeners.dequeue(); - t->receive_signal(Signal(c->_imprint, c->_number)); + Signal::Data data((Genode::Signal_context *)c->_imprint, + c->_submits); + *(Signal::Data *)t->phys_utcb()->base() = data; + t->received_signal(); /* reset context */ - c->_number = 0; + c->_submits = 0; } } @@ -627,12 +657,12 @@ namespace Kernel bool pending() { return !_pending_contexts.empty(); } /** - * Recognize that one of our contexts was triggered + * Recognize that 'c' wants to deliver */ - void add_pending_context(Signal_context * const c) + void deliver(Signal_context * const c) { assert(c->_receiver == this); - if(!c->is_enqueued()) _pending_contexts.enqueue(c); + if (!c->is_enqueued()) _pending_contexts.enqueue(c); _listen(); } }; @@ -674,7 +704,7 @@ namespace Kernel return; default: cpu_scheduler()->remove(this); - _context->trigger_signal(1); + _context->submit(1); } } @@ -1193,13 +1223,38 @@ namespace Kernel /* lookup context */ Signal_context * const c = Signal_context::pool()->object(user->user_arg_1()); - assert(c); - + if(!c) { + PDBG("invalid signal-context capability"); + return; + } /* trigger signal at context */ - c->trigger_signal(user->user_arg_2()); + c->submit(user->user_arg_2()); } + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_ack_signal(Thread * const user) + { + Signal_context * const c = + Signal_context::pool()->object(user->user_arg_1()); + assert(c); + c->ack(); + } + + + /** + * Do specific syscall for 'user', for details see 'syscall.h' + */ + void do_kill_signal_context(Thread * const user) + { + Signal_context * const c = + Signal_context::pool()->object(user->user_arg_1()); + assert(c); + c->kill(user); + } + /** * Do specific syscall for 'user', for details see 'syscall.h' */ @@ -1283,6 +1338,8 @@ namespace Kernel /* 26 */ do_delete_thread, /* 27 */ do_signal_pending, /* 28 */ do_resume_faulter, + /* 29 */ do_ack_signal, + /* 30 */ do_kill_signal_context, }; enum { MAX_SYSCALL = sizeof(handle_sysc)/sizeof(handle_sysc[0]) - 1 }; @@ -1385,8 +1442,7 @@ int Thread::start(void *ip, void *sp, unsigned cpu_no, user_arg_0((unsigned)_virt_utcb); /* start thread */ - cpu_scheduler()->insert(this); - _state = ACTIVE; + _activate(); return 0; } @@ -1420,19 +1476,50 @@ void Thread::pagefault(addr_t const va, bool const w) } +void Thread::kill_signal_context_blocks() +{ + cpu_scheduler()->remove(this); + _state = KILL_SIGNAL_CONTEXT_BLOCKS; +} + + +void Thread::kill_signal_context_done() +{ + assert(_state == KILL_SIGNAL_CONTEXT_BLOCKS) + _activate(); +} + + /**************************** ** Kernel::Signal_context ** ****************************/ - -void Signal_context::trigger_signal(unsigned const number) +void Signal_context::_deliver() { - /* raise our number */ - unsigned const old_nr = _number; - _number += number; - assert(old_nr <= _number); - - /* notify our receiver */ - if (_number) _receiver->add_pending_context(this); + if (!_submits) return; + _receiver->deliver(this); + _await_ack = 1; +} + + +void Signal_context::ack() +{ + _await_ack = 0; + if (!_killer) { + _deliver(); + return; + } + _killer->kill_signal_context_done(); + this->~Signal_context(); +} + + +void Signal_context::submit(unsigned const n) +{ + assert(_submits < -1 - n); + if (_killer) return; + _submits += n; + if (_await_ack) return; + _deliver(); } diff --git a/base-hw/src/core/kernel/thread.h b/base-hw/src/core/kernel/thread.h index 58604af287..1ef64829bf 100644 --- a/base-hw/src/core/kernel/thread.h +++ b/base-hw/src/core/kernel/thread.h @@ -775,7 +775,7 @@ namespace Kernel public Irq_owner { enum State { STOPPED, ACTIVE, AWAIT_IPC, AWAIT_RESUMPTION, - AWAIT_IRQ, AWAIT_SIGNAL }; + AWAIT_IRQ, AWAIT_SIGNAL, KILL_SIGNAL_CONTEXT_BLOCKS }; Platform_thread * const _platform_thread; /* userland object wich * addresses this thread */ @@ -897,7 +897,7 @@ namespace Kernel /** * Gets called when we have received a signal at a signal receiver */ - void receive_signal(Signal const s); + void received_signal(); /** * Handle the exception that currently blocks this thread @@ -909,6 +909,9 @@ namespace Kernel */ void scheduled_next(); + void kill_signal_context_blocks(); + + void kill_signal_context_done(); /*************** ** Accessors ** diff --git a/base-hw/src/core/signal_session_component.cc b/base-hw/src/core/signal_session_component.cc index f95471ea1e..90bbf539e2 100644 --- a/base-hw/src/core/signal_session_component.cc +++ b/base-hw/src/core/signal_session_component.cc @@ -77,3 +77,9 @@ Signal_session_component::alloc_context(Signal_receiver_capability r, return reinterpret_cap_cast(c); } +/** + * FIXME should regain the kernel-object memory from kernel + */ +void Signal_session_component::free_context(Signal_context_capability cap) { + PDBG("Not implemented"); } +