changeset 78c9adc85718 in /z/repo/gem5
details: http://repo.gem5.org/gem5?cmd=changeset;node=78c9adc85718
description:
        kvm: Add experimental support for a perf-based execution timer

        Add support for using the CPU cycle counter instead of a normal POSIX
        timer to generate timed exits to gem5. This should, in theory, provide
        better resolution when requesting timer signals.

        The perf-based timer requires a fairly recent kernel since it requires
        a working PERF_EVENT_IOC_PERIOD ioctl. This ioctl has existed in the
        kernel for a long time, but it used to be completely broken due to an
        inverted match when the kernel copied things from user
        space. Additionally, the ioctl does not change the sample period
        correctly on all kernel versions which implement it. It is currently
        only known to work reliably on kernel version 3.7 and above on ARM.

diffstat:

 src/cpu/kvm/BaseKvmCPU.py |   1 +
 src/cpu/kvm/base.cc       |  53 +++++++++++++++++++++++++++++-----------------
 src/cpu/kvm/base.hh       |  15 +++++++-----
 src/cpu/kvm/perfevent.hh  |   3 +-
 src/cpu/kvm/timer.cc      |  34 ++++++++++++++++++++++++++++++
 src/cpu/kvm/timer.hh      |  41 ++++++++++++++++++++++++++++++++++++
 6 files changed, 120 insertions(+), 27 deletions(-)

diffs (246 lines):

diff -r 64b653b3d72f -r 78c9adc85718 src/cpu/kvm/BaseKvmCPU.py
--- a/src/cpu/kvm/BaseKvmCPU.py Mon Apr 22 13:20:32 2013 -0400
+++ b/src/cpu/kvm/BaseKvmCPU.py Mon Apr 22 13:20:32 2013 -0400
@@ -69,4 +69,5 @@
         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 -r 64b653b3d72f -r 78c9adc85718 src/cpu/kvm/base.cc
--- a/src/cpu/kvm/base.cc       Mon Apr 22 13:20:32 2013 -0400
+++ b/src/cpu/kvm/base.cc       Mon Apr 22 13:20:32 2013 -0400
@@ -78,6 +78,7 @@
       _kvmRun(NULL), mmioRing(NULL),
       pageSize(sysconf(_SC_PAGE_SIZE)),
       tickEvent(*this),
+      perfControlledByTimer(params->usePerfOverflow),
       hostFactor(params->hostFactor)
 {
     if (pageSize == -1)
@@ -93,9 +94,15 @@
     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 @@
 
     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 @@
                                 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 @@
                           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 -r 64b653b3d72f -r 78c9adc85718 src/cpu/kvm/base.hh
--- a/src/cpu/kvm/base.hh       Mon Apr 22 13:20:32 2013 -0400
+++ b/src/cpu/kvm/base.hh       Mon Apr 22 13:20:32 2013 -0400
@@ -460,12 +460,6 @@
     /** 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 @@
     /** @} */
 
     /**
+     * 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 -r 64b653b3d72f -r 78c9adc85718 src/cpu/kvm/perfevent.hh
--- a/src/cpu/kvm/perfevent.hh  Mon Apr 22 13:20:32 2013 -0400
+++ b/src/cpu/kvm/perfevent.hh  Mon Apr 22 13:20:32 2013 -0400
@@ -217,7 +217,8 @@
      * 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 -r 64b653b3d72f -r 78c9adc85718 src/cpu/kvm/timer.cc
--- a/src/cpu/kvm/timer.cc      Mon Apr 22 13:20:32 2013 -0400
+++ b/src/cpu/kvm/timer.cc      Mon Apr 22 13:20:32 2013 -0400
@@ -110,3 +110,37 @@
 
     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 -r 64b653b3d72f -r 78c9adc85718 src/cpu/kvm/timer.hh
--- a/src/cpu/kvm/timer.hh      Mon Apr 22 13:20:32 2013 -0400
+++ b/src/cpu/kvm/timer.hh      Mon Apr 22 13:20:32 2013 -0400
@@ -42,6 +42,7 @@
 
 #include <ctime>
 
+#include "cpu/kvm/perfevent.hh"
 #include "sim/core.hh"
 
 /**
@@ -203,4 +204,44 @@
     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
_______________________________________________
gem5-dev mailing list
[email protected]
http://m5sim.org/mailman/listinfo/gem5-dev

Reply via email to