cpu: o3: replace issueLatency with bool pipelined
[gem5.git] / src / cpu / kvm / base.cc
index 1149a37043c0728a9e7cb4e5b4ef101edb4a4c4c..e09c4b7f2c764f5dca30ef35fb8c49debeb140d2 100644 (file)
@@ -63,8 +63,6 @@
 /* Used by some KVM macros */
 #define PAGE_SIZE pageSize
 
-static volatile __thread bool timerOverflowed = false;
-
 BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
     : BaseCPU(params),
       vm(*params->kvmVM),
@@ -87,8 +85,14 @@ BaseKvmCPU::BaseKvmCPU(BaseKvmCPUParams *params)
         panic("KVM: Failed to determine host page size (%i)\n",
               errno);
 
-    thread = new SimpleThread(this, 0, params->system,
-                              params->itb, params->dtb, params->isa[0]);
+    if (FullSystem)
+        thread = new SimpleThread(this, 0, params->system, params->itb, params->dtb,
+                                  params->isa[0]);
+    else
+        thread = new SimpleThread(this, /* thread_num */ 0, params->system,
+                                  params->workload[0], params->itb,
+                                  params->dtb, params->isa[0]);
+
     thread->setStatus(ThreadContext::Halted);
     tc = thread->getTC();
     threadContexts.push_back(tc);
@@ -114,8 +118,6 @@ BaseKvmCPU::init()
     // initialize CPU, including PC
     if (FullSystem && !switchedOut())
         TheISA::initCPU(tc, tc->contextId());
-
-    mmio_req.setThreadContext(tc->contextId(), 0);
 }
 
 void
@@ -178,6 +180,8 @@ BaseKvmCPU::startupThread()
     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();
@@ -186,11 +190,11 @@ BaseKvmCPU::startupThread()
 
     if (p->usePerfOverflow)
         runTimer.reset(new PerfKvmTimer(hwCycles,
-                                        KVM_TIMER_SIGNAL,
+                                        KVM_KICK_SIGNAL,
                                         p->hostFactor,
                                         p->hostFreq));
     else
-        runTimer.reset(new PosixKvmTimer(KVM_TIMER_SIGNAL, CLOCK_MONOTONIC,
+        runTimer.reset(new PosixKvmTimer(KVM_KICK_SIGNAL, CLOCK_MONOTONIC,
                                          p->hostFactor,
                                          p->hostFreq));
 
@@ -415,6 +419,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;
@@ -423,9 +434,9 @@ BaseKvmCPU::wakeup()
 }
 
 void
-BaseKvmCPU::activateContext(ThreadID thread_num, Cycles delay)
+BaseKvmCPU::activateContext(ThreadID thread_num)
 {
-    DPRINTF(Kvm, "ActivateContext %d (%d cycles)\n", thread_num, delay);
+    DPRINTF(Kvm, "ActivateContext %d\n", thread_num);
 
     assert(thread_num == 0);
     assert(thread);
@@ -435,7 +446,7 @@ BaseKvmCPU::activateContext(ThreadID thread_num, Cycles delay)
 
     numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend);
 
-    schedule(tickEvent, clockEdge(delay));
+    schedule(tickEvent, clockEdge(Cycles(0)));
     _status = Running;
 }
 
@@ -451,7 +462,7 @@ BaseKvmCPU::suspendContext(ThreadID thread_num)
     if (_status == Idle)
         return;
 
-    assert(_status == Running);
+    assert(_status == Running || _status == RunningServiceCompletion);
 
     // The tick event may no be scheduled if the quest has requested
     // the monitor to wait for interrupts. The normal CPU models can
@@ -607,7 +618,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
@@ -617,16 +627,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
@@ -634,6 +646,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());
@@ -659,27 +679,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_INST_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;
@@ -982,7 +993,8 @@ BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write)
     ThreadContext *tc(thread->getTC());
     syncThreadContext();
 
-    mmio_req.setPhys(paddr, size, Request::UNCACHEABLE, dataMasterId());
+    Request mmio_req(paddr, size, Request::UNCACHEABLE, dataMasterId());
+    mmio_req.setThreadContext(tc->contextId(), 0);
     // 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
@@ -998,11 +1010,20 @@ BaseKvmCPU::doMMIOAccess(Addr paddr, void *data, int size, bool write)
     pkt.dataStatic(data);
 
     if (mmio_req.isMmappedIpr()) {
+        // 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));
+        threadContextDirty = true;
         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);
     }
 }
@@ -1065,10 +1086,9 @@ BaseKvmCPU::flushCoalescedMMIO()
 }
 
 /**
- * Cycle timer overflow when running in KVM. Forces the KVM syscall to
- * exit with EINTR and allows us to run the event queue.
+ * Dummy handler for KVM kick signals.
  *
- * @warn This function might not be called since some kernels don't
+ * @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
@@ -1076,18 +1096,7 @@ BaseKvmCPU::flushCoalescedMMIO()
  * handling.
  */
 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)
+onKickSignal(int signo, siginfo_t *si, void *data)
 {
 }
 
@@ -1097,33 +1106,25 @@ 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)
+    if (sigaction(KVM_KICK_SIGNAL, &sa, NULL) == -1)
         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 (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 except for the KVM control signals. We'll
-    // sometimes need to raise the KVM_TIMER_SIGNAL to cause immediate
-    // exits from KVM after servicing IO requests. See kvmRun().
-    sigdelset(&sigset, KVM_TIMER_SIGNAL);
-    sigdelset(&sigset, KVM_INST_SIGNAL);
+    // 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 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);
+    sigaddset(&sigset, KVM_KICK_SIGNAL);
     if (pthread_sigmask(SIG_SETMASK, &sigset, NULL) == -1)
         panic("KVM: Failed mask the KVM control signals\n");
 }
@@ -1266,7 +1267,7 @@ BaseKvmCPU::setupInstCounter(uint64_t period)
                           hwCycles);
 
     if (period)
-        hwInstructions.enableSignals(KVM_INST_SIGNAL);
+        hwInstructions.enableSignals(KVM_KICK_SIGNAL);
 
     activeInstPeriod = period;
 }