From 611cd95eb3861490f415de614c745adcb8d06a99 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 16 Sep 2013 17:01:52 +0200 Subject: [PATCH] hw: destruct IPC end-nodes ref #589 --- base-hw/src/core/kernel/ipc_node.h | 146 +++++++++++++++++++++++------ base-hw/src/core/kernel/thread.h | 59 +++++++++--- 2 files changed, 166 insertions(+), 39 deletions(-) diff --git a/base-hw/src/core/kernel/ipc_node.h b/base-hw/src/core/kernel/ipc_node.h index 1824c1ab1..dcfc3ca7a 100644 --- a/base-hw/src/core/kernel/ipc_node.h +++ b/base-hw/src/core/kernel/ipc_node.h @@ -54,12 +54,13 @@ class Kernel::Ipc_node void * base; size_t size; - Ipc_node * origin; + Ipc_node * src; }; Message_fifo _request_queue; Message_buf _inbuf; Message_buf _outbuf; + Ipc_node * _outbuf_dst; State _state; /** @@ -75,7 +76,7 @@ class Kernel::Ipc_node /* fetch message */ Genode::memcpy(_inbuf.base, r->base, r->size); _inbuf.size = r->size; - _inbuf.origin = r->origin; + _inbuf.src = r->src; /* update state */ _state = PREPARE_REPLY; @@ -101,7 +102,7 @@ class Kernel::Ipc_node /* update state */ if (_state != PREPARE_AND_AWAIT_REPLY) { _state = INACTIVE; } else { _state = PREPARE_REPLY; } - _has_received(_inbuf.size); + _await_ipc_succeeded(_inbuf.size); } /** @@ -112,7 +113,7 @@ class Kernel::Ipc_node /* directly receive request if we've awaited it */ if (_state == AWAIT_REQUEST) { _receive_request(r); - _has_received(_inbuf.size); + _await_ipc_succeeded(_inbuf.size); return; } /* cannot receive yet, so queue request */ @@ -120,33 +121,97 @@ class Kernel::Ipc_node } /** - * IPC node waits for a message to receive to its inbuffer + * Cancel all requests in request queue */ - virtual void _awaits_receipt() = 0; + void _cancel_request_queue() + { + while (1) { + Message_buf * const r = _request_queue.dequeue(); + if (!r) { return; } + r->src->_outbuf_request_cancelled(); + } + } /** - * IPC node has received a message in its inbuffer - * - * \param s size of the message + * Cancel request in outgoing buffer */ - virtual void _has_received(size_t const s) = 0; + void _cancel_outbuf_request() + { + if (_outbuf_dst) { + _outbuf_dst->_announced_request_cancelled(&_outbuf); + _outbuf_dst = 0; + } + } + + /** + * Cancel request in incoming buffer + */ + void _cancel_inbuf_request() + { + if (_inbuf.src) { + _inbuf.src->_outbuf_request_cancelled(); + _inbuf.src = 0; + } + } + + /** + * A request 'r' in inbuf or request queue was cancelled by sender + */ + void _announced_request_cancelled(Message_buf * const r) + { + if (_inbuf.src == r->src) { + _inbuf.src = 0; + return; + } + _request_queue.remove(r); + } + + /** + * The request in the outbuf was cancelled by receiver + */ + void _outbuf_request_cancelled() + { + if (_outbuf_dst) { + _outbuf_dst = 0; + if (!_inbuf.src) { _state = INACTIVE; } + else { _state = PREPARE_REPLY; } + _await_ipc_failed(); + } + } + + /** + * IPC node received a request without waiting + */ + virtual void _received_ipc_request(size_t const s) = 0; + + /** + * IPC node started waiting for message receipt + */ + virtual void _await_ipc() = 0; + + /** + * IPC node returned from waiting due to message receipt + * + * \param s size of incoming message + */ + virtual void _await_ipc_succeeded(size_t const s) = 0; + + /** + * IPC node returned from waiting due to cancellation + */ + virtual void _await_ipc_failed() = 0; public: /** - * Construct an initially inactive IPC node + * Constructor */ Ipc_node() : _state(INACTIVE) { - _inbuf.size = 0; - _outbuf.size = 0; + _inbuf.src = 0; + _outbuf_dst = 0; } - /** - * Destructor - */ - virtual ~Ipc_node() { } - /** * Send a request and wait for the according reply * @@ -156,7 +221,7 @@ class Kernel::Ipc_node * \param inbuf_base base of the reply buffer * \param inbuf_size size of the reply buffer */ - void send_request_await_reply(Ipc_node * const dest, + void send_request_await_reply(Ipc_node * const dst, void * const req_base, size_t const req_size, void * const inbuf_base, @@ -168,19 +233,21 @@ class Kernel::Ipc_node /* prepare transmission of request message */ _outbuf.base = req_base; _outbuf.size = req_size; - _outbuf.origin = this; + _outbuf.src = this; + _outbuf_dst = dst; /* prepare reception of reply message */ _inbuf.base = inbuf_base; _inbuf.size = inbuf_size; + /* don't clear '_inbuf.origin' because we might prepare a reply */ /* update state */ if (_state != PREPARE_REPLY) { _state = AWAIT_REPLY; } else { _state = PREPARE_AND_AWAIT_REPLY; } - _awaits_receipt(); + _await_ipc(); /* announce request */ - dest->_announce_request(&_outbuf); + dst->_announce_request(&_outbuf); } /** @@ -198,16 +265,17 @@ class Kernel::Ipc_node /* prepare receipt of request */ _inbuf.base = inbuf_base; _inbuf.size = inbuf_size; + _inbuf.src = 0; /* if anybody already announced a request receive it */ if (!_request_queue.empty()) { _receive_request(_request_queue.dequeue()); - _has_received(_inbuf.size); + _received_ipc_request(_inbuf.size); return; } /* no request announced, so wait */ _state = AWAIT_REQUEST; - _awaits_receipt(); + _await_ipc(); } /** @@ -221,19 +289,41 @@ class Kernel::Ipc_node { /* reply to the last request if we have to */ if (_state == PREPARE_REPLY) { - _inbuf.origin->_receive_reply(reply_base, reply_size); + if (_inbuf.src) { + _inbuf.src->_receive_reply(reply_base, reply_size); + _inbuf.src = 0; + } _state = INACTIVE; } } /** - * Stop waiting for a receipt if in a waiting state + * Destructor + */ + ~Ipc_node() + { + _cancel_request_queue(); + _cancel_inbuf_request(); + _cancel_outbuf_request(); + } + + /** + * If IPC node waits, cancel '_outbuf' to stop waiting */ void cancel_waiting() { - if (_state == PREPARE_AND_AWAIT_REPLY) { _state = PREPARE_REPLY; } - if (_state == AWAIT_REPLY || _state == AWAIT_REQUEST) { + switch (_state) { + case AWAIT_REPLY: + _cancel_outbuf_request(); _state = INACTIVE; + _await_ipc_failed(); + return; + case PREPARE_AND_AWAIT_REPLY: + _cancel_outbuf_request(); + _state = PREPARE_REPLY; + _await_ipc_failed(); + return; + default: return; } } }; diff --git a/base-hw/src/core/kernel/thread.h b/base-hw/src/core/kernel/thread.h index 8f4e73295..daa5a7208 100644 --- a/base-hw/src/core/kernel/thread.h +++ b/base-hw/src/core/kernel/thread.h @@ -172,21 +172,12 @@ class Kernel::Thread ** Ipc_node ** **************/ - void _has_received(size_t const s) + void _received_ipc_request(size_t const s) { switch (_state) { - case AWAIT_IPC: - _schedule(); case SCHEDULED: user_arg_0(s); return; - case AWAIT_PAGER_IPC: - _schedule(); - return; - case AWAIT_PAGER: - /* pager replied before pagefault has been resolved */ - _state = AWAIT_RESUME; - return; default: PERR("wrong thread state to receive IPC"); crash(); @@ -194,7 +185,7 @@ class Kernel::Thread } } - void _awaits_receipt() + void _await_ipc() { switch (_state) { case SCHEDULED: @@ -209,6 +200,52 @@ class Kernel::Thread } } + void _await_ipc_succeeded(size_t const s) + { + switch (_state) { + case AWAIT_IPC: + _schedule(); + user_arg_0(s); + return; + case AWAIT_PAGER_IPC: + _schedule(); + return; + case AWAIT_PAGER: + _state = AWAIT_RESUME; + return; + default: + PERR("wrong thread state to receive IPC"); + crash(); + return; + } + } + + void _await_ipc_failed() + { + switch (_state) { + case AWAIT_IPC: + PERR("failed to receive IPC"); + crash(); + return; + case SCHEDULED: + PERR("failed to receive IPC"); + crash(); + return; + case AWAIT_PAGER_IPC: + PERR("failed to get pagefault resolved"); + crash(); + return; + case AWAIT_PAGER: + PERR("failed to get pagefault resolved"); + crash(); + return; + default: + PERR("wrong thread state to cancel IPC"); + crash(); + return; + } + } + /*************** ** Irq_owner **