From 1dd2a199a5adcfce6ffaaae0ccf5ca3182d1651e Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 6 Sep 2013 11:03:12 +0200 Subject: [PATCH] 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 --- base-nova/include/base/ipc_msgbuf.h | 143 ++++++++++++++++------------ base-nova/src/base/ipc/ipc.cc | 16 +++- base-nova/src/base/server/server.cc | 7 +- 3 files changed, 97 insertions(+), 69 deletions(-) diff --git a/base-nova/include/base/ipc_msgbuf.h b/base-nova/include/base/ipc_msgbuf.h index c351a68387..491da21647 100644 --- a/base-nova/include/base/ipc_msgbuf.h +++ b/base-nova/include/base/ipc_msgbuf.h @@ -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; } }; diff --git a/base-nova/src/base/ipc/ipc.cc b/base-nova/src/base/ipc/ipc.cc index 2499a360e1..1072badf7d 100644 --- a/base-nova/src/base/ipc/ipc.cc +++ b/base-nova/src/base/ipc/ipc.cc @@ -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); +} /**************** diff --git a/base-nova/src/base/server/server.cc b/base-nova/src/base/server/server.cc index 327196332c..88f1f0fb0c 100644 --- a/base-nova/src/base/server/server.cc +++ b/base-nova/src/base/server/server.cc @@ -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();