diff options
-rw-r--r-- | src/cpu/kvm/base.cc | 62 | ||||
-rw-r--r-- | src/cpu/kvm/base.hh | 14 | ||||
-rw-r--r-- | src/cpu/kvm/timer.cc | 24 |
3 files changed, 78 insertions, 22 deletions
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<BaseKvmCPU, + &BaseKvmCPU::startupThread>(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<const BaseKvmCPUParams *>(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"); } diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh index 8788d6c8a..5a0b80b15 100644 --- a/src/cpu/kvm/base.hh +++ b/src/cpu/kvm/base.hh @@ -618,6 +618,20 @@ class BaseKvmCPU : public BaseCPU */ bool discardPendingSignal(int signum) const; + /** + * Thread-specific initialization. + * + * Some KVM-related initialization requires us to know the TID of + * the thread that is going to execute our event queue. For + * example, when setting up timers, we need to know the TID of the + * thread executing in KVM in order to deliver the timer signal to + * that thread. This method is called as the first event in this + * SimObject's event queue. + * + * @see startup + */ + void startupThread(); + /** Try to drain the CPU if a drain is pending */ bool tryDrain(); diff --git a/src/cpu/kvm/timer.cc b/src/cpu/kvm/timer.cc index 6882063e4..cb48046f7 100644 --- a/src/cpu/kvm/timer.cc +++ b/src/cpu/kvm/timer.cc @@ -40,12 +40,30 @@ #include <algorithm> #include <csignal> #include <ctime> +#include <unistd.h> +#include <sys/syscall.h> #include "base/misc.hh" #include "base/trace.hh" #include "cpu/kvm/timer.hh" #include "debug/KvmTimer.hh" +/* According to timer_create(2), the value SIGEV_THREAD_ID can be used + * to specify which thread a timer signal gets delivered to. According + * to the man page, the member sigev_notify_thread is used to specify + * the TID. This member is currently not defined by default in + * siginfo.h on x86, so we define it here as a workaround. + */ +#ifndef sigev_notify_thread_id +#define sigev_notify_thread_id _sigev_un._tid +#endif + +static pid_t +gettid() +{ + return syscall(__NR_gettid); +} + /** * Minimum number of cycles that a host can spend in a KVM call (used * to calculate the resolution of some timers). @@ -62,11 +80,9 @@ PosixKvmTimer::PosixKvmTimer(int signo, clockid_t clockID, { struct sigevent sev; - // TODO: We should request signal delivery to thread instead of - // the process here. Unfortunately this seems to be broken, or at - // least not work as specified in the man page. - sev.sigev_notify = SIGEV_SIGNAL; + sev.sigev_notify = SIGEV_THREAD_ID; sev.sigev_signo = signo; + sev.sigev_notify_thread_id = gettid(); sev.sigev_value.sival_ptr = NULL; while (timer_create(clockID, &sev, &timer) == -1) { |