diff options
author | Andreas Sandberg <Andreas.Sandberg@ARM.com> | 2013-04-22 13:20:32 -0400 |
---|---|---|
committer | Andreas Sandberg <Andreas.Sandberg@ARM.com> | 2013-04-22 13:20:32 -0400 |
commit | f8f66fa3df3ef5b4e30ee86d33a4e95476ba425a (patch) | |
tree | d3880f7bfda96783c2535d3e690026b6d6478efb /src/cpu/kvm/base.cc | |
parent | dc83d234254861f142854bdf523581101c3d5c8d (diff) | |
download | gem5-f8f66fa3df3ef5b4e30ee86d33a4e95476ba425a.tar.xz |
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 (limited to 'src/cpu/kvm/base.cc')
-rw-r--r-- | src/cpu/kvm/base.cc | 53 |
1 files changed, 33 insertions, 20 deletions
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(); -} |