Halting in userspace requires a relatively cumbersome mechanism to signal the
halted VCPU. Implementing halt in kernel should be relatively straight
forward and it eliminates the need for the signaling
Signed-off-by: Gregory Haskins <[EMAIL PROTECTED]>
---
drivers/kvm/kvm.h | 3 ++
drivers/kvm/kvm_main.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/kvm/svm.c | 7 +---
drivers/kvm/vmx.c | 7 +---
4 files changed, 90 insertions(+), 12 deletions(-)
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h
index 57b6d14..7030f0d 100644
--- a/drivers/kvm/kvm.h
+++ b/drivers/kvm/kvm.h
@@ -275,6 +275,7 @@ struct kvm_stat {
u32 signal_exits;
u32 irq_window_exits;
u32 halt_exits;
+ u32 halt_wakeup;
u32 request_irq_exits;
u32 irq_exits;
u32 light_exits;
@@ -357,6 +358,7 @@ struct kvm_vcpu_irq {
int pending;
int deferred;
int guest_cpu;
+ wait_queue_head_t wq;
};
struct kvm_lapic {
@@ -636,6 +638,7 @@ void kvm_mmu_module_exit(void);
int kvm_apicbus_send(struct kvm *kvm, int dest, int trig_mode, int level,
int dest_mode, int delivery_mode, int vector);
+int kvm_vcpu_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run);
void kvm_mmu_destroy(struct kvm_vcpu *vcpu);
int kvm_mmu_create(struct kvm_vcpu *vcpu);
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c
index 79b6477..59f94cf 100644
--- a/drivers/kvm/kvm_main.c
+++ b/drivers/kvm/kvm_main.c
@@ -69,6 +69,7 @@ static struct kvm_stats_debugfs_item {
{ "signal_exits", STAT_OFFSET(signal_exits) },
{ "irq_window", STAT_OFFSET(irq_window_exits) },
{ "halt_exits", STAT_OFFSET(halt_exits) },
+ { "halt_wakeup", STAT_OFFSET(halt_wakeup) },
{ "request_irq", STAT_OFFSET(request_irq_exits) },
{ "irq_exits", STAT_OFFSET(irq_exits) },
{ "light_exits", STAT_OFFSET(light_exits) },
@@ -334,6 +335,7 @@ static struct kvm *kvm_create_vm(void)
memset(&vcpu->irq, 0, sizeof(vcpu->irq));
spin_lock_init(&vcpu->irq.lock);
vcpu->irq.deferred = -1;
+ init_waitqueue_head(&vcpu->irq.wq);
vcpu->cpu = -1;
vcpu->kvm = kvm;
@@ -2434,6 +2436,79 @@ out1:
}
/*
+ * The vCPU has executed a HLT instruction with in-kernel mode enabled.
+ */
+static int kvm_vcpu_kern_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int r = 1;
+
+ spin_lock_irq(&vcpu->irq.lock);
+ __add_wait_queue(&vcpu->irq.wq, &wait);
+
+ /*
+ * We will block until either an interrupt or a signal wakes us up
+ */
+ while(!__kvm_vcpu_irq_pending(vcpu)
+ && !signal_pending(current)
+ && !kvm_run->request_interrupt_window) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_unlock_irq(&vcpu->irq.lock);
+ vcpu_put(vcpu);
+
+ schedule();
+
+ vcpu_load(vcpu);
+ spin_lock_irq(&vcpu->irq.lock);
+ }
+
+ /*
+ * If userspace is waiting for an injection point, we cant sleep here
+ */
+ if (kvm_run->request_interrupt_window
+ && !__kvm_vcpu_irq_pending(vcpu)) {
+ kvm_run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN;
+ r = 0;
+ }
+
+ __remove_wait_queue(&vcpu->irq.wq, &wait);
+ __set_current_state(TASK_RUNNING);
+ spin_unlock_irq(&vcpu->irq.lock);
+
+ return r;
+}
+
+/*
+ * The vCPU has executed a HLT instruction.
+ */
+int kvm_vcpu_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ int r = -EINVAL;
+
+ ++vcpu->stat.halt_exits;
+
+ if (vcpu->kvm->enable_kernel_pic)
+ /*
+ * If the in-kernel PIC is enabled, we will perform HLT
+ * in-kernel as well
+ */
+ r = kvm_vcpu_kern_halt(vcpu, kvm_run);
+ else {
+ /*
+ * Else, we decide to go back to userspace or vmenter depending
+ * on whether there are interrupts currently pending or not
+ */
+ r = kvm_vcpu_irq_pending(vcpu) ? 1 : 0;
+ if (!r)
+ kvm_run->exit_reason = KVM_EXIT_HLT;
+ }
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_halt);
+
+/*
* This function is invoked whenever we want to interrupt a vcpu that is
* currently executing in guest-mode. It currently is a no-op because
* the simple delivery of the IPI to execute this function accomplishes our
@@ -2481,6 +2556,16 @@ static void kvm_vcpu_intr(struct kvm_irqsink *this,
BUG_ON(direct_ipi == smp_processor_id());
++vcpu->stat.guest_preempt;
}
+
+ /*
+ * If the CPU is halted it will be waiting for a wake-up
+ */
+ if (waitqueue_active(&vcpu->irq.wq)) {
+ wake_up_interruptible_sync(&vcpu->irq.wq);
+ set_tsk_need_resched(current);
+ ++vcpu->stat.halt_wakeup;
+ }
+
} else
++vcpu->stat.irq_ignored;
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c
index 61dfee2..bdc5d98 100644
--- a/drivers/kvm/svm.c
+++ b/drivers/kvm/svm.c
@@ -1098,12 +1098,7 @@ static int halt_interception(struct kvm_vcpu *vcpu,
struct kvm_run *kvm_run)
{
vcpu->svm->next_rip = vcpu->svm->vmcb->save.rip + 1;
skip_emulated_instruction(vcpu);
- if (kvm_vcpu_irq_pending(vcpu))
- return 1;
-
- kvm_run->exit_reason = KVM_EXIT_HLT;
- ++vcpu->stat.halt_exits;
- return 0;
+ return kvm_vcpu_halt(vcpu, kvm_run);
}
static int vmmcall_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c
index 1dd8c9c..b7d756d 100644
--- a/drivers/kvm/vmx.c
+++ b/drivers/kvm/vmx.c
@@ -2022,12 +2022,7 @@ static int handle_interrupt_window(struct kvm_vcpu *vcpu,
static int handle_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
skip_emulated_instruction(vcpu);
- if (kvm_vcpu_irq_pending(vcpu))
- return 1;
-
- kvm_run->exit_reason = KVM_EXIT_HLT;
- ++vcpu->stat.halt_exits;
- return 0;
+ return kvm_vcpu_halt(vcpu, kvm_run);
}
static int handle_vmcall(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel