@@ -307,9 +307,18 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return -EINVAL;
}
+/**
+ * kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled
+ * @v: The VCPU pointer
+ *
+ * If the guest CPU is not waiting for interrupts (or is waiting for interrupts
+ * but there actually is an incoming interrupt), then it is by definition
+ * runnable.
+ */
int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
{
- return 0;
+ return !!v->arch.virt_irq ||
+ !v->arch.wait_for_interrupts;
}
static inline int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
@@ -385,6 +394,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
int ret;
for (;;) {
+ if (vcpu->arch.wait_for_interrupts)
+ goto wait_for_interrupts;
+
if (run->exit_reason == KVM_EXIT_MMIO) {
ret = kvm_handle_mmio_return(vcpu, vcpu->run);
if (ret)
@@ -420,6 +432,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
vcpu_load(vcpu);
}
+wait_for_interrupts:
+ if (vcpu->arch.wait_for_interrupts)
+ kvm_vcpu_block(vcpu);
+
if (signal_pending(current) && !(run->exit_reason)) {
run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN;
break;
@@ -460,6 +476,8 @@ static int kvm_arch_vm_ioctl_irq_line(struct kvm *kvm,
if (irq_level->level) {
vcpu->arch.virt_irq |= mask;
vcpu->arch.wait_for_interrupts = 0;
+ if (waitqueue_active(&vcpu->wq))
+ wake_up_interruptible(&vcpu->wq);
} else
vcpu->arch.virt_irq &= ~mask;
@@ -333,8 +333,19 @@ unsupp_err_out:
return -EINVAL;
}
+/**
+ * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest
+ * @vcpu: the vcpu pointer
+ * @run: the kvm_run structure pointer
+ *
+ * Simply sets the wait_for_interrupts flag on the vcpu structure, which will
+ * halt execution of world-switches and schedule other host processes until
+ * there is an incoming IRQ or FIQ to the VM.
+ */
int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
+ trace_kvm_wfi(vcpu->arch.regs.pc);
+ vcpu->arch.wait_for_interrupts = 1;
return 0;
}
@@ -104,6 +104,21 @@ TRACE_EVENT(kvm_irq_line,
__entry->level, __entry->vcpu_idx)
);
+TRACE_EVENT(kvm_wfi,
+ TP_PROTO(unsigned long vcpu_pc),
+ TP_ARGS(vcpu_pc),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, vcpu_pc )
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_pc = vcpu_pc;
+ ),
+
+ TP_printk("guest executed wfi at: 0x%08lx", __entry->vcpu_pc)
+);
+
#endif /* _TRACE_KVM_H */
When the guest executes a WFI instruction the operation is trapped to KVM, which emulates the instruction in software. There is no correlation between a guest executing a WFI instruction and actually puttin the hardware into a low-power mode, since a KVM guest is essentially a process and the WFI instruction can be seen as 'sleep' call from this process. Therefore, we flag the VCPU to be in wait_for_interrupts mode and call the main KVM function kvm_vcpu_block() function. This function will put the thread on a wait-queue and call schedule. When an interrupt comes in through KVM_IRQ_LINE (see previous patch) we signal the VCPU thread and unflag the VCPU to no longer wait for interrupts. All calls to kvm_arch_vcpu_ioctl_run() result in a call to kvm_vcpu_block() as long as the VCPU is in wfi-mode. Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com> --- arch/arm/kvm/arm.c | 20 +++++++++++++++++++- arch/arm/kvm/arm_emulate.c | 11 +++++++++++ arch/arm/kvm/trace.h | 15 +++++++++++++++ 3 files changed, 45 insertions(+), 1 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html