sel4: avoid unsynchronized vm state tracking

Remove _extra_dispatch_up which is not required and racy which may lead
to hangs between ep and vcpu thread.

Issue #5461
This commit is contained in:
Alexander Boettcher 2025-02-09 11:20:19 +01:00 committed by Christian Helmuth
parent ea97d1943a
commit c6e8acc037

View File

@ -80,8 +80,6 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
uint64_t _tsc_offset { 0 };
Semaphore _state_ready { 0 };
bool _dispatching { false };
bool _extra_dispatch_up { false };
void *_ep_handler { nullptr };
Constructible<Sel4_native_rpc> _rpc { };
@ -201,10 +199,7 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
_state_ready.up();
if (_extra_dispatch_up) {
_extra_dispatch_up = false;
_exit_handler.ready_semaphore().down();
}
_exit_handler.ready_semaphore().down();
continue;
}
@ -244,17 +239,16 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
if (res != SEL4_VMENTER_RESULT_FAULT) {
Mutex::Guard guard(_remote_mutex);
if (_remote == PAUSE) {
if (_remote == PAUSE)
_remote = NONE;
_wake_up.down();
}
}
/* notify vCPU handler EP that state is valid */
_state_ready.up();
if (skip_dispatch)
continue;
/* notify VM handler */
Genode::Signal_transmitter(_exit_handler.signal_cap()).submit();
if (!skip_dispatch)
/* notify VM handler */
Genode::Signal_transmitter(_exit_handler.signal_cap()).submit();
/*
* Wait until VM handler is really really done,
@ -768,9 +762,14 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
void _wrapper_dispatch()
{
_dispatching = true;
_vcpu_handler.dispatch(1);
_dispatching = false;
try {
_dispatching = true;
_vcpu_handler.dispatch(1);
_dispatching = false;
} catch (...) {
_dispatching = false;
throw;
}
}
public:
@ -787,7 +786,6 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
/* wait until thread is alive, e.g. Thread::cap() is valid */
_startup.block();
_ep_handler = reinterpret_cast<Thread *>(&handler.rpc_ep());
_rpc.construct(vm, this->cap(), *this);
@ -811,48 +809,35 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
void with_state(auto const &fn)
{
if (!_dispatching) {
if (Thread::myself() != _ep_handler) {
error("vCPU state requested outside of vcpu_handler EP");
sleep_forever();
}
if (_dispatching) {
_remote_mutex.acquire();
/* wait for vCPU thread until it provides the state */
_state_ready.down();
if (fn(_state))
resume();
return;
}
{
Mutex::Guard guard(_remote_mutex);
/* Trigger pause exit */
_remote = PAUSE;
seL4_Signal(_recall);
_wake_up.up();
_remote_mutex.release();
_state_ready.down();
/*
* We're in the async dispatch, yet processing a non-pause exit.
* Signal that we have to wrap the dispatch loop around.
*/
if (_state.exit_reason != VMEXIT_RECALL) {
_extra_dispatch_up = true;
}
} else {
_state_ready.down();
}
if (fn(_state)
|| _extra_dispatch_up)
/* wait for vCPU thread until it provides the state */
_state_ready.down();
if (fn(_state))
resume();
/*
* The regular exit was handled by the asynchronous dispatch handler
* triggered by the pause request.
*
* Fake finishing the exit dispatch so that the vCPU loop
* processes the asynchronously dispatched exit and provides
* the VMEXIT_RECALL to the already pending dispatch function
* for the exit code.
*/
if (!_dispatching && _extra_dispatch_up)
_exit_handler.ready_semaphore().up();
/* notify vCPU thread that we are done handling the state. */
_exit_handler.ready_semaphore().up();
}
Sel4_native_rpc * rpc() { return &*_rpc; }