mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
nova: support cap receive window of various sizes
Open a capability receive window according to the number of the capabilities expected as out parameter per RPC function. Typically the number of capabilities expected during the reply of a RPC/IPC call is 0 to 1. Before this patch ever a capability receive window of 4 has been opened. On Nova the capability selectors of receive windows must be naturally aligned to the size/order of the expected capabilities. This leads until now to the issue that the left over 3 capabilities couldn't be reused for new IPCs since they are not naturally aligned to 4. Issue #905
This commit is contained in:
parent
a6af6c80ab
commit
1dd2a199a5
@ -67,31 +67,28 @@ namespace Genode {
|
||||
bool del;
|
||||
} _rcv_pt_sel [MAX_CAP_ARGS];
|
||||
|
||||
enum { FREE_SEL, UNUSED_CAP, USED_CAP } _rcv_pt_cap_free [MAX_CAP_ARGS];
|
||||
enum { FREE_INVALID, FREE_SEL, UNUSED_CAP, USED_CAP }
|
||||
_rcv_pt_cap_free [MAX_CAP_ARGS];
|
||||
|
||||
/**
|
||||
* Read counter for unmarshalling portal capability
|
||||
* selectors
|
||||
*/
|
||||
unsigned _rcv_pt_sel_cnt;
|
||||
unsigned _rcv_pt_sel_max;
|
||||
|
||||
/**
|
||||
* Number of capabilities which has been received,
|
||||
* reported by the kernel.
|
||||
*/
|
||||
unsigned _rcv_items;
|
||||
unsigned short _rcv_pt_sel_cnt;
|
||||
unsigned short _rcv_pt_sel_max;
|
||||
unsigned short _rcv_wnd_log2;
|
||||
|
||||
char _msg_start[]; /* symbol marks start of message */
|
||||
|
||||
public:
|
||||
|
||||
enum { INVALID_INDEX = ~0UL };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Msgbuf_base()
|
||||
: _rcv_pt_base(INVALID_INDEX), _rcv_items(0)
|
||||
: _rcv_pt_base(INVALID_INDEX), _rcv_wnd_log2(MAX_CAP_ARGS_LOG2)
|
||||
{
|
||||
rcv_reset();
|
||||
snd_reset();
|
||||
@ -170,6 +167,18 @@ namespace Genode {
|
||||
*/
|
||||
addr_t rcv_pt_base() const { return _rcv_pt_base; }
|
||||
|
||||
/**
|
||||
* Set log2 number of capabilities to be received during reply of
|
||||
* a IPC call.
|
||||
*/
|
||||
void rcv_wnd(unsigned short const caps_log2)
|
||||
{
|
||||
if (caps_log2 > MAX_CAP_ARGS_LOG2)
|
||||
nova_die();
|
||||
|
||||
_rcv_wnd_log2 = caps_log2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset portal-capability receive window
|
||||
*/
|
||||
@ -222,10 +231,10 @@ namespace Genode {
|
||||
* \result 'true' - receive window must be re-initialized
|
||||
* 'false' - portal selectors has been kept
|
||||
*/
|
||||
bool rcv_cleanup(bool keep)
|
||||
bool rcv_cleanup(bool keep, unsigned short const new_max = MAX_CAP_ARGS)
|
||||
{
|
||||
/* mark used mapped capabilities as used to prevent freeing */
|
||||
bool used = false;
|
||||
bool reinit = false;
|
||||
for (unsigned i = 0; i < _rcv_pt_sel_cnt; i++) {
|
||||
if (!_rcv_pt_sel[i].del)
|
||||
continue;
|
||||
@ -236,30 +245,36 @@ namespace Genode {
|
||||
nova_die();
|
||||
|
||||
_rcv_pt_cap_free [_rcv_pt_sel[i].sel - rcv_pt_base()] = USED_CAP;
|
||||
used = true;
|
||||
reinit = true;
|
||||
}
|
||||
|
||||
/* revoke received caps which are unused */
|
||||
for (unsigned i = 0; i < MAX_CAP_ARGS; i++) {
|
||||
if (_rcv_pt_cap_free[i] != UNUSED_CAP)
|
||||
continue;
|
||||
if (i < new_max && _rcv_pt_cap_free[i] == FREE_INVALID)
|
||||
reinit = true;
|
||||
|
||||
Nova::revoke(Nova::Obj_crd(rcv_pt_base() + i, 0), true);
|
||||
if (_rcv_pt_cap_free[i] == UNUSED_CAP)
|
||||
Nova::revoke(Nova::Obj_crd(rcv_pt_base() + i, 0), true);
|
||||
}
|
||||
|
||||
_rcv_pt_sel_cnt = 0;
|
||||
_rcv_pt_sel_max = 0;
|
||||
|
||||
/* we can keep the cap selectors if none was used */
|
||||
if (keep && !used)
|
||||
if (keep && !reinit) {
|
||||
/* free rest of indexes if new_max is smaller then last window */
|
||||
for (unsigned i = new_max; i < MAX_CAP_ARGS; i++)
|
||||
if (_rcv_pt_cap_free[i] == FREE_SEL)
|
||||
cap_selector_allocator()->free(rcv_pt_base() + i, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* keep used selectors, free up rest */
|
||||
for (unsigned i = 0; i < MAX_CAP_ARGS; i++) {
|
||||
if (_rcv_pt_cap_free[i] == USED_CAP)
|
||||
continue;
|
||||
|
||||
cap_selector_allocator()->free(rcv_pt_base() + i, 0);
|
||||
if (_rcv_pt_cap_free[i] == UNUSED_CAP ||
|
||||
_rcv_pt_cap_free[i] == FREE_SEL)
|
||||
cap_selector_allocator()->free(rcv_pt_base() + i, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -280,43 +295,40 @@ namespace Genode {
|
||||
* rcv_window parameter, this function allocates a
|
||||
* fresh receive window and clears 'rcv_invalid'.
|
||||
*/
|
||||
bool rcv_prepare_pt_sel_window(Nova::Utcb *utcb,
|
||||
addr_t rcv_window = INVALID_INDEX)
|
||||
bool prepare_rcv_window(Nova::Utcb *utcb,
|
||||
addr_t rcv_window = INVALID_INDEX)
|
||||
{
|
||||
try {
|
||||
/*
|
||||
* If a rcv_window was specified use solely
|
||||
* the selector specified by rcv_window.
|
||||
*/
|
||||
if (rcv_window != INVALID_INDEX) {
|
||||
/* cleanup if this msgbuf was already used */
|
||||
if (!rcv_invalid()) rcv_cleanup(false);
|
||||
/* open maximal translate window */
|
||||
utcb->crd_xlt = Nova::Obj_crd(0, ~0UL);
|
||||
|
||||
_rcv_pt_base = rcv_window;
|
||||
} else {
|
||||
if (rcv_invalid() || rcv_cleanup(true))
|
||||
_rcv_pt_base = cap_selector_allocator()->alloc(MAX_CAP_ARGS_LOG2);
|
||||
}
|
||||
/* use receive window if specified */
|
||||
if (rcv_window != INVALID_INDEX) {
|
||||
/* cleanup if receive window already used */
|
||||
if (!rcv_invalid()) rcv_cleanup(false);
|
||||
|
||||
addr_t max = 0;
|
||||
if (rcv_window == INVALID_INDEX)
|
||||
max = MAX_CAP_ARGS_LOG2;
|
||||
|
||||
using namespace Nova;
|
||||
/* setup receive window */
|
||||
utcb->crd_rcv = Obj_crd(rcv_pt_base(), max);
|
||||
/* open maximal translate window */
|
||||
utcb->crd_xlt = Obj_crd(0, ~0UL);
|
||||
_rcv_pt_base = rcv_window;
|
||||
|
||||
/* open receive window */
|
||||
utcb->crd_rcv = Nova::Obj_crd(rcv_pt_base(), _rcv_wnd_log2);
|
||||
return true;
|
||||
} catch (Bit_array_out_of_indexes) {
|
||||
using namespace Nova;
|
||||
/* setup receive window */
|
||||
utcb->crd_rcv = Obj_crd();
|
||||
/* open maximal translate window */
|
||||
utcb->crd_xlt = Obj_crd(0, ~0UL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* allocate receive window if necessary, otherwise use old one */
|
||||
if (rcv_invalid() || rcv_cleanup(true, 1U << _rcv_wnd_log2))
|
||||
{
|
||||
try {
|
||||
_rcv_pt_base = cap_selector_allocator()->alloc(_rcv_wnd_log2);
|
||||
} catch (Bit_array_out_of_indexes) {
|
||||
_rcv_pt_base = INVALID_INDEX;
|
||||
/* no mappings can be received */
|
||||
utcb->crd_rcv = Nova::Obj_crd();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* open receive window */
|
||||
utcb->crd_rcv = Nova::Obj_crd(rcv_pt_base(), _rcv_wnd_log2);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -329,19 +341,25 @@ namespace Genode {
|
||||
*
|
||||
* \param utcb UTCB of designated receiver thread
|
||||
*/
|
||||
void post_ipc(Nova::Utcb *utcb)
|
||||
void post_ipc(Nova::Utcb *utcb, addr_t const rcv_window = INVALID_INDEX)
|
||||
{
|
||||
using namespace Nova;
|
||||
|
||||
_rcv_items = (utcb->items >> 16) & 0xffffu;
|
||||
unsigned const rcv_items = (utcb->items >> 16) & 0xffffu;
|
||||
|
||||
_rcv_pt_sel_max = 0;
|
||||
_rcv_pt_sel_cnt = 0;
|
||||
|
||||
for (unsigned i = 0; i < MAX_CAP_ARGS; i++)
|
||||
_rcv_pt_cap_free [i] = FREE_SEL;
|
||||
unsigned short const max = 1U << utcb->crd_rcv.order();
|
||||
if (max > MAX_CAP_ARGS)
|
||||
nova_die();
|
||||
|
||||
for (unsigned short i = 0; i < MAX_CAP_ARGS; i++)
|
||||
_rcv_pt_cap_free [i] = (i >= max) ? FREE_INVALID : FREE_SEL;
|
||||
|
||||
addr_t max = 1UL << utcb->crd_rcv.order();
|
||||
for (unsigned i = 0; i < _rcv_items; i++) {
|
||||
|
||||
for (unsigned i = 0; i < rcv_items; i++) {
|
||||
Utcb::Item * item = utcb->get_item(i);
|
||||
if (!item)
|
||||
break;
|
||||
@ -352,7 +370,7 @@ namespace Genode {
|
||||
if (!cap.is_null() && item->is_del()) {
|
||||
/* should never happen */
|
||||
if (cap.base() < rcv_pt_base() ||
|
||||
(cap.base() >= rcv_pt_base() + MAX_CAP_ARGS))
|
||||
(cap.base() >= rcv_pt_base() + max))
|
||||
nova_die();
|
||||
_rcv_pt_cap_free [cap.base() - rcv_pt_base()] = UNUSED_CAP;
|
||||
}
|
||||
@ -371,12 +389,11 @@ namespace Genode {
|
||||
|
||||
/*
|
||||
* If a specific rcv_window has been specified,
|
||||
* (see rcv_prepare_pt_sel_window) then the
|
||||
* caller want to take care about freeing the
|
||||
* selector. Make the _rcv_pt_base invalid so
|
||||
* that it is not cleanup twice.
|
||||
* (see prepare_rcv_window) then the caller want to take care
|
||||
* about freeing the * selector. Make the _rcv_pt_base invalid
|
||||
* so that it is not cleanup twice.
|
||||
*/
|
||||
if (max != MAX_CAP_ARGS)
|
||||
if (rcv_window != INVALID_INDEX)
|
||||
_rcv_pt_base = INVALID_INDEX;
|
||||
}
|
||||
};
|
||||
|
@ -162,7 +162,7 @@ void Ipc_client::_call()
|
||||
}
|
||||
|
||||
/* if we can't setup receive window, die in order to recognize the issue */
|
||||
if (!_rcv_msg->rcv_prepare_pt_sel_window(utcb, Ipc_ostream::_dst.rcv_window()))
|
||||
if (!_rcv_msg->prepare_rcv_window(utcb, Ipc_ostream::_dst.rcv_window()))
|
||||
/* printf doesn't work here since for IPC also rcv_prepare* is used */
|
||||
nova_die();
|
||||
|
||||
@ -175,7 +175,7 @@ void Ipc_client::_call()
|
||||
ret(ERR_INVALID_OBJECT);
|
||||
}
|
||||
|
||||
_rcv_msg->post_ipc(utcb);
|
||||
_rcv_msg->post_ipc(utcb, Ipc_ostream::_dst.rcv_window());
|
||||
copy_utcb_to_msgbuf(utcb, _rcv_msg);
|
||||
_snd_msg->snd_reset();
|
||||
|
||||
@ -186,7 +186,17 @@ void Ipc_client::_call()
|
||||
Ipc_client::Ipc_client(Native_capability const &srv, Msgbuf_base *snd_msg,
|
||||
Msgbuf_base *rcv_msg, unsigned short const rcv_caps)
|
||||
: Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0)
|
||||
{ }
|
||||
{
|
||||
if (rcv_caps == ~0)
|
||||
/* use default values for rcv_wnd */
|
||||
return;
|
||||
|
||||
/* calculate max order of caps to be received during reply */
|
||||
unsigned short log2_max = rcv_caps ? log2(rcv_caps) : 0;
|
||||
if ((1U << log2_max) < rcv_caps) log2_max ++;
|
||||
|
||||
rcv_msg->rcv_wnd(log2_max);
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
|
@ -140,8 +140,8 @@ void Rpc_entrypoint::_activation_entry()
|
||||
}
|
||||
|
||||
/* if we can't setup receive window, die in order to recognize the issue */
|
||||
if (!ep->_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)ep->utcb()))
|
||||
/* printf doesn't work here since for IPC also rcv_prepare* is used */
|
||||
if (!ep->_rcv_buf.prepare_rcv_window((Nova::Utcb *)ep->utcb()))
|
||||
/* printf doesn't work here since for IPC also prepare_rcv_window is used */
|
||||
nova_die();
|
||||
|
||||
srv << IPC_REPLY;
|
||||
@ -230,7 +230,8 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
|
||||
/* prepare portal receive window of new thread */
|
||||
_rcv_buf.rcv_prepare_pt_sel_window((Nova::Utcb *)&_context->utcb);
|
||||
if (!_rcv_buf.prepare_rcv_window((Nova::Utcb *)&_context->utcb))
|
||||
throw Cpu_session::Thread_creation_failed();
|
||||
|
||||
if (start_on_construction)
|
||||
activate();
|
||||
|
Loading…
Reference in New Issue
Block a user