diff options
author | Eddie Dong <eddie.dong@intel.com> | 2007-07-18 05:15:21 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-13 04:18:25 -0400 |
commit | b6958ce44a11a9e9425d2b67a653b1ca2a27796f (patch) | |
tree | 503478ef27254df9b6ea21d2a6a279a7dfe2ef04 | |
parent | 1fd4f2a5ed8f80cf6e23d2bdf78554f6a1ac7997 (diff) |
KVM: Emulate hlt in the kernel
By sleeping in the kernel when hlt is executed, we simplify the in-kernel
guest interrupt path considerably.
Signed-off-by: Gregory Haskins <ghaskins@novell.com>
Signed-off-by: Yaozu (Eddie) Dong <eddie.dong@intel.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
-rw-r--r-- | drivers/kvm/i8259.c | 3 | ||||
-rw-r--r-- | drivers/kvm/irq.c | 4 | ||||
-rw-r--r-- | drivers/kvm/kvm.h | 2 | ||||
-rw-r--r-- | drivers/kvm/kvm_main.c | 41 | ||||
-rw-r--r-- | drivers/kvm/svm.c | 9 | ||||
-rw-r--r-- | drivers/kvm/vmx.c | 8 | ||||
-rw-r--r-- | include/linux/kvm.h | 1 |
7 files changed, 57 insertions, 11 deletions
diff --git a/drivers/kvm/i8259.c b/drivers/kvm/i8259.c index 40ad10462238..ee6030dc5c04 100644 --- a/drivers/kvm/i8259.c +++ b/drivers/kvm/i8259.c | |||
@@ -413,8 +413,11 @@ static void picdev_read(struct kvm_io_device *this, | |||
413 | static void pic_irq_request(void *opaque, int level) | 413 | static void pic_irq_request(void *opaque, int level) |
414 | { | 414 | { |
415 | struct kvm *kvm = opaque; | 415 | struct kvm *kvm = opaque; |
416 | struct kvm_vcpu *vcpu = kvm->vcpus[0]; | ||
416 | 417 | ||
417 | pic_irqchip(kvm)->output = level; | 418 | pic_irqchip(kvm)->output = level; |
419 | if (vcpu) | ||
420 | kvm_vcpu_kick(vcpu); | ||
418 | } | 421 | } |
419 | 422 | ||
420 | struct kvm_pic *kvm_create_pic(struct kvm *kvm) | 423 | struct kvm_pic *kvm_create_pic(struct kvm *kvm) |
diff --git a/drivers/kvm/irq.c b/drivers/kvm/irq.c index 5265f8267b3b..e09cd65925d6 100644 --- a/drivers/kvm/irq.c +++ b/drivers/kvm/irq.c | |||
@@ -70,6 +70,10 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu) | |||
70 | { | 70 | { |
71 | int ipi_pcpu = vcpu->cpu; | 71 | int ipi_pcpu = vcpu->cpu; |
72 | 72 | ||
73 | if (waitqueue_active(&vcpu->wq)) { | ||
74 | wake_up_interruptible(&vcpu->wq); | ||
75 | ++vcpu->stat.halt_wakeup; | ||
76 | } | ||
73 | if (vcpu->guest_mode) | 77 | if (vcpu->guest_mode) |
74 | smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0, 0); | 78 | smp_call_function_single(ipi_pcpu, vcpu_kick_intr, vcpu, 0, 0); |
75 | } | 79 | } |
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h index 8d07a993af94..bb506b71797d 100644 --- a/drivers/kvm/kvm.h +++ b/drivers/kvm/kvm.h | |||
@@ -231,6 +231,7 @@ struct kvm_stat { | |||
231 | u32 signal_exits; | 231 | u32 signal_exits; |
232 | u32 irq_window_exits; | 232 | u32 irq_window_exits; |
233 | u32 halt_exits; | 233 | u32 halt_exits; |
234 | u32 halt_wakeup; | ||
234 | u32 request_irq_exits; | 235 | u32 request_irq_exits; |
235 | u32 irq_exits; | 236 | u32 irq_exits; |
236 | u32 light_exits; | 237 | u32 light_exits; |
@@ -353,6 +354,7 @@ struct kvm_vcpu { | |||
353 | gva_t mmio_fault_cr2; | 354 | gva_t mmio_fault_cr2; |
354 | struct kvm_pio_request pio; | 355 | struct kvm_pio_request pio; |
355 | void *pio_data; | 356 | void *pio_data; |
357 | wait_queue_head_t wq; | ||
356 | 358 | ||
357 | int sigset_active; | 359 | int sigset_active; |
358 | sigset_t sigset; | 360 | sigset_t sigset; |
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index ffbdadd87971..4384364fc0c8 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
@@ -76,6 +76,7 @@ static struct kvm_stats_debugfs_item { | |||
76 | { "signal_exits", STAT_OFFSET(signal_exits) }, | 76 | { "signal_exits", STAT_OFFSET(signal_exits) }, |
77 | { "irq_window", STAT_OFFSET(irq_window_exits) }, | 77 | { "irq_window", STAT_OFFSET(irq_window_exits) }, |
78 | { "halt_exits", STAT_OFFSET(halt_exits) }, | 78 | { "halt_exits", STAT_OFFSET(halt_exits) }, |
79 | { "halt_wakeup", STAT_OFFSET(halt_wakeup) }, | ||
79 | { "request_irq", STAT_OFFSET(request_irq_exits) }, | 80 | { "request_irq", STAT_OFFSET(request_irq_exits) }, |
80 | { "irq_exits", STAT_OFFSET(irq_exits) }, | 81 | { "irq_exits", STAT_OFFSET(irq_exits) }, |
81 | { "light_exits", STAT_OFFSET(light_exits) }, | 82 | { "light_exits", STAT_OFFSET(light_exits) }, |
@@ -248,6 +249,7 @@ int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) | |||
248 | vcpu->mmu.root_hpa = INVALID_PAGE; | 249 | vcpu->mmu.root_hpa = INVALID_PAGE; |
249 | vcpu->kvm = kvm; | 250 | vcpu->kvm = kvm; |
250 | vcpu->vcpu_id = id; | 251 | vcpu->vcpu_id = id; |
252 | init_waitqueue_head(&vcpu->wq); | ||
251 | 253 | ||
252 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); | 254 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); |
253 | if (!page) { | 255 | if (!page) { |
@@ -1307,15 +1309,41 @@ int emulate_instruction(struct kvm_vcpu *vcpu, | |||
1307 | } | 1309 | } |
1308 | EXPORT_SYMBOL_GPL(emulate_instruction); | 1310 | EXPORT_SYMBOL_GPL(emulate_instruction); |
1309 | 1311 | ||
1310 | int kvm_emulate_halt(struct kvm_vcpu *vcpu) | 1312 | /* |
1313 | * The vCPU has executed a HLT instruction with in-kernel mode enabled. | ||
1314 | */ | ||
1315 | static void kvm_vcpu_kernel_halt(struct kvm_vcpu *vcpu) | ||
1311 | { | 1316 | { |
1312 | if (vcpu->irq_summary || | 1317 | DECLARE_WAITQUEUE(wait, current); |
1313 | (irqchip_in_kernel(vcpu->kvm) && kvm_cpu_has_interrupt(vcpu))) | 1318 | |
1314 | return 1; | 1319 | add_wait_queue(&vcpu->wq, &wait); |
1320 | |||
1321 | /* | ||
1322 | * We will block until either an interrupt or a signal wakes us up | ||
1323 | */ | ||
1324 | while(!(irqchip_in_kernel(vcpu->kvm) && kvm_cpu_has_interrupt(vcpu)) | ||
1325 | && !vcpu->irq_summary | ||
1326 | && !signal_pending(current)) { | ||
1327 | set_current_state(TASK_INTERRUPTIBLE); | ||
1328 | vcpu_put(vcpu); | ||
1329 | schedule(); | ||
1330 | vcpu_load(vcpu); | ||
1331 | } | ||
1315 | 1332 | ||
1316 | vcpu->run->exit_reason = KVM_EXIT_HLT; | 1333 | remove_wait_queue(&vcpu->wq, &wait); |
1334 | set_current_state(TASK_RUNNING); | ||
1335 | } | ||
1336 | |||
1337 | int kvm_emulate_halt(struct kvm_vcpu *vcpu) | ||
1338 | { | ||
1317 | ++vcpu->stat.halt_exits; | 1339 | ++vcpu->stat.halt_exits; |
1318 | return 0; | 1340 | if (irqchip_in_kernel(vcpu->kvm)) { |
1341 | kvm_vcpu_kernel_halt(vcpu); | ||
1342 | return 1; | ||
1343 | } else { | ||
1344 | vcpu->run->exit_reason = KVM_EXIT_HLT; | ||
1345 | return 0; | ||
1346 | } | ||
1319 | } | 1347 | } |
1320 | EXPORT_SYMBOL_GPL(kvm_emulate_halt); | 1348 | EXPORT_SYMBOL_GPL(kvm_emulate_halt); |
1321 | 1349 | ||
@@ -2916,6 +2944,7 @@ static long kvm_dev_ioctl(struct file *filp, | |||
2916 | 2944 | ||
2917 | switch (ext) { | 2945 | switch (ext) { |
2918 | case KVM_CAP_IRQCHIP: | 2946 | case KVM_CAP_IRQCHIP: |
2947 | case KVM_CAP_HLT: | ||
2919 | r = 1; | 2948 | r = 1; |
2920 | break; | 2949 | break; |
2921 | default: | 2950 | default: |
diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c index d576451827e7..a347b61644cd 100644 --- a/drivers/kvm/svm.c +++ b/drivers/kvm/svm.c | |||
@@ -1398,9 +1398,12 @@ static void do_interrupt_requests(struct vcpu_svm *svm, | |||
1398 | static void post_kvm_run_save(struct vcpu_svm *svm, | 1398 | static void post_kvm_run_save(struct vcpu_svm *svm, |
1399 | struct kvm_run *kvm_run) | 1399 | struct kvm_run *kvm_run) |
1400 | { | 1400 | { |
1401 | kvm_run->ready_for_interrupt_injection | 1401 | if (irqchip_in_kernel(svm->vcpu.kvm)) |
1402 | = (svm->vcpu.interrupt_window_open && | 1402 | kvm_run->ready_for_interrupt_injection = 1; |
1403 | svm->vcpu.irq_summary == 0); | 1403 | else |
1404 | kvm_run->ready_for_interrupt_injection = | ||
1405 | (svm->vcpu.interrupt_window_open && | ||
1406 | svm->vcpu.irq_summary == 0); | ||
1404 | kvm_run->if_flag = (svm->vmcb->save.rflags & X86_EFLAGS_IF) != 0; | 1407 | kvm_run->if_flag = (svm->vmcb->save.rflags & X86_EFLAGS_IF) != 0; |
1405 | kvm_run->cr8 = get_cr8(&svm->vcpu); | 1408 | kvm_run->cr8 = get_cr8(&svm->vcpu); |
1406 | kvm_run->apic_base = kvm_get_apic_base(&svm->vcpu); | 1409 | kvm_run->apic_base = kvm_get_apic_base(&svm->vcpu); |
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index c4cc17cc00f7..7ec8cf84e6ea 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -1961,8 +1961,12 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu, | |||
1961 | kvm_run->if_flag = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) != 0; | 1961 | kvm_run->if_flag = (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) != 0; |
1962 | kvm_run->cr8 = get_cr8(vcpu); | 1962 | kvm_run->cr8 = get_cr8(vcpu); |
1963 | kvm_run->apic_base = kvm_get_apic_base(vcpu); | 1963 | kvm_run->apic_base = kvm_get_apic_base(vcpu); |
1964 | kvm_run->ready_for_interrupt_injection = (vcpu->interrupt_window_open && | 1964 | if (irqchip_in_kernel(vcpu->kvm)) |
1965 | vcpu->irq_summary == 0); | 1965 | kvm_run->ready_for_interrupt_injection = 1; |
1966 | else | ||
1967 | kvm_run->ready_for_interrupt_injection = | ||
1968 | (vcpu->interrupt_window_open && | ||
1969 | vcpu->irq_summary == 0); | ||
1966 | } | 1970 | } |
1967 | 1971 | ||
1968 | static int handle_interrupt_window(struct kvm_vcpu *vcpu, | 1972 | static int handle_interrupt_window(struct kvm_vcpu *vcpu, |
diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 997bb3e46f1e..b0a13d1b34cc 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h | |||
@@ -283,6 +283,7 @@ struct kvm_signal_mask { | |||
283 | * Extension capability list. | 283 | * Extension capability list. |
284 | */ | 284 | */ |
285 | #define KVM_CAP_IRQCHIP 0 | 285 | #define KVM_CAP_IRQCHIP 0 |
286 | #define KVM_CAP_HLT 1 | ||
286 | 287 | ||
287 | /* | 288 | /* |
288 | * ioctls for VM fds | 289 | * ioctls for VM fds |