aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAvi Kivity <avi@qumranet.com>2007-10-25 10:52:32 -0400
committerAvi Kivity <avi@qumranet.com>2008-01-30 11:01:20 -0500
commitb93463aa59d67b21b4921e30bd5c5dcc7c35ffbd (patch)
tree8de9a2789624d27155d8a94c93a23c01f473fea7
parentb209749f528488c4c0d20a42c0fbcbf49e6933b3 (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.c51
-rw-r--r--arch/x86/kvm/lapic.h6
-rw-r--r--arch/x86/kvm/x86.c55
-rw-r--r--include/linux/kvm.h7
-rw-r--r--include/linux/kvm_para.h2
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
821u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu) 822u64 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}
1106EXPORT_SYMBOL_GPL(kvm_migrate_apic_timer); 1107EXPORT_SYMBOL_GPL(kvm_migrate_apic_timer);
1108
1109void 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
1124void 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
1149void 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};
22int kvm_create_lapic(struct kvm_vcpu *vcpu); 24int kvm_create_lapic(struct kvm_vcpu *vcpu);
23void kvm_free_lapic(struct kvm_vcpu *vcpu); 25void kvm_free_lapic(struct kvm_vcpu *vcpu);
@@ -41,4 +43,8 @@ int kvm_lapic_enabled(struct kvm_vcpu *vcpu);
41int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); 43int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu);
42void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec); 44void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec);
43 45
46void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
47void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
48void 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
2440static 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
2452static 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
2424static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) 2463static 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
2438preempted: 2479preempted:
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 */
220struct 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