base-hw: no PD code / virt methods in Ipc_node

This is a simplication of the asyncronous-IPC module of the base-hw kernel.
Besides structuring the code in a cleaner way, it prepares for the in-place
translation of the module into Ada in the context of the Spunky project.

* Get rid of virtual methods in Ipc_node.
* Move all stuff related to protection domains, capabilities, and UTCBs to
  the Thread class. this code might later be moved to a dedicated module, but
  for now it's just fine to have it done by the thread module.

Ref #3308
This commit is contained in:
Martin Stein 2019-04-19 18:41:14 +02:00 committed by Christian Helmuth
parent 4b3c40f35b
commit 6a5aa18a7b
4 changed files with 199 additions and 189 deletions

View File

@ -19,74 +19,16 @@
#include <base/internal/native_utcb.h>
/* core includes */
#include <platform_pd.h>
#include <kernel/ipc_node.h>
#include <kernel/pd.h>
#include <kernel/kernel.h>
#include <kernel/thread.h>
using namespace Kernel;
static inline void free_obj_id_ref(Pd &pd, void *ptr)
{
pd.platform_pd().capability_slab().free(ptr, sizeof(Object_identity_reference));
}
void Ipc_node::copy_msg(Ipc_node &sender)
{
using namespace Genode;
using Reference = Object_identity_reference;
/* copy payload and set destination capability id */
*_utcb = *sender._utcb;
_utcb->destination(sender._capid);
/* translate capabilities */
for (unsigned i = 0; i < _rcv_caps; i++) {
capid_t id = sender._utcb->cap_get(i);
/* if there is no capability to send, just free the pre-allocation */
if (i >= sender._utcb->cap_cnt()) {
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
continue;
}
/* lookup the capability id within the caller's cap space */
Reference *oir = (id == cap_id_invalid())
? nullptr : sender.pd().cap_tree().find(id);
/* if the caller's capability is invalid, free the pre-allocation */
if (!oir) {
_utcb->cap_add(cap_id_invalid());
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
continue;
}
/* lookup the capability id within the callee's cap space */
Reference *dst_oir = oir->find(pd());
/* if it is not found, and the target is not core, create a reference */
if (!dst_oir && (&pd() != &core_pd())) {
dst_oir = oir->factory(_obj_id_ref_ptr[i], pd());
if (!dst_oir)
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
} else /* otherwise free the pre-allocation */
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
if (dst_oir) dst_oir->add_to_utcb();
/* add the translated capability id to the target buffer */
_utcb->cap_add(dst_oir ? dst_oir->capid() : cap_id_invalid());
}
}
void Ipc_node::_receive_request(Ipc_node &caller)
{
copy_msg(caller);
_thread.ipc_copy_msg(caller._thread);
_caller = &caller;
_state = INACTIVE;
}
@ -94,9 +36,9 @@ void Ipc_node::_receive_request(Ipc_node &caller)
void Ipc_node::_receive_reply(Ipc_node &callee)
{
copy_msg(callee);
_thread.ipc_copy_msg(callee._thread);
_state = INACTIVE;
_send_request_succeeded();
_thread.ipc_send_request_succeeded();
}
@ -105,7 +47,7 @@ void Ipc_node::_announce_request(Ipc_node &node)
/* directly receive request if we've awaited it */
if (_state == AWAIT_REQUEST) {
_receive_request(node);
_await_request_succeeded();
_thread.ipc_await_request_succeeded();
return;
}
@ -152,40 +94,24 @@ void Ipc_node::_outbuf_request_cancelled()
_callee = nullptr;
_state = INACTIVE;
_send_request_failed();
_thread.ipc_send_request_failed();
}
bool Ipc_node::_helps_outbuf_dst() { return (_state == AWAIT_REPLY) && _help; }
void Ipc_node::_init(Genode::Native_utcb &utcb, Ipc_node &starter)
bool Ipc_node::can_send_request()
{
_utcb = &utcb;
_rcv_caps = starter._utcb->cap_cnt();
Genode::Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < _rcv_caps; i++)
_obj_id_ref_ptr[i] = slab.alloc(sizeof(Object_identity_reference));
copy_msg(starter);
return _state == INACTIVE;
}
void Ipc_node::send_request(Ipc_node &callee, capid_t capid, bool help,
unsigned rcv_caps)
void Ipc_node::send_request(Ipc_node &callee, bool help)
{
if (_state != INACTIVE) {
Genode::raw("IPC send request: bad state");
return;
}
Genode::Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < rcv_caps; i++)
_obj_id_ref_ptr[i] = slab.alloc(sizeof(Object_identity_reference));
_state = AWAIT_REPLY;
_callee = &callee;
_capid = capid;
_help = false;
_rcv_caps = rcv_caps;
/* announce request */
_callee->_announce_request(*this);
@ -198,18 +124,14 @@ Ipc_node * Ipc_node::helping_sink() {
return _helps_outbuf_dst() ? _callee->helping_sink() : this; }
bool Ipc_node::await_request(unsigned rcv_caps)
bool Ipc_node::can_await_request()
{
if (_state != INACTIVE) {
Genode::raw("IPC await request: bad state");
return true;
}
Genode::Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < rcv_caps; i++)
_obj_id_ref_ptr[i] = slab.alloc(sizeof(Object_identity_reference));
return _state == INACTIVE;
}
_rcv_caps = rcv_caps;
bool Ipc_node::await_request()
{
/* if no request announced then wait */
bool announced = false;
_state = AWAIT_REQUEST;
@ -239,11 +161,11 @@ void Ipc_node::cancel_waiting()
case AWAIT_REPLY:
_cancel_outbuf_request();
_state = INACTIVE;
_send_request_failed();
_thread.ipc_send_request_failed();
break;
case AWAIT_REQUEST:
_state = INACTIVE;
_await_request_failed();
_thread.ipc_await_request_failed();
break;
return;
default: return;
@ -251,6 +173,12 @@ void Ipc_node::cancel_waiting()
}
Ipc_node::Ipc_node(Thread &thread)
:
_thread(thread)
{ }
Ipc_node::~Ipc_node()
{
_cancel_request_queue();

View File

@ -15,18 +15,14 @@
#ifndef _CORE__KERNEL__IPC_NODE_H_
#define _CORE__KERNEL__IPC_NODE_H_
/* base-local includes */
#include <base/internal/native_utcb.h>
/* core includes */
#include <kernel/interface.h>
#include <assertion.h>
/* Genode includes */
#include <util/fifo.h>
namespace Genode { class Msgbuf_base; };
namespace Kernel
{
class Pd;
class Thread;
/**
* Backend for end points of synchronous interprocess communication
@ -47,27 +43,19 @@ class Kernel::Ipc_node : private Ipc_node_queue::Element
AWAIT_REQUEST = 3,
};
void _init(Genode::Native_utcb &utcb, Ipc_node &callee);
private:
friend class Core_thread;
friend class Genode::Fifo<Ipc_node>;
State _state = INACTIVE;
capid_t _capid = cap_id_invalid();
Ipc_node * _caller = nullptr;
Ipc_node * _callee = nullptr;
bool _help = false;
size_t _rcv_caps = 0; /* max capability num to receive */
Genode::Native_utcb * _utcb = nullptr;
Thread &_thread;
State _state { INACTIVE };
Ipc_node * _caller { nullptr };
Ipc_node * _callee { nullptr };
bool _help { false };
Ipc_node_queue _request_queue { };
/* pre-allocation array for obkject identity references */
void * _obj_id_ref_ptr[Genode::Msgbuf_base::MAX_CAPS_PER_MSG];
inline void copy_msg(Ipc_node &sender);
/**
* Buffer next request from request queue in 'r' to handle it
*/
@ -114,40 +102,26 @@ class Kernel::Ipc_node : private Ipc_node_queue::Element
bool _helps_outbuf_dst();
/**
* IPC node returned from waiting due to reply receipt
* Make the class noncopyable because it has pointer members
*/
virtual void _send_request_succeeded() = 0;
Ipc_node(const Ipc_node&) = delete;
/**
* IPC node returned from waiting due to reply cancellation
* Make the class noncopyable because it has pointer members
*/
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;
protected:
Pd * _pd = nullptr; /* pointer to PD this IPC node is part of */
/***************
** Accessors **
***************/
Ipc_node * callee() { return _callee; }
State state() { return _state; }
const Ipc_node& operator=(const Ipc_node&) = delete;
public:
virtual ~Ipc_node();
/**
* Destructor
*/
~Ipc_node();
/**
* Constructor
*/
Ipc_node(Thread &thread);
/**
* Send a request and wait for the according reply
@ -155,8 +129,8 @@ class Kernel::Ipc_node : private Ipc_node_queue::Element
* \param callee targeted IPC node
* \param help wether the request implies a helping relationship
*/
void send_request(Ipc_node &callee, capid_t capid, bool help,
unsigned rcv_caps);
bool can_send_request();
void send_request(Ipc_node &callee, bool help);
/**
* Return root destination of the helping-relation tree we are in
@ -181,7 +155,8 @@ class Kernel::Ipc_node : private Ipc_node_queue::Element
*
* \return wether a request could be received already
*/
bool await_request(unsigned rcv_caps);
bool can_await_request();
bool await_request();
/**
* Reply to last request if there's any
@ -198,15 +173,9 @@ class Kernel::Ipc_node : private Ipc_node_queue::Element
** Accessors **
***************/
Pd &pd() const
{
if (_pd)
return *_pd;
ASSERT_NEVER_CALLED;
}
Genode::Native_utcb *utcb() { return _utcb; }
Ipc_node * callee() { return _callee; }
State state() { return _state; }
Thread &thread() { return _thread; }
};
#endif /* _CORE__KERNEL__IPC_NODE_H_ */

View File

@ -35,6 +35,72 @@ extern "C" void _core_start(void);
using namespace Kernel;
static inline void free_obj_id_ref(Pd &pd, void *ptr)
{
pd.platform_pd().capability_slab().free(ptr, sizeof(Object_identity_reference));
}
void Thread::_ipc_init(Genode::Native_utcb &utcb, Thread &starter)
{
_utcb = &utcb;
_ipc_rcv_caps = starter._utcb->cap_cnt();
Genode::Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < _ipc_rcv_caps; i++)
_obj_id_ref_ptr[i] = slab.alloc(sizeof(Object_identity_reference));
ipc_copy_msg(starter);
}
void Thread::ipc_copy_msg(Thread &sender)
{
using namespace Genode;
using Reference = Object_identity_reference;
/* copy payload and set destination capability id */
*_utcb = *sender._utcb;
_utcb->destination(sender._ipc_capid);
/* translate capabilities */
for (unsigned i = 0; i < _ipc_rcv_caps; i++) {
capid_t id = sender._utcb->cap_get(i);
/* if there is no capability to send, just free the pre-allocation */
if (i >= sender._utcb->cap_cnt()) {
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
continue;
}
/* lookup the capability id within the caller's cap space */
Reference *oir = (id == cap_id_invalid())
? nullptr : sender.pd().cap_tree().find(id);
/* if the caller's capability is invalid, free the pre-allocation */
if (!oir) {
_utcb->cap_add(cap_id_invalid());
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
continue;
}
/* lookup the capability id within the callee's cap space */
Reference *dst_oir = oir->find(pd());
/* if it is not found, and the target is not core, create a reference */
if (!dst_oir && (&pd() != &core_pd())) {
dst_oir = oir->factory(_obj_id_ref_ptr[i], pd());
if (!dst_oir)
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
} else /* otherwise free the pre-allocation */
free_obj_id_ref(pd(), _obj_id_ref_ptr[i]);
if (dst_oir) dst_oir->add_to_utcb();
/* add the translated capability id to the target buffer */
_utcb->cap_add(dst_oir ? dst_oir->capid() : cap_id_invalid());
}
}
Thread::Tlb_invalidation::Tlb_invalidation(Thread & caller, Pd & pd,
addr_t addr, size_t size,
@ -114,7 +180,7 @@ void Thread::_receive_signal(void * const base, size_t const size)
}
void Thread::_send_request_succeeded()
void Thread::ipc_send_request_succeeded()
{
assert(_state == AWAITS_IPC);
user_arg_0(0);
@ -123,7 +189,7 @@ void Thread::_send_request_succeeded()
}
void Thread::_send_request_failed()
void Thread::ipc_send_request_failed()
{
assert(_state == AWAITS_IPC);
user_arg_0(-1);
@ -132,7 +198,7 @@ void Thread::_send_request_failed()
}
void Thread::_await_request_succeeded()
void Thread::ipc_await_request_succeeded()
{
assert(_state == AWAITS_IPC);
user_arg_0(0);
@ -140,7 +206,7 @@ void Thread::_await_request_succeeded()
}
void Thread::_await_request_failed()
void Thread::ipc_await_request_failed()
{
assert(_state == AWAITS_IPC);
user_arg_0(-1);
@ -151,15 +217,15 @@ void Thread::_await_request_failed()
void Thread::_deactivate_used_shares()
{
Cpu_job::_deactivate_own_share();
Ipc_node::for_each_helper([&] (Ipc_node &h) {
static_cast<Thread &>(h)._deactivate_used_shares(); });
_ipc_node.for_each_helper([&] (Ipc_node &ipc_node) {
ipc_node.thread()._deactivate_used_shares(); });
}
void Thread::_activate_used_shares()
{
Cpu_job::_activate_own_share();
Ipc_node::for_each_helper([&] (Ipc_node &h) {
static_cast<Thread &>(h)._activate_used_shares(); });
_ipc_node.for_each_helper([&] (Ipc_node &ipc_node) {
ipc_node.thread()._activate_used_shares(); });
}
void Thread::_become_active()
@ -180,7 +246,7 @@ void Thread::_die() { _become_inactive(DEAD); }
Cpu_job * Thread::helping_sink() {
return static_cast<Thread *>(Ipc_node::helping_sink()); }
return &_ipc_node.helping_sink()->thread(); }
size_t Thread::_core_to_kernel_quota(size_t const quota) const
@ -212,7 +278,7 @@ void Thread::_call_start_thread()
/* join protection domain */
thread._pd = (Pd *) user_arg_3();
thread.Ipc_node::_init(*(Native_utcb *)user_arg_4(), *this);
thread._ipc_init(*(Native_utcb *)user_arg_4(), *this);
thread._become_active();
}
@ -285,7 +351,7 @@ void Thread::_cancel_blocking()
_become_active();
return;
case AWAITS_IPC:
Ipc_node::cancel_waiting();
_ipc_node.cancel_waiting();
return;
case AWAITS_SIGNAL:
Signal_handler::cancel_waiting();
@ -339,7 +405,18 @@ void Thread::_call_delete_thread()
void Thread::_call_await_request_msg()
{
if (Ipc_node::await_request(user_arg_1())) {
if (!_ipc_node.can_await_request()) {
Genode::raw("IPC await request: bad state");
user_arg_0(0);
return;
}
unsigned const rcv_caps = user_arg_1();
Genode::Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < rcv_caps; i++)
_obj_id_ref_ptr[i] = slab.alloc(sizeof(Object_identity_reference));
_ipc_rcv_caps = rcv_caps;
if (_ipc_node.await_request()) {
user_arg_0(0);
return;
}
@ -390,8 +467,19 @@ void Thread::_call_send_request_msg()
bool const help = Cpu_job::_helping_possible(*dst);
oir = oir->find(dst->pd());
Ipc_node::send_request(*dst, oir ? oir->capid() : cap_id_invalid(),
help, user_arg_2());
if (!_ipc_node.can_send_request()) {
Genode::raw("IPC send request: bad state");
} else {
unsigned const rcv_caps = user_arg_2();
Genode::Allocator &slab = pd().platform_pd().capability_slab();
for (unsigned i = 0; i < rcv_caps; i++)
_obj_id_ref_ptr[i] = slab.alloc(sizeof(Object_identity_reference));
_ipc_capid = oir ? oir->capid() : cap_id_invalid();
_ipc_rcv_caps = rcv_caps;
_ipc_node.send_request(dst->_ipc_node, help);
}
_state = AWAITS_IPC;
if (!help || !dst->own_share_active()) { _deactivate_used_shares(); }
}
@ -399,7 +487,7 @@ void Thread::_call_send_request_msg()
void Thread::_call_send_reply_msg()
{
Ipc_node::send_reply();
_ipc_node.send_reply();
bool const await_request_msg = user_arg_2();
if (await_request_msg) { _call_await_request_msg(); }
else { user_arg_0(0); }
@ -730,7 +818,7 @@ void Thread::_mmu_exception()
Thread::Thread(unsigned const priority, unsigned const quota,
char const * const label, bool core)
:
Cpu_job(priority, quota), _state(AWAITS_START),
Cpu_job(priority, quota), _ipc_node(*this), _state(AWAITS_START),
_signal_receiver(0), _label(label), _core(core), regs(core) { }

View File

@ -14,6 +14,8 @@
#ifndef _CORE__KERNEL__THREAD_H_
#define _CORE__KERNEL__THREAD_H_
/* Genode includes */
#include <base/signal.h>
#include <util/reconstructible.h>
@ -24,6 +26,12 @@
#include <kernel/signal_receiver.h>
#include <kernel/ipc_node.h>
#include <object.h>
#include <kernel/interface.h>
#include <assertion.h>
/* base-local includes */
#include <base/internal/native_utcb.h>
namespace Kernel
{
@ -50,9 +58,8 @@ struct Kernel::Thread_fault
*/
class Kernel::Thread
:
public Kernel::Object, public Cpu_job,
public Ipc_node, public Signal_context_killer, public Signal_handler,
private Timeout
public Kernel::Object, public Cpu_job, public Signal_context_killer,
public Signal_handler, private Timeout
{
private:
@ -121,15 +128,21 @@ class Kernel::Thread
DEAD = 7,
};
Signal_context * _pager = nullptr;
Thread_fault _fault { };
State _state;
Signal_receiver * _signal_receiver;
char const * const _label;
capid_t _timeout_sigid = 0;
bool _paused = false;
bool _cancel_next_await_signal = false;
bool const _core = false;
void *_obj_id_ref_ptr[Genode::Msgbuf_base::MAX_CAPS_PER_MSG];
Ipc_node _ipc_node;
capid_t _ipc_capid { cap_id_invalid() };
size_t _ipc_rcv_caps { 0 };
Genode::Native_utcb *_utcb { nullptr };
Pd *_pd { nullptr };
Signal_context *_pager { nullptr };
Thread_fault _fault { };
State _state;
Signal_receiver *_signal_receiver;
char const *const _label;
capid_t _timeout_sigid { 0 };
bool _paused { false };
bool _cancel_next_await_signal { false };
bool const _core { false };
Genode::Constructible<Tlb_invalidation> _tlb_invalidation {};
Genode::Constructible<Destroy> _destroy {};
@ -258,6 +271,8 @@ class Kernel::Thread
kobj.destruct();
}
void _ipc_init(Genode::Native_utcb &utcb, Thread &callee);
/***************************
** Signal_context_killer **
@ -276,15 +291,6 @@ class Kernel::Thread
void _receive_signal(void * const base, size_t const size) override;
/**************
** Ipc_node **
**************/
void _send_request_succeeded() override;
void _send_request_failed() override;
void _await_request_succeeded() override;
void _await_request_failed() override;
public:
Genode::Align_at<Genode::Cpu::Context> regs;
@ -373,6 +379,16 @@ class Kernel::Thread
void print(Genode::Output &out) const;
/**************
** Ipc_node **
**************/
void ipc_send_request_succeeded() ;
void ipc_send_request_failed() ;
void ipc_await_request_succeeded();
void ipc_await_request_failed() ;
void ipc_copy_msg(Thread &sender) ;
/*************
** Cpu_job **
@ -396,6 +412,15 @@ class Kernel::Thread
char const * label() const { return _label; }
Thread_fault fault() const { return _fault; }
Genode::Native_utcb *utcb() { return _utcb; }
Pd &pd() const
{
if (_pd)
return *_pd;
ASSERT_NEVER_CALLED;
}
};