diff options
-rw-r--r-- | src/cpu/kvm/BaseKvmCPU.py | 1 | ||||
-rw-r--r-- | src/cpu/kvm/base.cc | 53 | ||||
-rw-r--r-- | src/cpu/kvm/base.hh | 15 | ||||
-rw-r--r-- | src/cpu/kvm/perfevent.hh | 3 | ||||
-rw-r--r-- | src/cpu/kvm/timer.cc | 34 | ||||
-rw-r--r-- | src/cpu/kvm/timer.hh | 41 |
6 files changed, 120 insertions, 27 deletions
diff --git a/src/cpu/kvm/BaseKvmCPU.py b/src/cpu/kvm/BaseKvmCPU.py index aa7ad4c2c..a8356ac5b 100644 --- a/src/cpu/kvm/BaseKvmCPU.py +++ b/src/cpu/kvm/BaseKvmCPU.py @@ -69,4 +69,5 @@ class BaseKvmCPU(BaseCPU): return True kvmVM = Param.KvmVM(Parent.any, 'KVM VM (i.e., shared memory domain)') + usePerfOverflow = Param.Bool(False, "Use perf event overflow counters (EXPERIMENTAL)") hostFactor = Param.Float(1.0, "Cycle scale factor") diff --git a/src/cpu/kvm/base.cc b/src/cpu/kvm/base.cc index 89f5e0f5d..2d993cf35 100644 --- a/src/cpu/kvm/base.cc +++ b/src/cpu/kvm/base.cc @@ -78,6 +78,7 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params) _kvmRun(NULL), mmioRing(NULL), pageSize(sysconf(_SC_PAGE_SIZE)), tickEvent(*this), + perfControlledByTimer(params->usePerfOverflow), hostFactor(params->hostFactor) { if (pageSize == -1) @@ -93,9 +94,15 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params) setupCounters(); setupSignalHandler(); - runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC, - params->hostFactor, - params->clock)); + if (params->usePerfOverflow) + runTimer.reset(new PerfKvmTimer(hwCycles, + KVM_TIMER_SIGNAL, + params->hostFactor, + params->clock)); + else + runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC, + params->hostFactor, + params->clock)); } BaseKvmCPU::~BaseKvmCPU() @@ -450,15 +457,25 @@ BaseKvmCPU::kvmRun(Tick ticks) DPRINTF(KvmRun, "KVM: Executing for %i ticks\n", ticks); timerOverflowed = false; + + // Arm the run timer and start the cycle timer if it isn't + // controlled by the overflow timer. Starting/stopping the cycle + // timer automatically starts the other perf timers as they are in + // the same counter group. runTimer->arm(ticks); - startCounters(); + if (!perfControlledByTimer) + hwCycles.start(); + if (ioctl(KVM_RUN) == -1) { if (errno != EINTR) panic("KVM: Failed to start virtual CPU (errno: %i)\n", errno); } - stopCounters(); + runTimer->disarm(); + if (!perfControlledByTimer) + hwCycles.stop(); + uint64_t cyclesExecuted(hwCycles.read() - baseCycles); Tick ticksExecuted(runTimer->ticksFromHostCycles(cyclesExecuted)); @@ -821,6 +838,17 @@ BaseKvmCPU::setupCounters() PERF_COUNT_HW_CPU_CYCLES); cfgCycles.disabled(true) .pinned(true); + + if (perfControlledByTimer) { + // We need to configure the cycles counter to send overflows + // since we are going to use it to trigger timer signals that + // trap back into m5 from KVM. In practice, this means that we + // need to set some non-zero sample period that gets + // overridden when the timer is armed. + cfgCycles.wakeupEvents(1) + .samplePeriod(42); + } + hwCycles.attach(cfgCycles, 0); // TID (0 => currentThread) @@ -831,18 +859,3 @@ BaseKvmCPU::setupCounters() 0, // TID (0 => currentThread) hwCycles); } - -void -BaseKvmCPU::startCounters() -{ - // We only need to start/stop the hwCycles counter since hwCycles - // and hwInstructions are a counter group with hwCycles as the - // group leader. - hwCycles.start(); -} - -void -BaseKvmCPU::stopCounters() -{ - hwCycles.stop(); -} diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh index 90ebae644..0554f913e 100644 --- a/src/cpu/kvm/base.hh +++ b/src/cpu/kvm/base.hh @@ -460,12 +460,6 @@ class BaseKvmCPU : public BaseCPU /** Setup hardware performance counters */ void setupCounters(); - /** @{ */ - /** Start/stop counting HW performance events */ - void startCounters(); - void stopCounters(); - /** @} */ - /** KVM vCPU file descriptor */ int vcpuFD; /** Size of MMAPed kvm_run area */ @@ -496,6 +490,15 @@ class BaseKvmCPU : public BaseCPU /** @} */ /** + * Does the runTimer control the performance counters? + * + * The run timer will automatically enable and disable performance + * counters if a PerfEvent-based timer is used to control KVM + * exits. + */ + bool perfControlledByTimer; + + /** * Timer used to force execution into the monitor after a * specified number of simulation tick equivalents have executed * in the guest. This counter generates the signal specified by diff --git a/src/cpu/kvm/perfevent.hh b/src/cpu/kvm/perfevent.hh index 8242cc071..eed900994 100644 --- a/src/cpu/kvm/perfevent.hh +++ b/src/cpu/kvm/perfevent.hh @@ -217,7 +217,8 @@ public: * like the new period isn't effective until after the next * counter overflow. If you use this method to change the sample * period, you will see one sample with the old period and then - * start sampling with the new period. + * start sampling with the new period. This problem was fixed for + * ARM in version 3.7 of the kernel. * * @warning This method doesn't work at all on some 2.6.3x kernels * since it has inverted check for the return value when copying diff --git a/src/cpu/kvm/timer.cc b/src/cpu/kvm/timer.cc index 059d70f6b..e1f74a552 100644 --- a/src/cpu/kvm/timer.cc +++ b/src/cpu/kvm/timer.cc @@ -110,3 +110,37 @@ PosixKvmTimer::calcResolution() return resolution; } + + +PerfKvmTimer::PerfKvmTimer(PerfKvmCounter &ctr, + int signo, float hostFactor, Tick hostFreq) + : BaseKvmTimer(signo, hostFactor, hostFreq), + hwOverflow(ctr) +{ + hwOverflow.enableSignals(signo); +} + +PerfKvmTimer::~PerfKvmTimer() +{ +} + +void +PerfKvmTimer::arm(Tick ticks) +{ + hwOverflow.period(hostCycles(ticks)); + hwOverflow.refresh(1); +} + +void +PerfKvmTimer::disarm() +{ + hwOverflow.stop(); +} + +Tick +PerfKvmTimer::calcResolution() +{ + // This is a bit arbitrary, but in practice, we can't really do + // anything useful in less than ~1000 anyway. + return ticksFromHostCycles(1000); +} diff --git a/src/cpu/kvm/timer.hh b/src/cpu/kvm/timer.hh index a5105e7fa..df60b7227 100644 --- a/src/cpu/kvm/timer.hh +++ b/src/cpu/kvm/timer.hh @@ -42,6 +42,7 @@ #include <ctime> +#include "cpu/kvm/perfevent.hh" #include "sim/core.hh" /** @@ -203,4 +204,44 @@ class PosixKvmTimer : public BaseKvmTimer timer_t timer; }; +/** + * PerfEvent based timer using the host's CPU cycle counter. + * + * @warning There is a known problem in some versions of the PerfEvent + * API that prevents the counter overflow period from being updated + * reliably, which might break this timer. See PerfKvmCounter::period() + * for details. + */ +class PerfKvmTimer : public BaseKvmTimer +{ + public: + /** + * Create a timer that uses an existing hardware cycle counter. + * + * @note The performance counter must be configured for overflow + * sampling, which in practice means that it must have a non-zero + * sample period. The initial sample period is ignored since + * period will be updated when arm() is called. + * + * @param ctr Attached performance counter configured for overflow + * reporting. + * @param signo Signal to deliver + * @param hostFactor Performance scaling factor + * @param hostFreq Clock frequency of the host + */ + PerfKvmTimer(PerfKvmCounter &ctr, + int signo, + float hostFactor, Tick hostFreq); + ~PerfKvmTimer(); + + void arm(Tick ticks); + void disarm(); + + protected: + Tick calcResolution(); + + private: + PerfKvmCounter &hwOverflow; +}; + #endif |