/* 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),
_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)
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()
// 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,
}
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
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;
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
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.
_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;
{
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
++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
// 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());
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;
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);
}
}
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
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
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
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;
+}