cpu: implement a bi-mode branch predictor
[gem5.git] / src / cpu / kvm / base.cc
index 1631fb54c67fff3196ccd58dabf7837e77eadf4f..2082670dc03735ff1c8ee0b8654cf445cce57644 100644 (file)
 /* Used by some KVM macros */
 #define PAGE_SIZE pageSize
 
-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),
@@ -83,8 +75,8 @@ 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)
@@ -98,18 +90,6 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
     thread->setStatus(ThreadContext::Halted);
     tc = thread->getTC();
     threadContexts.push_back(tc);
-
-    setupCounters();
-
-    if (params->usePerfOverflow)
-        runTimer.reset(new PerfKvmTimer(hwCycles,
-                                        KVM_TIMER_SIGNAL,
-                                        params->hostFactor,
-                                        params->hostFreq));
-    else
-        runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC,
-                                         params->hostFactor,
-                                         params->hostFreq));
 }
 
 BaseKvmCPU::~BaseKvmCPU()
@@ -156,10 +136,6 @@ BaseKvmCPU::startup()
     // point. Initialize virtual CPUs here instead.
     vcpuFD = vm.createVCPU(vcpuID);
 
-    // Setup signal handlers. This has to be done after the vCPU is
-    // created since it manipulates the vCPU signal mask.
-    setupSignalHandler();
-
     // Map the KVM run structure */
     vcpuMMapSize = kvm.getVCPUMMapSize();
     _kvmRun = (struct kvm_run *)mmap(0, vcpuMMapSize,
@@ -183,6 +159,41 @@ BaseKvmCPU::startup()
     }
 
     thread->startup();
+
+    Event *startupEvent(
+        new EventWrapper<BaseKvmCPU,
+                         &BaseKvmCPU::startupThread>(this, true));
+    schedule(startupEvent, curTick());
+}
+
+void
+BaseKvmCPU::startupThread()
+{
+    // Do thread-specific initialization. We need to setup signal
+    // delivery for counters and timers from within the thread that
+    // will execute the event queue to ensure that signals are
+    // delivered to the right threads.
+    const BaseKvmCPUParams * const p(
+        dynamic_cast<const BaseKvmCPUParams *>(params()));
+
+    vcpuThread = pthread_self();
+
+    // Setup signal handlers. This has to be done after the vCPU is
+    // created since it manipulates the vCPU signal mask.
+    setupSignalHandler();
+
+    setupCounters();
+
+    if (p->usePerfOverflow)
+        runTimer.reset(new PerfKvmTimer(hwCycles,
+                                        KVM_KICK_SIGNAL,
+                                        p->hostFactor,
+                                        p->hostFreq));
+    else
+        runTimer.reset(new PosixKvmTimer(KVM_KICK_SIGNAL, CLOCK_MONOTONIC,
+                                         p->hostFactor,
+                                         p->hostFreq));
+
 }
 
 void
@@ -404,6 +415,13 @@ void
 BaseKvmCPU::wakeup()
 {
     DPRINTF(Kvm, "wakeup()\n");
+    // This method might have been called from another
+    // context. Migrate to this SimObject's event queue when
+    // delivering the wakeup signal.
+    EventQueue::ScopedMigration migrate(eventQueue());
+
+    // Kick the vCPU to get it to come out of KVM.
+    kick();
 
     if (thread->status() != ThreadContext::Suspended)
         return;
@@ -511,11 +529,16 @@ BaseKvmCPU::tick()
 
       case RunningServiceCompletion:
       case Running: {
-          Tick ticksToExecute(mainEventQueue.nextTick() - curTick());
+          EventQueue *q = curEventQueue();
+          Tick ticksToExecute(q->nextTick() - curTick());
 
           // 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
@@ -527,6 +550,12 @@ BaseKvmCPU::tick()
               delay = kvmRun(ticksToExecute);
           }
 
+          // The CPU might have been suspended before entering into
+          // KVM. Assume that the CPU was suspended /before/ entering
+          // into KVM and skip the exit handling.
+          if (_status == Idle)
+              break;
+
           // Entering into KVM implies that we'll have to reload the thread
           // context from KVM if we want to access it. Flag the KVM state as
           // dirty with respect to the cached thread context.
@@ -541,6 +570,12 @@ BaseKvmCPU::tick()
               _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;
       } break;
