summaryrefslogtreecommitdiff
path: root/src/cpu/kvm/base.hh
diff options
context:
space:
mode:
authorAndreas Sandberg <andreas@sandberg.pp.se>2013-06-11 09:24:51 +0200
committerAndreas Sandberg <andreas@sandberg.pp.se>2013-06-11 09:24:51 +0200
commit4f002930bc03125b9b886233985993291f5a4730 (patch)
tree43d8eb037db7316629ed109add8386ac94d7a5b2 /src/cpu/kvm/base.hh
parentdf059f45a0cbd230ad00f6da24cfc5d228430e16 (diff)
downloadgem5-4f002930bc03125b9b886233985993291f5a4730.tar.xz
kvm: Don't handle IO and execute in the same tick
We currently execute instructions in the guest and then handle any IO request right after we break out of the virtualized environment. This has the effect of executing IO requests in the exact same tick as the first instruction in the sequence that was just run. There seem to be cases where this simplification upsets some timing-sensitive devices. This changeset splits execute and IO (and other services) across multiple ticks. This is implemented by adding a separate RunningService state to the CPU state machine. When a VM requires service, it enters into this state and pending IO is then serviced in the future instead of immediately. The delay between getting the request and servicing it depends on the number of cycles executed in the guest, which allows other components to catch up with the CPU.
Diffstat (limited to 'src/cpu/kvm/base.hh')
-rw-r--r--src/cpu/kvm/base.hh147
1 files changed, 138 insertions, 9 deletions
diff --git a/src/cpu/kvm/base.hh b/src/cpu/kvm/base.hh
index 81b24a378..42a7eca2b 100644
--- a/src/cpu/kvm/base.hh
+++ b/src/cpu/kvm/base.hh
@@ -41,6 +41,7 @@
#define __CPU_KVM_BASE_HH__
#include <memory>
+#include <csignal>
#include "base/statistics.hh"
#include "cpu/kvm/perfevent.hh"
@@ -133,11 +134,67 @@ class BaseKvmCPU : public BaseCPU
KvmVM &vm;
protected:
+ /**
+ *
+ * @dot
+ * digraph {
+ * Idle;
+ * Running;
+ * RunningService;
+ * RunningServiceCompletion;
+ *
+ * Idle -> Idle;
+ * Idle -> Running [label="activateContext()", URL="\ref activateContext"];
+ * Running -> Running [label="tick()", URL="\ref tick"];
+ * Running -> RunningService [label="tick()", URL="\ref tick"];
+ * Running -> Idle [label="suspendContext()", URL="\ref suspendContext"];
+ * Running -> Idle [label="drain()", URL="\ref drain"];
+ * Idle -> Running [label="drainResume()", URL="\ref drainResume"];
+ * RunningService -> RunningServiceCompletion [label="handleKvmExit()", URL="\ref handleKvmExit"];
+ * RunningServiceCompletion -> Running [label="tick()", URL="\ref tick"];
+ * RunningServiceCompletion -> RunningService [label="tick()", URL="\ref tick"];
+ * }
+ * @enddot
+ */
enum Status {
- /** Context not scheduled in KVM */
+ /** Context not scheduled in KVM.
+ *
+ * The CPU generally enters this state when the guest execute
+ * an instruction that halts the CPU (e.g., WFI on ARM or HLT
+ * on X86) if KVM traps this instruction. Ticks are not
+ * scheduled in this state.
+ *
+ * @see suspendContext()
+ */
Idle,
- /** Running normally */
+ /** Running normally.
+ *
+ * This is the normal run state of the CPU. KVM will be
+ * entered next time tick() is called.
+ */
Running,
+ /** Requiring service at the beginning of the next cycle.
+ *
+ * The virtual machine has exited and requires service, tick()
+ * will call handleKvmExit() on the next cycle. The next state
+ * after running service is determined in handleKvmExit() and
+ * depends on what kind of service the guest requested:
+ * <ul>
+ * <li>IO/MMIO: RunningServiceCompletion
+ * <li>Halt: Idle
+ * <li>Others: Running
+ * </ul>
+ */
+ RunningService,
+ /** Service completion in progress.
+ *
+ * The VM has requested service that requires KVM to be
+ * entered once in order to get to a consistent state. This
+ * happens in handleKvmExit() or one of its friends after IO
+ * exits. After executing tick(), the CPU will transition into
+ * the Running or RunningService state.
+ */
+ RunningServiceCompletion,
};
/** CPU run state */
@@ -146,12 +203,8 @@ class BaseKvmCPU : public BaseCPU
/**
* Execute the CPU until the next event in the main event queue or
* until the guest needs service from gem5.
- *
- * @note This method is virtual in order to allow implementations
- * to check for architecture specific events (e.g., interrupts)
- * before entering the VM.
*/
- virtual void tick();
+ void tick();
/**
* Get the value of the hardware cycle counter in the guest.
@@ -177,10 +230,32 @@ class BaseKvmCPU : public BaseCPU
* can, for example, occur when the guest executes MMIO. A larger
* number is typically due to performance counter inaccuracies.
*
- * @param ticks Number of ticks to execute
+ * @note This method is virtual in order to allow implementations
+ * to check for architecture specific events (e.g., interrupts)
+ * before entering the VM.
+ *
+ * @note It is the response of the caller (normally tick()) to
+ * make sure that the KVM state is synchronized and that the TC is
+ * invalidated after entering KVM.
+ *
+ * @param ticks Number of ticks to execute, set to 0 to exit
+ * immediately after finishing pending operations.
* @return Number of ticks executed (see note)
*/
- Tick kvmRun(Tick ticks);
+ virtual Tick kvmRun(Tick ticks);
+
+ /**
+ * Request the CPU to run until draining completes.
+ *
+ * This function normally calls kvmRun(0) to make KVM finish
+ * pending MMIO operations. Architecures implementing
+ * archIsDrained() must override this method.
+ *
+ * @see BaseKvmCPU::archIsDrained()
+ *
+ * @return Number of ticks executed
+ */
+ virtual Tick kvmRunDrain();
/**
* Get a pointer to the kvm_run structure containing all the input
@@ -385,6 +460,24 @@ class BaseKvmCPU : public BaseCPU
/** @} */
/**
+ * Is the architecture specific code in a state that prevents
+ * draining?
+ *
+ * This method should return false if there are any pending events
+ * in the guest vCPU that won't be carried over to the gem5 state
+ * and thus will prevent correct checkpointing or CPU handover. It
+ * might, for example, check for pending interrupts that have been
+ * passed to the vCPU but not acknowledged by the OS. Architecures
+ * implementing this method <i>must</i> override
+ * kvmRunDrain().
+ *
+ * @see BaseKvmCPU::kvmRunDrain()
+ *
+ * @return true if the vCPU is drained, false otherwise.
+ */
+ virtual bool archIsDrained() const { return true; }
+
+ /**
* Inject a memory mapped IO request into gem5
*
* @param paddr Physical address
@@ -395,6 +488,21 @@ class BaseKvmCPU : public BaseCPU
*/
Tick doMMIOAccess(Addr paddr, void *data, int size, bool write);
+ /** @{ */
+ /**
+ * Set the signal mask used in kvmRun()
+ *
+ * This method allows the signal mask of the thread executing
+ * kvmRun() to be overridden inside the actual system call. This
+ * allows us to mask timer signals used to force KVM exits while
+ * in gem5.
+ *
+ * The signal mask can be disabled by setting it to NULL.
+ *
+ * @param mask Signals to mask
+ */
+ void setSignalMask(const sigset_t *mask);
+ /** @} */
/**
* @addtogroup KvmIoctl
@@ -499,9 +607,23 @@ class BaseKvmCPU : public BaseCPU
*/
void setupSignalHandler();
+ /**
+ * Discard a (potentially) pending signal.
+ *
+ * @param signum Signal to discard
+ * @return true if the signal was pending, false otherwise.
+ */
+ bool discardPendingSignal(int signum) const;
+
/** Setup hardware performance counters */
void setupCounters();
+ /** Try to drain the CPU if a drain is pending */
+ bool tryDrain();
+
+ /** Execute the KVM_RUN ioctl */
+ void ioctlRun();
+
/** KVM vCPU file descriptor */
int vcpuFD;
/** Size of MMAPed kvm_run area */
@@ -550,6 +672,13 @@ class BaseKvmCPU : public BaseCPU
float hostFactor;
+ /**
+ * Drain manager to use when signaling drain completion
+ *
+ * This pointer is non-NULL when draining and NULL otherwise.
+ */
+ DrainManager *drainManager;
+
public:
/* @{ */
Stats::Scalar numInsts;