From 0d6009e8dc3ab8419ca7daf9c79c9c987464e3ae Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Thu, 20 Feb 2014 15:43:53 +0100 Subject: kvm: Add support for multi-system simulation The introduction of parallel event queues added most of the support needed to run multiple VMs (systems) within the same gem5 instance. This changeset fixes up signal delivery so that KVM's control signals are delivered to the thread that executes the CPU's event queue. Specifically: * Timers and counters are now initialized from a separate method (startupThread) that is scheduled as the first event in the thread-specific event queue. This ensures that they are initialized from the thread that is going to execute the CPUs event queue and enables signal delivery to the right thread when exiting from KVM. * The POSIX-timer-based KVM timer (used to force exits from KVM) has been updated to deliver signals to the thread that's executing KVM instead of the process (thread is undefined in that case). This assumes that the timer is instantiated from the thread that is going to execute the KVM vCPU. * Signal masking is now done using pthread_sigmask instead of sigprocmask. The behavior of the latter is undefined in threaded applications. * Since signal masks can be inherited, make sure to actively unmask the control signals when setting up the KVM signal mask. There are currently no facilities to multiplex between multiple KVM CPUs in the same event queue, we are therefore limited to configurations where there is only one KVM CPU per event queue. In practice, this means that multi-system configurations can be simulated, but not multiple CPUs in a shared-memory configuration. --- src/cpu/kvm/base.cc | 62 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 18 deletions(-) (limited to 'src/cpu/kvm/base.cc') diff --git a/src/cpu/kvm/base.cc b/src/cpu/kvm/base.cc index d25e145a5..64c1a5e81 100644 --- a/src/cpu/kvm/base.cc +++ b/src/cpu/kvm/base.cc @@ -63,7 +63,7 @@ /* Used by some KVM macros */ #define PAGE_SIZE pageSize -volatile bool timerOverflowed = false; +static volatile __thread bool timerOverflowed = false; BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params) : BaseCPU(params), @@ -92,18 +92,6 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params) thread->setStatus(ThreadContext::Halted); tc = thread->getTC(); threadContexts.push_back(tc); - - setupCounters(); - - if (params->usePerfOverflow) - runTimer.reset(new PerfKvmTimer(hwCycles, - KVM_TIMER_SIGNAL, - params->hostFactor, - params->hostFreq)); - else - runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC, - params->hostFactor, - params->hostFreq)); } BaseKvmCPU::~BaseKvmCPU() @@ -177,6 +165,35 @@ BaseKvmCPU::startup() } thread->startup(); + + Event *startupEvent( + new EventWrapper(this, true)); + schedule(startupEvent, curTick()); +} + +void +BaseKvmCPU::startupThread() +{ + // Do thread-specific initialization. We need to setup signal + // delivery for counters and timers from within the thread that + // will execute the event queue to ensure that signals are + // delivered to the right threads. + const BaseKvmCPUParams * const p( + dynamic_cast(params())); + + setupCounters(); + + if (p->usePerfOverflow) + runTimer.reset(new PerfKvmTimer(hwCycles, + KVM_TIMER_SIGNAL, + p->hostFactor, + p->hostFreq)); + else + runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC, + p->hostFactor, + p->hostFreq)); + } void @@ -1044,6 +1061,13 @@ BaseKvmCPU::flushCoalescedMMIO() /** * Cycle timer overflow when running in KVM. Forces the KVM syscall to * exit with EINTR and allows us to run the event queue. + * + * @warn This function might not be called since some kernels don't + * seem to deliver signals when the signal is only unmasked when + * running in KVM. This doesn't matter though since we are only + * interested in getting KVM to exit, which happens as expected. See + * setupSignalHandler() and kvmRun() for details about KVM signal + * handling. */ static void onTimerOverflow(int signo, siginfo_t *si, void *data) @@ -1079,20 +1103,22 @@ BaseKvmCPU::setupSignalHandler() panic("KVM: Failed to setup vCPU instruction signal handler\n"); sigset_t sigset; - if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1) + if (pthread_sigmask(SIG_BLOCK, NULL, &sigset) == -1) panic("KVM: Failed get signal mask\n"); // Request KVM to setup the same signal mask as we're currently - // running with. We'll sometimes need to mask the KVM_TIMER_SIGNAL - // to cause immediate exits from KVM after servicing IO - // requests. See kvmRun(). + // running with except for the KVM control signals. We'll + // sometimes need to raise the KVM_TIMER_SIGNAL to cause immediate + // exits from KVM after servicing IO requests. See kvmRun(). + sigdelset(&sigset, KVM_TIMER_SIGNAL); + sigdelset(&sigset, KVM_INST_SIGNAL); setSignalMask(&sigset); // Mask our control signals so they aren't delivered unless we're // actually executing inside KVM. sigaddset(&sigset, KVM_TIMER_SIGNAL); sigaddset(&sigset, KVM_INST_SIGNAL); - if (sigprocmask(SIG_SETMASK, &sigset, NULL) == -1) + if (pthread_sigmask(SIG_SETMASK, &sigset, NULL) == -1) panic("KVM: Failed mask the KVM control signals\n"); } -- cgit v1.2.3