@@ -579,7 +614,6 @@ BaseKvmCPU::kvmRun(Tick ticks)
 {
     Tick ticksExecuted;
     DPRINTF(KvmRun, "KVM: Executing for %i ticks\n", ticks);
-    timerOverflowed = false;
 
     if (ticks == 0) {
         // Settings ticks == 0 is a special case which causes an entry
@@ -589,16 +623,18 @@ BaseKvmCPU::kvmRun(Tick ticks)
 
         ++numVMHalfEntries;
 
-        // This signal is always masked while we are executing in gem5
-        // and gets unmasked temporarily as soon as we enter into
+        // Send a KVM_KICK_SIGNAL to the vCPU thread (i.e., this
+        // thread). The KVM control signal is masked while executing
+        // in gem5 and gets unmasked temporarily as when entering
         // KVM. See setSignalMask() and setupSignalHandler().
-        raise(KVM_TIMER_SIGNAL);
+        kick();
 
-        // Enter into KVM. KVM will check for signals after completing
-        // pending operations (IO). Since the KVM_TIMER_SIGNAL is
-        // pending, this forces an immediate exit into gem5 again. We
+        // Start the vCPU. KVM will check for signals after completing
+        // pending operations (IO). Since the KVM_KICK_SIGNAL is
+        // pending, this forces an immediate exit to gem5 again. We
         // don't bother to setup timers since this shouldn't actually
-        // execute any code in the guest.
+        // execute any code (other than completing half-executed IO
+        // instructions) in the guest.
         ioctlRun();
 
         // We always execute at least one cycle to prevent the
@@ -606,6 +642,14 @@ BaseKvmCPU::kvmRun(Tick ticks)
         // twice.
         ticksExecuted = clockPeriod();
     } else {
+        // This method is executed as a result of a tick event. That
+        // means that the event queue will be locked when entering the
+        // method. We temporarily unlock the event queue to allow
+        // other threads to steal control of this thread to inject
+        // interrupts. They will typically lock the queue and then
+        // force an exit from KVM by kicking the vCPU.
+        EventQueue::ScopedRelease release(curEventQueue());
+
         if (ticks < runTimer->resolution()) {
             DPRINTF(KvmRun, "KVM: Adjusting tick count (%i -> %i)\n",
                     ticks, runTimer->resolution());
@@ -631,26 +675,18 @@ BaseKvmCPU::kvmRun(Tick ticks)
         if (!perfControlledByTimer)
             hwCycles.stop();
 
-        // The timer signal may have been delivered after we exited
+        // The control signal may have been delivered after we exited
         // from KVM. It will be pending in that case since it is
         // masked when we aren't executing in KVM. Discard it to make
         // sure we don't deliver it immediately next time we try to
         // enter into KVM.
-        discardPendingSignal(KVM_TIMER_SIGNAL);
+        discardPendingSignal(KVM_KICK_SIGNAL);
 
         const uint64_t hostCyclesExecuted(getHostCycles() - baseCycles);
         const uint64_t simCyclesExecuted(hostCyclesExecuted * hostFactor);
         const uint64_t instsExecuted(hwInstructions.read() - baseInstrs);
         ticksExecuted = runTimer->ticksFromHostCycles(hostCyclesExecuted);
 
-        if (ticksExecuted < ticks &&
-            timerOverflowed &&
-            _kvmRun->exit_reason == KVM_EXIT_INTR) {
-            // TODO: We should probably do something clever here...
-            warn("KVM: Early timer event, requested %i ticks but got %i ticks.\n",
-                 ticks, ticksExecuted);
-        }
-
         /* Update statistics */
         numCycles += simCyclesExecuted;;
         numInsts += instsExecuted;
@@ -969,11 +1005,19 @@ BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write)
     pkt.dataStatic(data);
 
     if (mmio_req.isMmappedIpr()) {
-        if (write)
-            return TheISA::handleIprWrite(tc, &pkt);
-        else
-            return TheISA::handleIprRead(tc, &pkt);
+        // We currently assume that there is no need to migrate to a
+        // different event queue when doing IPRs. Currently, IPRs are
+        // only used for m5ops, so it should be a valid assumption.
+        const Cycles ipr_delay(write ?
+                             TheISA::handleIprWrite(tc, &pkt) :
+                             TheISA::handleIprRead(tc, &pkt));
+        return clockPeriod() * ipr_delay;
     } else {
+        // Temporarily lock and migrate to the event queue of the
+        // VM. This queue is assumed to "own" all devices we need to
+        // access if running in multi-core mode.
+        EventQueue::ScopedMigration migrate(vm.eventQueue());
+
         return dataPort.sendAtomic(&pkt);
     }
 }
@@ -1035,32 +1079,48 @@ BaseKvmCPU::flushCoalescedMMIO()
     return ticks;
 }
 
