Skip to content

Commit b93463a

Browse files
committed
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>
1 parent b209749 commit b93463a

File tree

5 files changed

+120
-1
lines changed

5 files changed

+120
-1
lines changed

arch/x86/kvm/lapic.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,8 @@ void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8)
815815

816816
if (!apic)
817817
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));
819820
}
820821

821822
u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu)
@@ -1104,3 +1105,51 @@ void kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
11041105
hrtimer_start(timer, timer->expires, HRTIMER_MODE_ABS);
11051106
}
11061107
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+
}

arch/x86/kvm/lapic.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ struct kvm_lapic {
1818
struct kvm_vcpu *vcpu;
1919
struct page *regs_page;
2020
void *regs;
21+
gpa_t vapic_addr;
22+
struct page *vapic_page;
2123
};
2224
int kvm_create_lapic(struct kvm_vcpu *vcpu);
2325
void kvm_free_lapic(struct kvm_vcpu *vcpu);
@@ -41,4 +43,8 @@ int kvm_lapic_enabled(struct kvm_vcpu *vcpu);
4143
int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu);
4244
void kvm_apic_timer_intr_post(struct kvm_vcpu *vcpu, int vec);
4345

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+
4450
#endif

arch/x86/kvm/x86.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,19 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
11731173
r = 0;
11741174
break;
11751175
};
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+
}
11761189
default:
11771190
r = -EINVAL;
11781191
}
@@ -2214,6 +2227,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
22142227
}
22152228

22162229
switch (nr) {
2230+
case KVM_HC_VAPIC_POLL_IRQ:
2231+
ret = 0;
2232+
break;
22172233
default:
22182234
ret = -KVM_ENOSYS;
22192235
break;
@@ -2421,6 +2437,29 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu,
24212437
vcpu->arch.irq_summary == 0);
24222438
}
24232439

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+
24242463
static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
24252464
{
24262465
int r;
@@ -2435,6 +2474,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
24352474
vcpu->arch.mp_state = VCPU_MP_STATE_RUNNABLE;
24362475
}
24372476

2477+
vapic_enter(vcpu);
2478+
24382479
preempted:
24392480
if (vcpu->guest_debug.enabled)
24402481
kvm_x86_ops->guest_debug_pre(vcpu);
@@ -2444,6 +2485,14 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
24442485
if (unlikely(r))
24452486
goto out;
24462487

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+
24472496
kvm_inject_pending_timer_irqs(vcpu);
24482497

24492498
preempt_disable();
@@ -2469,6 +2518,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
24692518
else
24702519
kvm_x86_ops->inject_pending_vectors(vcpu, kvm_run);
24712520

2521+
kvm_lapic_sync_to_vapic(vcpu);
2522+
24722523
vcpu->guest_mode = 1;
24732524
kvm_guest_enter();
24742525

@@ -2506,6 +2557,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
25062557
if (vcpu->arch.exception.pending && kvm_x86_ops->exception_injected(vcpu))
25072558
vcpu->arch.exception.pending = false;
25082559

2560+
kvm_lapic_sync_from_vapic(vcpu);
2561+
25092562
r = kvm_x86_ops->handle_exit(kvm_run, vcpu);
25102563

25112564
if (r > 0) {
@@ -2527,6 +2580,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
25272580

25282581
post_kvm_run_save(vcpu, kvm_run);
25292582

2583+
vapic_exit(vcpu);
2584+
25302585
return r;
25312586
}
25322587

include/linux/kvm.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ struct kvm_tpr_access_ctl {
216216
__u32 reserved[8];
217217
};
218218

219+
/* for KVM_SET_VAPIC_ADDR */
220+
struct kvm_vapic_addr {
221+
__u64 vapic_addr;
222+
};
223+
219224
#define KVMIO 0xAE
220225

221226
/*
@@ -291,5 +296,7 @@ struct kvm_tpr_access_ctl {
291296
#define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2)
292297
/* Available with KVM_CAP_VAPIC */
293298
#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)
294301

295302
#endif

include/linux/kvm_para.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
/* Return values for hypercalls */
1313
#define KVM_ENOSYS 1000
1414

15+
#define KVM_HC_VAPIC_POLL_IRQ 1
16+
1517
#ifdef __KERNEL__
1618
/*
1719
* hypercalls use architecture specific

0 commit comments

Comments
 (0)