summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpu/kvm/BaseKvmCPU.py1
-rw-r--r--src/cpu/kvm/base.cc53
-rw-r--r--src/cpu/kvm/base.hh15
-rw-r--r--src/cpu/kvm/perfevent.hh3
-rw-r--r--src/cpu/kvm/timer.cc34
-rw-r--r--src/cpu/kvm/timer.hh41
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