mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +00:00
hw: reference count capabilities in UTCBs
When capabilities are delegated to components, they are added to the UTCB of the target thread. Before the thread is able to take out the capability id out of the UTCB and adapt the user-level capability reference counter, it might happen that another thread of the same component deletes the same capability because its user-level reference counter reached zero. If the kernel then destroys the capability, before the same capability id is taken out of all UTCBs, an inconsitent view in the component is the result. To keep an consistent view in the multi-threading scenario, the kernel now counts how often it puts a capability into a UTCB. The threads on the other hand hint the kernel when they took capabilities out of the UTCB, so the kernel can decrement the counter again. Only when the counter is zero, capabilities can get destructed. Fix #1623
This commit is contained in:
parent
41b9f6bd03
commit
60ba210a6b
@ -161,7 +161,10 @@ class Genode::Native_utcb
|
||||
void copy_to(Msgbuf_base &o)
|
||||
{
|
||||
o._snd_cap_cnt = _cap_cnt;
|
||||
for (unsigned i = 0; i < _cap_cnt; i++) o._caps[i] = _caps[i];
|
||||
for (unsigned i = 0; i < _cap_cnt; i++) {
|
||||
o._caps[i] = _caps[i];
|
||||
if (o._caps[i].valid()) Kernel::ack_cap(o._caps[i].dst());
|
||||
}
|
||||
|
||||
memcpy(o.buf, _buf, min(_size, o._size));
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ namespace Kernel
|
||||
constexpr Call_arg call_id_print_char() { return 10; }
|
||||
constexpr Call_arg call_id_update_data_region() { return 11; }
|
||||
constexpr Call_arg call_id_update_instr_region() { return 12; }
|
||||
constexpr Call_arg call_id_delete_cap() { return 13; }
|
||||
constexpr Call_arg call_id_ack_cap() { return 13; }
|
||||
constexpr Call_arg call_id_delete_cap() { return 14; }
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
@ -266,6 +267,16 @@ namespace Kernel
|
||||
return call(call_id_kill_signal_context(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acknowledge reception of a capability
|
||||
*
|
||||
* \param cap capability id to acknowledge
|
||||
*/
|
||||
inline void ack_cap(capid_t const cap)
|
||||
{
|
||||
call(call_id_ack_cap(), cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a capability id
|
||||
*
|
||||
|
@ -106,6 +106,7 @@ class Kernel::Object_identity_reference
|
||||
capid_t _capid;
|
||||
Object_identity *_identity;
|
||||
Pd &_pd;
|
||||
unsigned short _in_utcbs;
|
||||
|
||||
public:
|
||||
|
||||
@ -125,6 +126,10 @@ class Kernel::Object_identity_reference
|
||||
Pd & pd() { return _pd; }
|
||||
capid_t capid() { return _capid; }
|
||||
|
||||
void add_to_utcb() { _in_utcbs++; }
|
||||
void remove_from_utcb() { _in_utcbs--; }
|
||||
bool in_utcb() { return _in_utcbs > 0; }
|
||||
|
||||
void invalidate();
|
||||
|
||||
|
||||
|
@ -244,6 +244,7 @@ class Kernel::Thread
|
||||
void _call_ack_irq();
|
||||
void _call_new_obj();
|
||||
void _call_delete_obj();
|
||||
void _call_ack_cap();
|
||||
void _call_delete_cap();
|
||||
|
||||
template <typename T, typename... ARGS>
|
||||
|
@ -47,13 +47,6 @@ void Ipc_node::copy_msg(Ipc_node * const sender)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* within the same pd, we can simply copy the id */
|
||||
if (pd() == sender->pd()) {
|
||||
_utcb->cap_add(id);
|
||||
pd()->platform_pd()->capability_slab().free(_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);
|
||||
@ -76,6 +69,8 @@ void Ipc_node::copy_msg(Ipc_node * const sender)
|
||||
} else /* otherwise free the pre-allocation */
|
||||
pd()->platform_pd()->capability_slab().free(_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());
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ void Object_identity_reference::invalidate() {
|
||||
|
||||
Object_identity_reference::Object_identity_reference(Object_identity *oi,
|
||||
Pd &pd)
|
||||
: _capid(pd.capid_alloc().alloc()), _identity(oi), _pd(pd)
|
||||
: _capid(pd.capid_alloc().alloc()), _identity(oi), _pd(pd), _in_utcbs(0)
|
||||
{
|
||||
if (_identity) _identity->insert(this);
|
||||
_pd.cap_tree().insert(this);
|
||||
|
@ -585,10 +585,21 @@ void Thread::_call_delete_obj()
|
||||
}
|
||||
|
||||
|
||||
void Thread::_call_ack_cap()
|
||||
{
|
||||
Object_identity_reference * oir = pd()->cap_tree().find(user_arg_1());
|
||||
if (oir) oir->remove_from_utcb();
|
||||
}
|
||||
|
||||
|
||||
void Thread::_call_delete_cap()
|
||||
{
|
||||
Object_identity_reference * oir = pd()->cap_tree().find(user_arg_1());
|
||||
if (oir) destroy(pd()->platform_pd()->capability_slab(), oir);
|
||||
if (!oir) return;
|
||||
|
||||
if (oir->in_utcb()) return;
|
||||
|
||||
destroy(pd()->platform_pd()->capability_slab(), oir);
|
||||
}
|
||||
|
||||
|
||||
@ -612,6 +623,7 @@ void Thread::_call()
|
||||
case call_id_await_signal(): _call_await_signal(); return;
|
||||
case call_id_ack_signal(): _call_ack_signal(); return;
|
||||
case call_id_print_char(): _call_print_char(); return;
|
||||
case call_id_ack_cap(): _call_ack_cap(); return;
|
||||
case call_id_delete_cap(): _call_delete_cap(); return;
|
||||
default:
|
||||
/* check wether this is a core thread */
|
||||
|
Loading…
Reference in New Issue
Block a user