diff -r d4953a954a2d -r 0bdbf5b24c62 src/cpu/kvm/base.hh --- a/src/cpu/kvm/base.hh Thu Sep 19 17:56:35 2013 +0200 +++ b/src/cpu/kvm/base.hh Thu Sep 19 17:56:47 2013 +0200 @@ -53,6 +53,9 @@ /** Signal to use to trigger time-based exits from KVM */ #define KVM_TIMER_SIGNAL SIGRTMIN +/** Signal to use to trigger instruction-based exits from KVM */ +#define KVM_INST_SIGNAL (SIGRTMIN+1) + // forward declarations class ThreadContext; struct BaseKvmCPUParams; @@ -615,9 +618,6 @@ */ bool discardPendingSignal(int signum) const; - /** Setup hardware performance counters */ - void setupCounters(); - /** Try to drain the CPU if a drain is pending */ bool tryDrain(); @@ -647,11 +647,59 @@ TickEvent tickEvent; + /** + * Setup an instruction break if there is one pending. + * + * Check if there are pending instruction breaks in the CPU's + * instruction event queue and schedule an instruction break using + * PerfEvent. + * + * @note This method doesn't currently handle the main system + * instruction event queue. + */ + void setupInstStop(); + /** @{ */ - /** Guest performance counters */ + /** Setup hardware performance counters */ + void setupCounters(); + + /** + * Setup the guest instruction counter. + * + * Setup the guest instruction counter and optionally request a + * signal every N instructions executed by the guest. This method + * will re-attach the counter if the counter has already been + * attached and its sampling settings have changed. + * + * @param period Signal period, set to 0 to disable signaling. + */ + void setupInstCounter(uint64_t period = 0); + + /** Currently active instruction count breakpoint */ + uint64_t activeInstPeriod; + + /** + * Guest cycle counter. + * + * This is the group leader of all performance counters measuring + * the guest system. It can be used in conjunction with the + * PerfKvmTimer (see perfControlledByTimer) to trigger exits from + * KVM. + */ PerfKvmCounter hwCycles; + + /** + * Guest instruction counter. + * + * This counter is typically only used to measure the number of + * instructions executed by the guest. However, it can also be + * used to trigger exits from KVM if the configuration script + * requests an exit after a certain number of instructions. + * + * @see setupInstBreak + * @see scheduleInstStop + */ PerfKvmCounter hwInstructions; - /** @} */ /** * Does the runTimer control the performance counters? @@ -661,6 +709,7 @@ * exits. */ bool perfControlledByTimer; + /** @} */ /** * Timer used to force execution into the monitor after a diff -r d4953a954a2d -r 0bdbf5b24c62 src/cpu/kvm/base.cc --- a/src/cpu/kvm/base.cc Thu Sep 19 17:56:35 2013 +0200 +++ b/src/cpu/kvm/base.cc Thu Sep 19 17:56:47 2013 +0200 @@ -71,6 +71,11 @@ timerOverflowed = true; } +static void +onInstEvent(int signo, siginfo_t *si, void *data) +{ +} + BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params) : BaseCPU(params), vm(*params->kvmVM), @@ -83,6 +88,7 @@ _kvmRun(NULL), mmioRing(NULL), pageSize(sysconf(_SC_PAGE_SIZE)), tickEvent(*this), + activeInstPeriod(0), perfControlledByTimer(params->usePerfOverflow), hostFreq(params->hostFreq), hostFactor(params->hostFactor), @@ -516,6 +522,10 @@ // We might need to update the KVM state. syncKvmState(); + // Setup any pending instruction count breakpoints using + // PerfEvent. + setupInstStop(); + DPRINTF(KvmRun, "Entering KVM...\n"); if (drainManager) { // Force an immediate exit from KVM after completing @@ -637,6 +647,7 @@ // sure we don't deliver it immediately next time we try to // enter into KVM. discardPendingSignal(KVM_TIMER_SIGNAL); + discardPendingSignal(KVM_INST_SIGNAL); const uint64_t hostCyclesExecuted(getHostCycles() - baseCycles); const uint64_t simCyclesExecuted(hostCyclesExecuted * hostFactor); @@ -1044,7 +1055,13 @@ sa.sa_sigaction = onTimerOverflow; sa.sa_flags = SA_SIGINFO | SA_RESTART; if (sigaction(KVM_TIMER_SIGNAL, &sa, NULL) == -1) - panic("KVM: Failed to setup vCPU signal handler\n"); + panic("KVM: Failed to setup vCPU timer signal handler\n"); + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = onInstEvent; + sa.sa_flags = SA_SIGINFO | SA_RESTART; + if (sigaction(KVM_INST_SIGNAL, &sa, NULL) == -1) + panic("KVM: Failed to setup vCPU instruction signal handler\n"); sigset_t sigset; if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1) @@ -1056,11 +1073,12 @@ // requests. See kvmRun(). setSignalMask(&sigset); - // Mask the KVM_TIMER_SIGNAL so it isn't delivered unless we're + // 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) - panic("KVM: Failed mask the KVM timer signal\n"); + panic("KVM: Failed mask the KVM control signals\n"); } bool @@ -1113,12 +1131,7 @@ hwCycles.attach(cfgCycles, 0); // TID (0 => currentThread) - DPRINTF(Kvm, "Attaching instruction counter...\n"); - PerfKvmCounterConfig cfgInstructions(PERF_TYPE_HARDWARE, - PERF_COUNT_HW_INSTRUCTIONS); - hwInstructions.attach(cfgInstructions, - 0, // TID (0 => currentThread) - hwCycles); + setupInstCounter(); } bool @@ -1153,3 +1166,49 @@ errno); } } + +void +BaseKvmCPU::setupInstStop() +{ + + if (comInstEventQueue[0]->empty()) { + setupInstCounter(0); + } else { + const uint64_t next(comInstEventQueue[0]->nextTick()); + + assert(next > ctrInsts); + setupInstCounter(next - ctrInsts); + } +} + +void +BaseKvmCPU::setupInstCounter(uint64_t period) +{ + // No need to do anything if we aren't attaching for the first + // time or the period isn't changing. + if (period == activeInstPeriod && hwInstructions.attached()) + return; + + PerfKvmCounterConfig cfgInstructions(PERF_TYPE_HARDWARE, + PERF_COUNT_HW_INSTRUCTIONS); + + if (period) { + // Setup a sampling counter if that has been requested. + cfgInstructions.wakeupEvents(1) + .samplePeriod(period); + } + + // We need to detach and re-attach the counter to reliably change + // sampling settings. See PerfKvmCounter::period() for details. + if (hwInstructions.attached()) + hwInstructions.detach(); + assert(hwCycles.attached()); + hwInstructions.attach(cfgInstructions, + 0, // TID (0 => currentThread) + hwCycles); + + if (period) + hwInstructions.enableSignals(KVM_INST_SIGNAL); + + activeInstPeriod = period; +}