volatile bool timerOverflowed = false;
-static void
-onTimerOverflow(int signo, siginfo_t *si, void *data)
-{
- timerOverflowed = true;
-}
-
BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
: BaseCPU(params),
vm(*params->kvmVM),
_kvmRun(NULL), mmioRing(NULL),
pageSize(sysconf(_SC_PAGE_SIZE)),
tickEvent(*this),
+ activeInstPeriod(0),
perfControlledByTimer(params->usePerfOverflow),
hostFreq(params->hostFreq),
hostFactor(params->hostFactor),
// 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
// 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);
return ticks;
}
+/**
+ * Cycle timer overflow when running in KVM. Forces the KVM syscall to
+ * exit with EINTR and allows us to run the event queue.
+ */
+static void
+onTimerOverflow(int signo, siginfo_t *si, void *data)
+{
+ timerOverflowed = true;
+}
+
+/**
+ * Instruction counter overflow when running in KVM. Forces the KVM
+ * syscall to exit with EINTR and allows us to handle instruction
+ * count events.
+ */
+static void
+onInstEvent(int signo, siginfo_t *si, void *data)
+{
+}
+
void
BaseKvmCPU::setupSignalHandler()
{
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)
// 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
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
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;
+}
/** 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;
*/
bool discardPendingSignal(int signum) const;
- /** Setup hardware performance counters */
- void setupCounters();
-
/** Try to drain the CPU if a drain is pending */
bool tryDrain();
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?
* exits.
*/
bool perfControlledByTimer;
+ /** @} */
/**
* Timer used to force execution into the monitor after a