NOVA: use translate feature of kernel, issue #268

Kernel patch:
Introduce a transfer item type to express that a cap should be translated
and if this fails to map it instead.

It would be possible without this combined transfer item type however
with additional overhead. In this case Genode/NOVA would
have to map and translate all caps used as parameter in IPC. It would look
like this:
* If the map and translation succeed, the cap at the new cap index
  would have to be revoked. Then the translated cap index can be used.
* If the map succeeds and the translation fails then the mapped cap index
  can be used.
* It would become complicated when multiple caps are mapped and translated
  and only some of the translation succeed. In such cases Genode would have
  to figure out the right relation of translated/mapped and not
  translated/mapped caps. It would require to make some assumption about the
  order how translated/mapped caps are reported at the UTCB by the kernel.
All the points above lead to the decision to create a separate transfer item
type for that.

Genode:
Most the times the translation succeeds, mapping of caps happens either
seldom. This takes now a bit the pressure of not enough aligned receive
cap windows as described in issue #247.

The patch mainly adds adjustments to handle the
translated and mapped caps correctly especially during freeing of the
receive window (don't free translated cap indexes).

Fixes #268
This commit is contained in:
Alexander Boettcher 2012-07-10 16:46:58 +02:00 committed by Norman Feske
parent 44ef8912d6
commit 2b161b7aff
4 changed files with 175 additions and 12 deletions

View File

@ -45,17 +45,23 @@ namespace Genode {
/**
* Portal capability selectors to delegate
*/
int _snd_pt_sel[MAX_CAP_ARGS];
addr_t _snd_pt_sel[MAX_CAP_ARGS];
/**
* Base of portal receive window
*/
int _rcv_pt_base;
struct {
addr_t sel;
bool del;
} _rcv_pt_sel [MAX_CAP_ARGS];
/**
* Read counter for unmarshalling portal capability selectors
*/
int _rcv_pt_sel_cnt;
unsigned _rcv_pt_sel_cnt;
unsigned _rcv_pt_sel_max;
/**
* Flag set to true if receive window must be re-initialized
@ -98,7 +104,7 @@ namespace Genode {
/**
* Append portal capability selector to message buffer
*/
inline bool snd_append_pt_sel(int pt_sel)
inline bool snd_append_pt_sel(addr_t pt_sel)
{
if (_snd_pt_sel_cnt >= MAX_CAP_ARGS - 1)
return false;
@ -119,25 +125,35 @@ namespace Genode {
* \return portal-capability selector, or
* -1 if index is invalid
*/
int snd_pt_sel(unsigned i) { return i < _snd_pt_sel_cnt ? _snd_pt_sel[i] : -1; }
addr_t snd_pt_sel(unsigned i) { return i < _snd_pt_sel_cnt ? _snd_pt_sel[i] : -1; }
/**
* Request current portal-receive window
*/
int rcv_pt_base() { return _rcv_pt_base; }
addr_t rcv_pt_base() { return _rcv_pt_base; }
/**
* Reset portal-capability receive window
*/
void rcv_reset() { _rcv_pt_sel_cnt = 0; }
void rcv_reset()
{
if (!rcv_invalid()) { rcv_cleanup(false); }
_rcv_pt_sel_cnt = 0;
_rcv_pt_sel_max = 0;
_rcv_pt_base = ~0UL;
}
/**
* Return received portal-capability selector
*/
int rcv_pt_sel()
addr_t rcv_pt_sel()
{
_rcv_dirty = true;
return _rcv_pt_base + _rcv_pt_sel_cnt++;
/* return only received or translated caps */
if (_rcv_pt_sel_cnt < _rcv_pt_sel_max)
return _rcv_pt_sel[_rcv_pt_sel_cnt++].sel;
else
return 0;
}
/**
@ -148,8 +164,62 @@ namespace Genode {
* current receive window with one or more portal capabilities.
* To enable the reception of portal capability selectors for the
* next IDC, we need a fresh receive window.
*
* \param keep If 'true', try to keep receive window if it's clean.
* If 'false', free caps of receive window because object is freed afterwards.
*
* \result 'true' if receive window was freed, 'false' if it was kept.
*/
bool rcv_dirty() { return _rcv_dirty; }
bool rcv_cleanup(bool keep)
{
/* If nothing has been used, revoke/free at once */
if (_rcv_pt_sel_cnt == 0) {
_rcv_pt_sel_max = 0;
if (_rcv_items)
Nova::revoke(Nova::Obj_crd(rcv_pt_base(), MAX_CAP_ARGS_LOG2), true);
if (keep) return false;
cap_selector_allocator()->free(rcv_pt_base(), MAX_CAP_ARGS_LOG2);
return true;
}
/* Revoke received unused caps, skip translated caps */
for (unsigned i = _rcv_pt_sel_cnt; i < _rcv_pt_sel_max; i++) {
if (_rcv_pt_sel[i].del) {
/* revoke cap we received but didn't use */
Nova::revoke(Nova::Obj_crd(_rcv_pt_sel[i].sel, 0), true);
/* mark cap as free */
cap_selector_allocator()->free(_rcv_pt_sel[i].sel, 0);
}
}
/* free all caps which are unused from the receive window */
for (unsigned i=0; i < MAX_CAP_ARGS; i++) {
unsigned j=0;
for (j=0; j < _rcv_pt_sel_max; j++) {
if (_rcv_pt_sel[j].sel == rcv_pt_base() + i) break;
}
if (j < _rcv_pt_sel_max) continue;
/* Revoke seems needless here, but is required.
* It is possible that an evil guy translated us
* more then MAX_CAP_ARGS and then mapped us
* something to the receive window.
* The mappings would not show up
* in _rcv_pt_sel, but would be there.
*/
Nova::revoke(Nova::Obj_crd(rcv_pt_base() + i, 0), true);
/* i was unused, free at allocator */
cap_selector_allocator()->free(rcv_pt_base() + i, 0);
}
_rcv_pt_sel_cnt = 0;
_rcv_pt_sel_max = 0;
return true;
}
/**
* Initialize receive window for portal capability selectors
@ -168,7 +238,34 @@ namespace Genode {
/* register receive window at the UTCB */
utcb->crd_rcv = Nova::Obj_crd(rcv_pt_base(),MAX_CAP_ARGS_LOG2);
utcb->crd_xlt = Nova::Obj_crd(0, sizeof(addr_t) * 8 - 12);
}
/**
* Post IPC processing.
*
* Remember where and which caps have been received
* respectively have been translated.
* The information is required to correctly free
* cap indexes and to revoke unused received caps.
*
* \param utcb UTCB of designated receiver thread
*/
void post_ipc(Nova::Utcb *utcb) {
_rcv_items = (utcb->items >> 16) & 0xffffu;
_rcv_pt_sel_max = 0;
_rcv_pt_sel_cnt = 0;
for (unsigned i=0; i < _rcv_items; i++) {
Nova::Utcb::Item * item = utcb->get_item(i);
if (!item) break;
if (_rcv_pt_sel_max >= MAX_CAP_ARGS) break;
_rcv_pt_sel[_rcv_pt_sel_max].sel = item->crd >> 12;
_rcv_pt_sel[_rcv_pt_sel_max++].del = item->is_del();
}
}
};

View File

@ -392,6 +392,7 @@ namespace Nova {
struct Item {
mword_t crd;
mword_t hotspot;
bool is_del() { return hotspot & 0x1; }
};
/**
@ -415,7 +416,8 @@ namespace Nova {
__attribute__((warn_unused_result))
bool append_item(Crd crd, mword_t sel_hotspot,
bool kern_pd = false,
bool update_guest_pt = false)
bool update_guest_pt = false,
bool translate_map = false)
{
/* transfer items start at the end of the UTCB */
items += 1 << 16;
@ -433,12 +435,24 @@ namespace Nova {
/* update guest page table */
unsigned g = update_guest_pt ? (1 << 10) : 0;
item->hotspot = crd.hotspot(sel_hotspot) | g | h | 1;
item->hotspot = crd.hotspot(sel_hotspot) | g | h | (translate_map ? 2 : 1);
item->crd = crd.value();
return true;
}
/**
* Return typed item at postion i in UTCB
*
* \param i position of item requested, starts with 0
*/
Item * get_item(const unsigned i) {
if (i > (PAGE_SIZE_BYTE / sizeof(struct Item))) return 0;
Item * item = reinterpret_cast<Item *>(this) + (PAGE_SIZE_BYTE / sizeof(struct Item)) - i - 1;
if (reinterpret_cast<mword_t *>(item) < this->msg) return 0;
return item;
}
mword_t mtd_value() const { return static_cast<Mtd>(mtd).value(); }
} __attribute__((packed));

View File

@ -0,0 +1,51 @@
diff --git a/src/pd.cpp b/src/pd.cpp
index 8160d73..be9aa63 100644
--- a/src/pd.cpp
+++ b/src/pd.cpp
@@ -173,8 +173,9 @@ void Pd::xlt_crd (Pd *pd, Crd xlt, Crd &crd)
sb = (sb - mdb->node_base) + (mdb->node_phys - node->node_phys) + node->node_base;
if ((ro = clamp (sb, rb, so, ro)) != ~0UL) {
+ trace (TRACE_DEL, "XLT OBJ PD:%p->%p SB:%#010lx RB:%#010lx O:%#04lx", pd, this, crd.base(), rb, so);
crd = Crd (crd.type(), rb, ro, mdb->node_attr);
- return;
+ return;
}
}
}
@@ -245,22 +246,32 @@ void Pd::rev_crd (Crd crd, bool self)
void Pd::xfer_items (Pd *src, Crd xlt, Crd del, Xfer *s, Xfer *d, unsigned long ti)
{
- for (Crd crd; ti--; s--) {
+ mword set_as_del;
+ for (Crd crd; ti--; s--) {
+
crd = *s;
+ set_as_del = 0;
- switch (s->flags() & 1) {
+ switch (s->flags() & 3) {
case 0:
xlt_crd (src, xlt, crd);
break;
+ case 2:
+ xlt_crd (src, xlt, crd);
+ if (crd.type()) break;
+
+ crd = *s;
+ set_as_del = 1;
+
case 1:
del_crd (src == &root && s->flags() & 0x800 ? &kern : src, del, crd, s->flags() >> 9 & 3, s->hotspot());
break;
};
if (d)
- *d-- = Xfer (crd, s->flags());
+ *d-- = Xfer (crd, s->flags() | set_as_del);
}
}

View File

@ -179,6 +179,7 @@ void Ipc_server::_wait()
Nova::Utcb *utcb = (Nova::Utcb *)Thread_base::myself()->utcb();
_rcv_msg->post_ipc(utcb);
copy_utcb_to_msgbuf(utcb, _rcv_msg);
/* reset unmarshaller */