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

Reply via email to