kvm: Service events in the instruction event queues
[gem5.git] / src / cpu / kvm / base.cc
index 3bfe44cf43264f2594430f2c21c8f2e91d69c9fc..d43288f4f7b0d12118e0164ae4fcb4944094c094 100644 (file)
@@ -46,6 +46,7 @@
 #include <csignal>
 #include <ostream>
 
+#include "arch/mmapped_ipr.hh"
 #include "arch/utility.hh"
 #include "cpu/kvm/base.hh"
 #include "debug/Checkpoint.hh"
 
 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),
@@ -82,7 +77,9 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
       _kvmRun(NULL), mmioRing(NULL),
       pageSize(sysconf(_SC_PAGE_SIZE)),
       tickEvent(*this),
+      activeInstPeriod(0),
       perfControlledByTimer(params->usePerfOverflow),
+      hostFreq(params->hostFreq),
       hostFactor(params->hostFactor),
       drainManager(NULL),
       ctrInsts(0)
@@ -103,11 +100,11 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
         runTimer.reset(new PerfKvmTimer(hwCycles,
                                         KVM_TIMER_SIGNAL,
                                         params->hostFactor,
-                                        params->clock));
+                                        params->hostFreq));
     else
         runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC,
                                          params->hostFactor,
-                                         params->clock));
+                                         params->hostFreq));
 }
 
 BaseKvmCPU::~BaseKvmCPU()
@@ -200,6 +197,16 @@ BaseKvmCPU::regStats()
         .desc("total number of KVM exits")
         ;
 
+    numVMHalfEntries
+        .name(name() + ".numVMHalfEntries")
+        .desc("number of KVM entries to finalize pending operations")
+        ;
+
+    numExitSignal
+        .name(name() + ".numExitSignal")
+        .desc("exits due to signal delivery")
+        ;
+
     numMMIO
         .name(name() + ".numMMIO")
         .desc("number of VM exits due to memory mapped IO")
@@ -410,8 +417,7 @@ BaseKvmCPU::activateContext(ThreadID thread_num, Cycles delay)
     assert(_status == Idle);
     assert(!tickEvent.scheduled());
 
-    numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend)
-        * hostFactor;
+    numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend);
 
     schedule(tickEvent, clockEdge(delay));
     _status = Running;
@@ -505,6 +511,10 @@ BaseKvmCPU::tick()
           // 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
@@ -523,10 +533,18 @@ BaseKvmCPU::tick()
 
           // Enter into the RunningService state unless the
           // simulation was stopped by a timer.
-          if (_kvmRun->exit_reason !=  KVM_EXIT_INTR)
+          if (_kvmRun->exit_reason !=  KVM_EXIT_INTR) {
               _status = RunningService;
-          else
+          } else {
+              ++numExitSignal;
               _status = Running;
+          }
+
+          // Service any pending instruction events. The vCPU should
+          // have exited in time for the event using the instruction
+          // counter configured by setupInstStop().
+          comInstEventQueue[0]->serviceEvents(ctrInsts);
+          system->instEventQueue.serviceEvents(system->totalNumInsts);
 
           if (tryDrain())
               _status = Idle;
@@ -574,6 +592,8 @@ BaseKvmCPU::kvmRun(Tick ticks)
         // then immediately exits.
         DPRINTF(KvmRun, "KVM: Delivering IO without full guest entry\n");
 
+        ++numVMHalfEntries;
+
         // This signal is always masked while we are executing in gem5
         // and gets unmasked temporarily as soon as we enter into
         // KVM. See setSignalMask() and setupSignalHandler().
@@ -622,6 +642,7 @@ BaseKvmCPU::kvmRun(Tick ticks)
         // 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);
@@ -935,12 +956,32 @@ BaseKvmCPU::handleKvmExitFailEntry()
 Tick
 BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write)
 {
+    ThreadContext *tc(thread->getTC());
+    syncThreadContext();
+
     mmio_req.setPhys(paddr, size, Request::UNCACHEABLE, dataMasterId());
+    // Some architectures do need to massage physical addresses a bit
+    // before they are inserted into the memory system. This enables
+    // APIC accesses on x86 and m5ops where supported through a MMIO
+    // interface.
+    BaseTLB::Mode tlb_mode(write ? BaseTLB::Write : BaseTLB::Read);
+    Fault fault(tc->getDTBPtr()->finalizePhysical(&mmio_req, tc, tlb_mode));
+    if (fault != NoFault)
+        warn("Finalization of MMIO address failed: %s\n", fault->name());
+
 
     const MemCmd cmd(write ? MemCmd::WriteReq : MemCmd::ReadReq);
     Packet pkt(&mmio_req, cmd);
     pkt.dataStatic(data);
-    return dataPort.sendAtomic(&pkt);
+
+    if (mmio_req.isMmappedIpr()) {
+        const Cycles ipr_delay(write ?
+                             TheISA::handleIprWrite(tc, &pkt) :
+                             TheISA::handleIprRead(tc, &pkt));
+        return clockEdge(ipr_delay);
+    } else {
+        return dataPort.sendAtomic(&pkt);
+    }
 }
 
 void
@@ -1000,6 +1041,26 @@ BaseKvmCPU::flushCoalescedMMIO()
     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()
 {
@@ -1009,7 +1070,13 @@ 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)
@@ -1021,11 +1088,12 @@ BaseKvmCPU::setupSignalHandler()
     // 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
@@ -1078,12 +1146,7 @@ BaseKvmCPU::setupCounters()
     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
@@ -1118,3 +1181,48 @@ BaseKvmCPU::ioctlRun()
                   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;
+}