genode/repos/base-hw/src/core/include/kernel/ipc_node.h

378 lines
8.4 KiB
C
Raw Normal View History

2013-09-05 13:40:54 +02:00
/*
* \brief Backend for end points of synchronous interprocess communication
2013-09-05 13:40:54 +02:00
* \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.
*/
#ifndef _KERNEL__IPC_NODE_H_
#define _KERNEL__IPC_NODE_H_
/* core includes */
#include <kernel/fifo.h>
2013-09-05 13:40:54 +02:00
#include <assert.h>
namespace Kernel
{
/**
* Backend for end points of synchronous interprocess communication
2013-09-05 13:40:54 +02:00
*/
class Ipc_node;
}
class Kernel::Ipc_node
{
protected:
2013-09-05 13:40:54 +02:00
enum State
{
INACTIVE = 1,
AWAIT_REPLY = 2,
AWAIT_REQUEST = 3,
PREPARE_REPLY = 4,
2013-09-05 13:40:54 +02:00
PREPARE_AND_AWAIT_REPLY = 5,
};
private:
class Message_buf;
typedef Kernel::Fifo<Message_buf> Message_fifo;
2013-09-05 13:40:54 +02:00
/**
* Describes the buffer for incoming or outgoing messages
*/
class Message_buf : public Message_fifo::Element
2013-09-05 13:40:54 +02:00
{
public:
void * base;
size_t size;
2013-09-16 17:01:52 +02:00
Ipc_node * src;
2013-09-05 13:40:54 +02:00
};
Message_fifo _request_queue;
Message_buf _inbuf;
Message_buf _outbuf;
2013-09-16 17:01:52 +02:00
Ipc_node * _outbuf_dst;
bool _outbuf_dst_help;
State _state;
2013-09-05 13:40:54 +02:00
/**
* Buffer next request from request queue in 'r' to handle it
*/
void _receive_request(Message_buf * const r)
{
/* FIXME: invalid requests should be discarded */
if (r->size > _inbuf.size) {
PWRN("oversized request");
r->size = _inbuf.size;
2013-09-05 13:40:54 +02:00
}
/* fetch message */
Genode::memcpy(_inbuf.base, r->base, r->size);
_inbuf.size = r->size;
2013-09-16 17:01:52 +02:00
_inbuf.src = r->src;
2013-09-05 13:40:54 +02:00
/* update state */
_state = PREPARE_REPLY;
2013-09-05 13:40:54 +02:00
}
/**
* Receive a given reply if one is expected
*
* \param base base of the reply payload
* \param size size of the reply payload
*/
void _receive_reply(void * const base, size_t const size)
{
/* FIXME: when discard awaited replies userland must get a hint */
if (size > _inbuf.size) {
2013-09-05 13:40:54 +02:00
PDBG("discard invalid IPC reply");
return;
}
/* receive reply */
Genode::memcpy(_inbuf.base, base, size);
_inbuf.size = size;
/* update state */
if (_state != PREPARE_AND_AWAIT_REPLY) { _state = INACTIVE; }
else { _state = PREPARE_REPLY; }
_send_request_succeeded();
2013-09-05 13:40:54 +02:00
}
/**
* Insert 'r' into request queue, buffer it if we were waiting for it
*/
void _announce_request(Message_buf * const r)
{
/* directly receive request if we've awaited it */
if (_state == AWAIT_REQUEST) {
_receive_request(r);
_await_request_succeeded();
2013-09-05 13:40:54 +02:00
return;
}
/* cannot receive yet, so queue request */
_request_queue.enqueue(r);
}
/**
2013-09-16 17:01:52 +02:00
* Cancel all requests in request queue
2013-09-05 13:40:54 +02:00
*/
2013-09-16 17:01:52 +02:00
void _cancel_request_queue()
{
while (1) {
Message_buf * const r = _request_queue.dequeue();
if (!r) { return; }
r->src->_outbuf_request_cancelled();
}
}
2013-09-05 13:40:54 +02:00
/**
2013-09-16 17:01:52 +02:00
* Cancel request in outgoing buffer
2013-09-05 13:40:54 +02:00
*/
2013-09-16 17:01:52 +02:00
void _cancel_outbuf_request()
{
if (_outbuf_dst) {
_outbuf_dst->_announced_request_cancelled(&_outbuf);
_outbuf_dst = 0;
}
}
2013-09-05 13:40:54 +02:00
2013-09-16 17:01:52 +02:00
/**
* Cancel request in incoming buffer
*/
void _cancel_inbuf_request()
{
if (_inbuf.src) {
_inbuf.src->_outbuf_request_cancelled();
_inbuf.src = 0;
}
}
2013-09-05 13:40:54 +02:00
/**
2013-09-16 17:01:52 +02:00
* A request 'r' in inbuf or request queue was cancelled by sender
2013-09-05 13:40:54 +02:00
*/
2013-09-16 17:01:52 +02:00
void _announced_request_cancelled(Message_buf * const r)
2013-09-05 13:40:54 +02:00
{
2013-09-16 17:01:52 +02:00
if (_inbuf.src == r->src) {
_inbuf.src = 0;
return;
}
_request_queue.remove(r);
2013-09-05 13:40:54 +02:00
}
/**
2013-09-16 17:01:52 +02:00
* 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; }
_send_request_failed();
2013-09-16 17:01:52 +02:00
}
}
/**
* Return wether we are the source of a helping relationship
*/
bool _helps_outbuf_dst()
{
return (_state == PREPARE_AND_AWAIT_REPLY ||
_state == AWAIT_REPLY) && _outbuf_dst_help;
}
2013-09-16 17:01:52 +02:00
/**
* IPC node returned from waiting due to reply receipt
2013-09-16 17:01:52 +02:00
*/
virtual void _send_request_succeeded() = 0;
2013-09-16 17:01:52 +02:00
/**
* IPC node returned from waiting due to reply cancellation
2013-09-16 17:01:52 +02:00
*/
virtual void _send_request_failed() = 0;
/**
* IPC node returned from waiting due to request receipt
*/
virtual void _await_request_succeeded() = 0;
/**
* IPC node returned from waiting due to request cancellation
*/
virtual void _await_request_failed() = 0;
2013-09-16 17:01:52 +02:00
protected:
/***************
** Accessors **
***************/
Ipc_node * outbuf_dst() { return _outbuf_dst; }
State state() { return _state; }
2013-09-16 17:01:52 +02:00
public:
/**
* Constructor
*/
Ipc_node() : _state(INACTIVE)
{
_inbuf.src = 0;
_outbuf_dst = 0;
}
2013-09-05 13:40:54 +02:00
/**
* Send a request and wait for the according reply
*
* \param dst targeted IPC node
* \param buf_base base of receive buffer and request message
* \param buf_size size of receive buffer
* \param msg_size size of request message
* \param help wether the request implies a helping relationship
2013-09-05 13:40:54 +02:00
*/
void send_request(Ipc_node * const dst, void * const buf_base,
size_t const buf_size, size_t const msg_size,
bool help)
2013-09-05 13:40:54 +02:00
{
/* assertions */
assert(_state == INACTIVE || _state == PREPARE_REPLY);
/* prepare transmission of request message */
_outbuf.base = buf_base;
_outbuf.size = msg_size;
_outbuf.src = this;
_outbuf_dst = dst;
_outbuf_dst_help = 0;
/*
* Prepare reception of reply message but don't clear
* '_inbuf.origin' because we might also prepare a reply.
*/
_inbuf.base = buf_base;
_inbuf.size = buf_size;
2013-09-05 13:40:54 +02:00
/* update state */
if (_state != PREPARE_REPLY) { _state = AWAIT_REPLY; }
else { _state = PREPARE_AND_AWAIT_REPLY; }
/* announce request */
2013-09-16 17:01:52 +02:00
dst->_announce_request(&_outbuf);
/* set help relation after announcement to simplify scheduling */
_outbuf_dst_help = help;
}
/**
* Return root destination of the helping-relation tree we are in
*/
Ipc_node * helping_sink() {
return _helps_outbuf_dst() ? _outbuf_dst->helping_sink() : this; }
/**
* Call function 'f' of type 'void (Ipc_node *)' for each helper
*/
template <typename F> void for_each_helper(F f)
{
/* if we have a helper in the receive buffer, call 'f' for it */
if (_state == PREPARE_REPLY || _state == PREPARE_AND_AWAIT_REPLY) {
if (_inbuf.src->_outbuf_dst_help) { f(_inbuf.src); } }
/* call 'f' for each helper in our request queue */
_request_queue.for_each([f] (Message_buf * const b) {
if (b->src->_outbuf_dst_help) { f(b->src); } });
2013-09-05 13:40:54 +02:00
}
/**
* Wait until a request has arrived and load it for handling
*
* \param buf_base base of receive buffer
* \param buf_size size of receive buffer
*
* \return wether a request could be received already
2013-09-05 13:40:54 +02:00
*/
bool await_request(void * const buf_base,
size_t const buf_size)
2013-09-05 13:40:54 +02:00
{
/* assertions */
assert(_state == INACTIVE);
/* prepare receipt of request */
_inbuf.base = buf_base;
_inbuf.size = buf_size;
2013-09-16 17:01:52 +02:00
_inbuf.src = 0;
2013-09-05 13:40:54 +02:00
/* if anybody already announced a request receive it */
if (!_request_queue.empty()) {
_receive_request(_request_queue.dequeue());
return true;
2013-09-05 13:40:54 +02:00
}
/* no request announced, so wait */
_state = AWAIT_REQUEST;
return false;
2013-09-05 13:40:54 +02:00
}
/**
* Reply to last request if there's any
*
* \param msg_base base of reply message
* \param msg_size size of reply message
2013-09-05 13:40:54 +02:00
*/
void send_reply(void * const msg_base,
size_t const msg_size)
2013-09-05 13:40:54 +02:00
{
/* reply to the last request if we have to */
if (_state == PREPARE_REPLY) {
2013-09-16 17:01:52 +02:00
if (_inbuf.src) {
_inbuf.src->_receive_reply(msg_base, msg_size);
2013-09-16 17:01:52 +02:00
_inbuf.src = 0;
}
2013-09-05 13:40:54 +02:00
_state = INACTIVE;
}
}
/**
2013-09-16 17:01:52 +02:00
* Destructor
*/
~Ipc_node()
{
_cancel_request_queue();
_cancel_inbuf_request();
_cancel_outbuf_request();
}
/**
* If IPC node waits, cancel '_outbuf' to stop waiting
2013-09-05 13:40:54 +02:00
*/
void cancel_waiting()
{
2013-09-16 17:01:52 +02:00
switch (_state) {
case AWAIT_REPLY:
_cancel_outbuf_request();
2013-09-05 13:40:54 +02:00
_state = INACTIVE;
_send_request_failed();
2013-09-16 17:01:52 +02:00
return;
case AWAIT_REQUEST:
_state = INACTIVE;
_await_request_failed();
return;
2013-09-16 17:01:52 +02:00
case PREPARE_AND_AWAIT_REPLY:
_cancel_outbuf_request();
_state = PREPARE_REPLY;
_send_request_failed();
2013-09-16 17:01:52 +02:00
return;
default: return;
2013-09-05 13:40:54 +02:00
}
}
};
#endif /* _KERNEL__IPC_NODE_H_ */