+/**
+ * Dummy handler for KVM kick signals.
+ *
+ * @note This function is usually not called since the kernel doesn't
+ * seem to deliver signals when the signal is only unmasked when
+ * running in KVM. This doesn't matter though since we are only
+ * interested in getting KVM to exit, which happens as expected. See
+ * setupSignalHandler() and kvmRun() for details about KVM signal
+ * handling.
+ */
+static void
+onKickSignal(int signo, siginfo_t *si, void *data)
+{
+}
+
 void
 BaseKvmCPU::setupSignalHandler()
 {
     struct sigaction sa;
 
     memset(&sa, 0, sizeof(sa));
-    sa.sa_sigaction = onTimerOverflow;
+    sa.sa_sigaction = onKickSignal;
     sa.sa_flags = SA_SIGINFO | SA_RESTART;
-    if (sigaction(KVM_TIMER_SIGNAL, &sa, NULL) == -1)
-        panic("KVM: Failed to setup vCPU signal handler\n");
+    if (sigaction(KVM_KICK_SIGNAL, &sa, NULL) == -1)
+        panic("KVM: Failed to setup vCPU timer signal handler\n");
 
     sigset_t sigset;
-    if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1)
+    if (pthread_sigmask(SIG_BLOCK, NULL, &sigset) == -1)
         panic("KVM: Failed get signal mask\n");
 
     // Request KVM to setup the same signal mask as we're currently
-    // running with. We'll sometimes need to mask the KVM_TIMER_SIGNAL
-    // to cause immediate exits from KVM after servicing IO
-    // requests. See kvmRun().
+    // running with except for the KVM control signal. We'll sometimes
+    // need to raise the KVM_KICK_SIGNAL to cause immediate exits from
+    // KVM after servicing IO requests. See kvmRun().
+    sigdelset(&sigset, KVM_KICK_SIGNAL);
     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);
-    if (sigprocmask(SIG_SETMASK, &sigset, NULL) == -1)
-        panic("KVM: Failed mask the KVM timer signal\n");
+    sigaddset(&sigset, KVM_KICK_SIGNAL);
+    if (pthread_sigmask(SIG_SETMASK, &sigset, NULL) == -1)
+        panic("KVM: Failed mask the KVM control signals\n");
 }
 
 bool
@@ -1100,6 +1160,12 @@ BaseKvmCPU::setupCounters()
     cfgCycles.disabled(true)
         .pinned(true);
 
+    // Try to exclude the host. We set both exclude_hv and
+    // exclude_host since different architectures use slightly
+    // different APIs in the kernel.
+    cfgCycles.exclude_hv(true)
+        .exclude_host(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
@@ -1113,12 +1179,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
@@ -1153,3 +1214,54 @@ 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);
+
+    // Try to exclude the host. We set both exclude_hv and
+    // exclude_host since different architectures use slightly
+    // different APIs in the kernel.
+    cfgInstructions.exclude_hv(true)
+        .exclude_host(true);
+
+    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_KICK_SIGNAL);
+
+    activeInstPeriod = period;
+}