vbox5-nova: avoid Blocking_canceled exception

using pthread primitives to implement block/wakeup of EMT thread

Fixes #3810
This commit is contained in:
Alexander Boettcher 2020-07-08 14:07:08 +02:00 committed by Norman Feske
parent ed4594c76b
commit 641679f7e7
2 changed files with 49 additions and 18 deletions

View File

@ -340,9 +340,6 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation,
if (!ns_diff)
return VINF_SUCCESS;
uint64_t const tsc_offset = genode_cpu_hz() * ns_diff / (1000*1000*1000);
uint64_t const tsc_abs = Genode::Trace::timestamp() + tsc_offset;
using namespace Genode;
if (ns_diff > RT_NS_1SEC)
@ -350,7 +347,7 @@ int SUPR3CallVMMR0Ex(PVMR0 pVMR0, VMCPUID idCpu, unsigned uOperation,
Vcpu_handler *vcpu_handler = lookup_vcpu_handler(idCpu);
Assert(vcpu_handler);
vcpu_handler->halt(tsc_abs);
vcpu_handler->halt(ns_diff);
return VINF_SUCCESS;
}

View File

@ -88,7 +88,9 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
X86FXSTATE _guest_fpu_state __attribute__((aligned(0x10)));
X86FXSTATE _emt_fpu_state __attribute__((aligned(0x10)));
pthread _pthread;
pthread _pthread;
pthread_cond_t _cond_wait;
pthread_mutex_t _mutex;
Vmm::Vcpu_other_pd _vcpu;
@ -125,6 +127,29 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
INTERRUPT_STATE_NONE = 0U,
};
timespec add_timespec_ns(timespec a, uint64_t ns) const
{
enum { NSEC_PER_SEC = 1'000'000'000ull };
long sec = a.tv_sec;
while (a.tv_nsec >= NSEC_PER_SEC) {
a.tv_nsec -= NSEC_PER_SEC;
sec++;
}
while (ns >= NSEC_PER_SEC) {
ns -= NSEC_PER_SEC;
sec++;
}
long nsec = a.tv_nsec + ns;
while (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
sec++;
}
return timespec { sec, nsec };
}
protected:
void _fpu_save()
@ -834,7 +859,16 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
_vcpu(cpu_connection, location, pd_vcpu),
_ec_sel(Genode::cap_map().insert()),
_cpu_id(cpu_id)
{ }
{
pthread_mutexattr_t _attr;
pthread_mutexattr_init(&_attr);
pthread_cond_init(&_cond_wait, nullptr);
pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&_mutex, &_attr);
}
pthread &pthread_obj() { return _pthread; }
@ -880,24 +914,24 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
#endif
}
void halt(Genode::uint64_t tsc_abs)
void halt(Genode::uint64_t const wait_ns)
{
Assert(utcb() == Thread::myself()->utcb());
/* calculate timeout */
timespec ts { 0, 0 };
clock_gettime(CLOCK_REALTIME, &ts);
ts = add_timespec_ns(ts, wait_ns);
Genode::addr_t sem = native_thread().exc_pt_sel + Nova::SM_SEL_EC;
Nova::sm_ctrl(sem, Nova::SEMAPHORE_DOWNZERO, tsc_abs);
/* wait for condition or timeout */
pthread_mutex_lock(&_mutex);
pthread_cond_timedwait(&_cond_wait, &_mutex, &ts);
pthread_mutex_unlock(&_mutex);
}
void wake_up()
{
/*
* Note: the following 'SEMAPHORE_UP' call can cause a
* 'Blocking_canceled' exception in the target thread
* if it is currently blocking on a Genode lock, because
* the same NOVA semaphore is used.
*/
Genode::addr_t sem = native_thread().exc_pt_sel + Nova::SM_SEL_EC;
Nova::sm_ctrl(sem, Nova::SEMAPHORE_UP);
pthread_mutex_lock(&_mutex);
pthread_cond_signal(&_cond_wait);
pthread_mutex_unlock(&_mutex);
}
int run_hw(PVMR0 pVMR0)