2013-09-05 13:40:54 +02:00
|
|
|
/*
|
2013-09-13 01:06: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_
|
|
|
|
|
|
|
|
/* Genode includes */
|
|
|
|
#include <util/fifo.h>
|
|
|
|
|
|
|
|
/* core includes */
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
namespace Kernel
|
|
|
|
{
|
|
|
|
/**
|
2013-09-13 01:06:54 +02:00
|
|
|
* Backend for end points of synchronous interprocess communication
|
2013-09-05 13:40:54 +02:00
|
|
|
*/
|
|
|
|
class Ipc_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Kernel::Ipc_node
|
|
|
|
{
|
2013-11-28 00:44:54 +01:00
|
|
|
protected:
|
2013-09-05 13:40:54 +02:00
|
|
|
|
|
|
|
enum State
|
|
|
|
{
|
2013-09-19 15:14:04 +02:00
|
|
|
INACTIVE = 1,
|
|
|
|
AWAIT_REPLY = 2,
|
|
|
|
AWAIT_REQUEST = 3,
|
|
|
|
PREPARE_REPLY = 4,
|
2013-09-05 13:40:54 +02:00
|
|
|
PREPARE_AND_AWAIT_REPLY = 5,
|
|
|
|
};
|
|
|
|
|
2013-11-28 00:44:54 +01:00
|
|
|
private:
|
|
|
|
|
|
|
|
class Message_buf;
|
|
|
|
|
|
|
|
typedef Genode::Fifo<Message_buf> Message_fifo;
|
|
|
|
|
2013-09-05 13:40:54 +02:00
|
|
|
/**
|
|
|
|
* Describes the buffer for incoming or outgoing messages
|
|
|
|
*/
|
2013-09-13 01:06:54 +02:00
|
|
|
class Message_buf : public Message_fifo::Element
|
2013-09-05 13:40:54 +02:00
|
|
|
{
|
2013-09-13 01:06: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
|
|
|
};
|
|
|
|
|
2013-09-13 01:06: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;
|
2013-09-13 01:06:54 +02:00
|
|
|
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) {
|
2014-03-12 16:23:01 +01:00
|
|
|
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 */
|
2013-09-13 01:06:54 +02:00
|
|
|
_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 */
|
2013-09-13 01:06:54 +02:00
|
|
|
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; }
|
2014-03-27 12:32:55 +01:00
|
|
|
_await_ipc_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);
|
2014-03-27 12:32:55 +01:00
|
|
|
_await_ipc_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; }
|
2013-10-17 13:51:17 +02:00
|
|
|
_await_ipc_failed();
|
2013-09-16 17:01:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* IPC node returned from waiting due to message receipt
|
|
|
|
*/
|
2014-03-27 12:32:55 +01:00
|
|
|
virtual void _await_ipc_succeeded() = 0;
|
2013-09-16 17:01:52 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* IPC node returned from waiting due to cancellation
|
|
|
|
*/
|
2013-10-17 13:51:17 +02:00
|
|
|
virtual void _await_ipc_failed() = 0;
|
2013-09-16 17:01:52 +02:00
|
|
|
|
2013-11-28 00:44:54 +01: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 dest targeted IPC node
|
|
|
|
* \param req_base base of the request payload
|
|
|
|
* \param req_size size of the request payload
|
|
|
|
* \param inbuf_base base of the reply buffer
|
|
|
|
* \param inbuf_size size of the reply buffer
|
|
|
|
*/
|
2013-09-16 17:01:52 +02:00
|
|
|
void send_request_await_reply(Ipc_node * const dst,
|
2013-09-05 13:40:54 +02:00
|
|
|
void * const req_base,
|
|
|
|
size_t const req_size,
|
|
|
|
void * const inbuf_base,
|
|
|
|
size_t const inbuf_size)
|
|
|
|
{
|
|
|
|
/* assertions */
|
|
|
|
assert(_state == INACTIVE || _state == PREPARE_REPLY);
|
|
|
|
|
|
|
|
/* prepare transmission of request message */
|
|
|
|
_outbuf.base = req_base;
|
|
|
|
_outbuf.size = req_size;
|
2013-09-16 17:01:52 +02:00
|
|
|
_outbuf.src = this;
|
|
|
|
_outbuf_dst = dst;
|
2013-09-05 13:40:54 +02:00
|
|
|
|
|
|
|
/* prepare reception of reply message */
|
|
|
|
_inbuf.base = inbuf_base;
|
|
|
|
_inbuf.size = inbuf_size;
|
2013-09-16 17:01:52 +02:00
|
|
|
/* don't clear '_inbuf.origin' because we might prepare a reply */
|
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);
|
2013-09-05 13:40:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait until a request has arrived and load it for handling
|
|
|
|
*
|
|
|
|
* \param inbuf_base base of the request buffer
|
|
|
|
* \param inbuf_size size of the request buffer
|
2014-03-27 12:20:55 +01:00
|
|
|
*
|
|
|
|
* \return wether a request could be received already
|
2013-09-05 13:40:54 +02:00
|
|
|
*/
|
2014-03-27 12:20:55 +01:00
|
|
|
bool await_request(void * const inbuf_base,
|
2013-09-05 13:40:54 +02:00
|
|
|
size_t const inbuf_size)
|
|
|
|
{
|
|
|
|
/* assertions */
|
|
|
|
assert(_state == INACTIVE);
|
|
|
|
|
|
|
|
/* prepare receipt of request */
|
|
|
|
_inbuf.base = inbuf_base;
|
|
|
|
_inbuf.size = inbuf_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());
|
2014-03-27 12:20:55 +01:00
|
|
|
return true;
|
2013-09-05 13:40:54 +02:00
|
|
|
}
|
|
|
|
/* no request announced, so wait */
|
|
|
|
_state = AWAIT_REQUEST;
|
2014-03-27 12:20:55 +01:00
|
|
|
return false;
|
2013-09-05 13:40:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reply to last request if there's any
|
|
|
|
*
|
|
|
|
* \param reply_base base of the reply payload
|
|
|
|
* \param reply_size size of the reply payload
|
|
|
|
*/
|
|
|
|
void send_reply(void * const reply_base,
|
|
|
|
size_t const reply_size)
|
|
|
|
{
|
|
|
|
/* 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(reply_base, reply_size);
|
|
|
|
_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;
|
2013-10-17 13:51:17 +02:00
|
|
|
_await_ipc_failed();
|
2013-09-16 17:01:52 +02:00
|
|
|
return;
|
2013-09-19 15:14:04 +02:00
|
|
|
case AWAIT_REQUEST:
|
|
|
|
_state = INACTIVE;
|
2013-10-17 13:51:17 +02:00
|
|
|
_await_ipc_failed();
|
2013-09-19 15:14:04 +02:00
|
|
|
return;
|
2013-09-16 17:01:52 +02:00
|
|
|
case PREPARE_AND_AWAIT_REPLY:
|
|
|
|
_cancel_outbuf_request();
|
|
|
|
_state = PREPARE_REPLY;
|
2013-10-17 13:51:17 +02:00
|
|
|
_await_ipc_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_ */
|