diff options
author | Avi Kivity <avi@qumranet.com> | 2007-10-25 10:52:32 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2008-01-30 11:01:20 -0500 |
commit | b93463aa59d67b21b4921e30bd5c5dcc7c35ffbd (patch) | |
tree | 8de9a2789624d27155d8a94c93a23c01f473fea7 | |
parent | b209749f528488c4c0d20a42c0fbcbf49e6933b3 (diff) |
KVM: Accelerated apic support
This adds a mechanism for exposing the virtual apic tpr to the guest, and a
protocol for letting the guest update the tpr without causing a vmexit if
conditions allow (e.g. there is no interrupt pending with a higher priority
than the new tpr).
Signed-off-by: Avi Kivity <avi@qumranet.com>
-rw-r--r-- | arch/x86/kvm/lapic.c | 51 | ||||
-rw-r--r-- | arch/x86/kvm/lapic.h | 6 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 55 | ||||
-rw-r--r-- | include/linux/kvm.h | 7 | ||||
-rw-r--r-- | include/linux/kvm_para.h | 2 |
5 files changed, 120 insertions, 1 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 50c3f3a8dd3d..e7513bb98af1 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c | |||
@@ -815,7 +815,8 @@ void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8) | |||
815 | 815 | ||
816 | if (!apic) | 816 | if (!apic) |
817 | return; | 817 | return; |
818 | apic_set_tpr(apic, ((cr8 & 0x0f) << 4)); | 818 | apic_set_tpr(apic, ((cr8 & 0x0f) << 4) |
819 | | (apic_get_reg(apic, APIC_TASKPRI) & 4)); | ||
819 | } | 820 | } |
820 | 821 | ||
821 | u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) | 822 | u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) |
@@ -1104,3 +1105,51 @@ void kvm_migrate_apic_timer(struct kvm_vcpu *vcpu) | |||
1104 | hrtimer_start(timer, timer->expires, HRTIMER_MODE_ABS); | 1105 | hrtimer_start(timer, timer->expires, HRTIMER_MODE_ABS); |
1105 | } | 1106 | } |
1106 | EXPORT_SYMBOL_GPL(kvm_migrate_apic_timer); | 1107 | EXPORT_SYMBOL_GPL(kvm_migrate_apic_timer); |
1108 | |||
1109 | void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu) | ||
1110 | { | ||
1111 | u32 data; | ||
1112 | void *vapic; | ||
1113 | |||
1114 | if (!irqchip_in_kernel(vcpu->kvm) || !vcpu->arch.apic->vapic_addr) | ||
1115 | return; | ||
1116 | |||
1117 | vapic = kmap_atomic(vcpu->arch.apic->vapic_page, KM_USER0); | ||
1118 | data = *(u32 *)(vapic + offset_in_page(vcpu->arch.apic->vapic_addr)); | ||
1119 | kunmap_atomic(vapic, KM_USER0); | ||
1120 | |||
1121 | apic_set_tpr(vcpu->arch.apic, data & 0xff); | ||
1122 | } | ||
1123 | |||
1124 | void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu) | ||
1125 | { | ||
1126 | u32 data, tpr; | ||
1127 | int max_irr, max_isr; | ||
1128 | struct kvm_lapic *apic; | ||
1129 | void *vapic; | ||
1130 | |||
1131 | if (!irqchip_in_kernel(vcpu->kvm) || !vcpu->arch.apic->vapic_addr) | ||
1132 | return; | ||
1133 | |||
1134 | apic = vcpu->arch.apic; | ||
1135 | tpr = apic_get_reg(apic, APIC_TASKPRI) & 0xff; | ||
1136 | max_irr = apic_find_highest_irr(apic); | ||
1137 | if (max_irr < 0) | ||
1138 | max_irr = 0; | ||
1139 | max_isr = apic_find_highest_isr(apic); | ||
1140 | if (max_isr < 0) | ||
1141 | max_isr = 0; | ||
1142 | data = (tpr & 0xff) | ((max_isr & 0xf0) << 8) | (max_irr << 24); | ||
1143 | |||
1144 | vapic = kmap_atomic(vcpu->arch.apic->vapic_page, KM_USER0); | ||
1145 | *(u32 *)(vapic + offset_in_page(vcpu->arch.apic->vapic_addr)) = data; | ||
1146 | kunmap_atomic(vapic, KM_USER0); | ||
1147 | } | ||
1148 | |||
1149 | void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr) | ||
1150 | { | ||
1151 | if (!irqchip_in_kernel(vcpu->kvm)) | ||
1152 | return; | ||
1153 | |||
1154 | vcpu->arch.apic->vapic_addr = vapic_addr; | ||
1155 | } | ||
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 447b654aefbb..676c396c9cee 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h | |||
@@ -18,6 +18,8 @@ struct kvm_lapic { | |||
18 | struct kvm_vcpu *vcpu; | 18 | struct kvm_vcpu *vcpu; |
19 | struct page *regs_page; | 19 | struct page *regs_page; |
20 | void *regs; | 20 | void *regs; |
21 | gpa_t vapic_addr; | ||
22 | struct page *vapic_page; | ||
21 | }; | 23 | }; |
22 | int kvm_create_lapic(struct kvm_vcpu *vcpu); | 24 | int kvm_create_lapic(struct kvm_vcpu *vcpu); |
23 | void kvm_free_lapic(struct kvm_vcpu *vcpu); | 25 | void kvm_free_lapic(struct kvm_vcpu *vcpu); |
@@ -41,4 +43,8 @@ int kvm_lapic_enabled(struct kvm_vcpu *vcpu); | |||
41 | int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); | 43 | int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); |
42 | void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec); | 44 | void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec); |
43 | 45 | ||
46 | void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr); | ||
47 | void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu); | ||
48 | void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu); | ||
49 | |||
44 | #endif | 50 | #endif |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c2b80884447e..e7eac27adb7f 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -1173,6 +1173,19 @@ long kvm_arch_vcpu_ioctl(struct file *filp, | |||
1173 | r = 0; | 1173 | r = 0; |
1174 | break; | 1174 | break; |
1175 | }; | 1175 | }; |
1176 | case KVM_SET_VAPIC_ADDR: { | ||
1177 | struct kvm_vapic_addr va; | ||
1178 | |||
1179 | r = -EINVAL; | ||
1180 | if (!irqchip_in_kernel(vcpu->kvm)) | ||
1181 | goto out; | ||
1182 | r = -EFAULT; | ||
1183 | if (copy_from_user(&va, argp, sizeof va)) | ||
1184 | goto out; | ||
1185 | r = 0; | ||
1186 | kvm_lapic_set_vapic_addr(vcpu, va.vapic_addr); | ||
1187 | break; | ||
1188 | } | ||
1176 | default: | 1189 | default: |
1177 | r = -EINVAL; | 1190 | r = -EINVAL; |
1178 | } | 1191 | } |
@@ -2214,6 +2227,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) | |||
2214 | } | 2227 | } |
2215 | 2228 | ||
2216 | switch (nr) { | 2229 | switch (nr) { |
2230 | case KVM_HC_VAPIC_POLL_IRQ: | ||
2231 | ret = 0; | ||
2232 | break; | ||
2217 | default: | 2233 | default: |
2218 | ret = -KVM_ENOSYS; | 2234 | ret = -KVM_ENOSYS; |
2219 | break; | 2235 | break; |
@@ -2421,6 +2437,29 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu, | |||
2421 | vcpu->arch.irq_summary == 0); | 2437 | vcpu->arch.irq_summary == 0); |
2422 | } | 2438 | } |
2423 | 2439 | ||
2440 | static void vapic_enter(struct kvm_vcpu *vcpu) | ||
2441 | { | ||
2442 | struct kvm_lapic *apic = vcpu->arch.apic; | ||
2443 | struct page *page; | ||
2444 | |||
2445 | if (!apic || !apic->vapic_addr) | ||
2446 | return; | ||
2447 | |||
2448 | page = gfn_to_page(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT); | ||
2449 | vcpu->arch.apic->vapic_page = page; | ||
2450 | } | ||
2451 | |||
2452 | static void vapic_exit(struct kvm_vcpu *vcpu) | ||
2453 | { | ||
2454 | struct kvm_lapic *apic = vcpu->arch.apic; | ||
2455 | |||
2456 | if (!apic || !apic->vapic_addr) | ||
2457 | return; | ||
2458 | |||
2459 | kvm_release_page_dirty(apic->vapic_page); | ||
2460 | mark_page_dirty(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT); | ||
2461 | } | ||
2462 | |||
2424 | static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | 2463 | static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) |
2425 | { | 2464 | { |
2426 | int r; | 2465 | int r; |
@@ -2435,6 +2474,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
2435 | vcpu->arch.mp_state = VCPU_MP_STATE_RUNNABLE; | 2474 | vcpu->arch.mp_state = VCPU_MP_STATE_RUNNABLE; |
2436 | } | 2475 | } |
2437 | 2476 | ||
2477 | vapic_enter(vcpu); | ||
2478 | |||
2438 | preempted: | 2479 | preempted: |
2439 | if (vcpu->guest_debug.enabled) | 2480 | if (vcpu->guest_debug.enabled) |
2440 | kvm_x86_ops->guest_debug_pre(vcpu); | 2481 | kvm_x86_ops->guest_debug_pre(vcpu); |
@@ -2444,6 +2485,14 @@ again: | |||
2444 | if (unlikely(r)) | 2485 | if (unlikely(r)) |
2445 | goto out; | 2486 | goto out; |
2446 | 2487 | ||
2488 | if (vcpu->requests) | ||
2489 | if (test_and_clear_bit(KVM_REQ_REPORT_TPR_ACCESS, | ||
2490 | &vcpu->requests)) { | ||
2491 | kvm_run->exit_reason = KVM_EXIT_TPR_ACCESS; | ||
2492 | r = 0; | ||
2493 | goto out; | ||
2494 | } | ||
2495 | |||
2447 | kvm_inject_pending_timer_irqs(vcpu); | 2496 | kvm_inject_pending_timer_irqs(vcpu); |
2448 | 2497 | ||
2449 | preempt_disable(); | 2498 | preempt_disable(); |
@@ -2469,6 +2518,8 @@ again: | |||
2469 | else | 2518 | else |
2470 | kvm_x86_ops->inject_pending_vectors(vcpu, kvm_run); | 2519 | kvm_x86_ops->inject_pending_vectors(vcpu, kvm_run); |
2471 | 2520 | ||
2521 | kvm_lapic_sync_to_vapic(vcpu); | ||
2522 | |||
2472 | vcpu->guest_mode = 1; | 2523 | vcpu->guest_mode = 1; |
2473 | kvm_guest_enter(); | 2524 | kvm_guest_enter(); |
2474 | 2525 | ||
@@ -2506,6 +2557,8 @@ again: | |||
2506 | if (vcpu->arch.exception.pending && kvm_x86_ops->exception_injected(vcpu)) | 2557 | if (vcpu->arch.exception.pending && kvm_x86_ops->exception_injected(vcpu)) |
2507 | vcpu->arch.exception.pending = false; | 2558 | vcpu->arch.exception.pending = false; |
2508 | 2559 | ||
2560 | kvm_lapic_sync_from_vapic(vcpu); | ||
2561 | |||
2509 | r = kvm_x86_ops->handle_exit(kvm_run, vcpu); | 2562 | r = kvm_x86_ops->handle_exit(kvm_run, vcpu); |
2510 | 2563 | ||
2511 | if (r > 0) { | 2564 | if (r > 0) { |
@@ -2527,6 +2580,8 @@ out: | |||
2527 | 2580 | ||
2528 | post_kvm_run_save(vcpu, kvm_run); | 2581 | post_kvm_run_save(vcpu, kvm_run); |
2529 | 2582 | ||
2583 | vapic_exit(vcpu); | ||
2584 | |||
2530 | return r; | 2585 | return r; |
2531 | } | 2586 | } |
2532 | 2587 | ||
diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 850f5ef76636..b27a381db5e8 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h | |||
@@ -216,6 +216,11 @@ struct kvm_tpr_access_ctl { | |||
216 | __u32 reserved[8]; | 216 | __u32 reserved[8]; |
217 | }; | 217 | }; |
218 | 218 | ||
219 | /* for KVM_SET_VAPIC_ADDR */ | ||
220 | struct kvm_vapic_addr { | ||
221 | __u64 vapic_addr; | ||
222 | }; | ||
223 | |||
219 | #define KVMIO 0xAE | 224 | #define KVMIO 0xAE |
220 | 225 | ||
221 | /* | 226 | /* |
@@ -291,5 +296,7 @@ struct kvm_tpr_access_ctl { | |||
291 | #define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2) | 296 | #define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2) |
292 | /* Available with KVM_CAP_VAPIC */ | 297 | /* Available with KVM_CAP_VAPIC */ |
293 | #define KVM_TPR_ACCESS_REPORTING _IOWR(KVMIO, 0x92, struct kvm_tpr_access_ctl) | 298 | #define KVM_TPR_ACCESS_REPORTING _IOWR(KVMIO, 0x92, struct kvm_tpr_access_ctl) |
299 | /* Available with KVM_CAP_VAPIC */ | ||
300 | #define KVM_SET_VAPIC_ADDR _IOW(KVMIO, 0x93, struct kvm_vapic_addr) | ||
294 | 301 | ||
295 | #endif | 302 | #endif |
diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h index e4db25ffdb52..6af91a506fbf 100644 --- a/include/linux/kvm_para.h +++ b/include/linux/kvm_para.h | |||
@@ -12,6 +12,8 @@ | |||
12 | /* Return values for hypercalls */ | 12 | /* Return values for hypercalls */ |
13 | #define KVM_ENOSYS 1000 | 13 | #define KVM_ENOSYS 1000 |
14 | 14 | ||
15 | #define KVM_HC_VAPIC_POLL_IRQ 1 | ||
16 | |||
15 | #ifdef __KERNEL__ | 17 | #ifdef __KERNEL__ |
16 | /* | 18 | /* |
17 | * hypercalls use architecture specific | 19 | * hypercalls use architecture specific |