Skip to content

Commit

Permalink
Untangle TPR shadowing and APIC virtualization.
Browse files Browse the repository at this point in the history
This speeds up Windows guests tremendously.

The patch does:
Add a new tuneable 'hw.vmm.vmx.use_tpr_shadowing' to disable TLP shadowing.
Also add 'hw.vmm.vmx.cap.tpr_shadowing' to be able to query if TPR shadowing is used.

Detach the initialization of TPR shadowing from the initialization of APIC virtualization.
APIC virtualization still needs TPR shadowing, but not vice versa.
Any CPU that supports APIC virtualization should also support TPR shadowing.

When TPR shadowing is used, the APIC page of each vCPU is written to the VMCS_VIRTUAL_APIC field of the VMCS
so that the CPU can write directly to the page without intercept.

On vm exit, vlapic_update_ppr() is called to update the PPR.

Submitted by:	Yamagi Burmeister
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D22942
  • Loading branch information
mr authored and mr committed Mar 10, 2020
1 parent cc7fabd commit b7a1d0c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 18 deletions.
91 changes: 74 additions & 17 deletions sys/amd64/vmm/intel/vmx.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ static int cap_invpcid;
SYSCTL_INT(_hw_vmm_vmx_cap, OID_AUTO, invpcid, CTLFLAG_RD, &cap_invpcid,
0, "Guests are allowed to use INVPCID");

static int tpr_shadowing;
SYSCTL_INT(_hw_vmm_vmx_cap, OID_AUTO, tpr_shadowing, CTLFLAG_RD,
&tpr_shadowing, 0, "TPR shadowing support");

static int virtual_interrupt_delivery;
SYSCTL_INT(_hw_vmm_vmx_cap, OID_AUTO, virtual_interrupt_delivery, CTLFLAG_RD,
&virtual_interrupt_delivery, 0, "APICv virtual interrupt delivery support");
Expand Down Expand Up @@ -630,7 +634,7 @@ vmx_restore(void)
static int
vmx_init(int ipinum)
{
int error, use_tpr_shadow;
int error;
uint64_t basic, fixed0, fixed1, feature_control;
uint32_t tmp, procbased2_vid_bits;

Expand Down Expand Up @@ -753,6 +757,24 @@ vmx_init(int ipinum)
MSR_VMX_PROCBASED_CTLS2, PROCBASED2_ENABLE_INVPCID, 0,
&tmp) == 0);

/*
* Check support for TPR shadow.
*/
error = vmx_set_ctlreg(MSR_VMX_PROCBASED_CTLS,
MSR_VMX_TRUE_PROCBASED_CTLS, PROCBASED_USE_TPR_SHADOW, 0,
&tmp);
if (error == 0) {
tpr_shadowing = 1;
TUNABLE_INT_FETCH("hw.vmm.vmx.use_tpr_shadowing",
&tpr_shadowing);
}

if (tpr_shadowing) {
procbased_ctls |= PROCBASED_USE_TPR_SHADOW;
procbased_ctls &= ~PROCBASED_CR8_LOAD_EXITING;
procbased_ctls &= ~PROCBASED_CR8_STORE_EXITING;
}

/*
* Check support for virtual interrupt delivery.
*/
Expand All @@ -761,13 +783,9 @@ vmx_init(int ipinum)
PROCBASED2_APIC_REGISTER_VIRTUALIZATION |
PROCBASED2_VIRTUAL_INTERRUPT_DELIVERY);

use_tpr_shadow = (vmx_set_ctlreg(MSR_VMX_PROCBASED_CTLS,
MSR_VMX_TRUE_PROCBASED_CTLS, PROCBASED_USE_TPR_SHADOW, 0,
&tmp) == 0);

error = vmx_set_ctlreg(MSR_VMX_PROCBASED_CTLS2, MSR_VMX_PROCBASED_CTLS2,
procbased2_vid_bits, 0, &tmp);
if (error == 0 && use_tpr_shadow) {
if (error == 0 && tpr_shadowing) {
virtual_interrupt_delivery = 1;
TUNABLE_INT_FETCH("hw.vmm.vmx.use_apic_vid",
&virtual_interrupt_delivery);
Expand All @@ -778,13 +796,6 @@ vmx_init(int ipinum)
procbased_ctls2 |= procbased2_vid_bits;
procbased_ctls2 &= ~PROCBASED2_VIRTUALIZE_X2APIC_MODE;

/*
* No need to emulate accesses to %CR8 if virtual
* interrupt delivery is enabled.
*/
procbased_ctls &= ~PROCBASED_CR8_LOAD_EXITING;
procbased_ctls &= ~PROCBASED_CR8_STORE_EXITING;

/*
* Check for Posted Interrupts only if Virtual Interrupt
* Delivery is enabled.
Expand Down Expand Up @@ -1054,10 +1065,13 @@ vmx_vminit(struct vm *vm, pmap_t pmap)
vmx->ctx[i].guest_dr6 = DBREG_DR6_RESERVED1;
error += vmwrite(VMCS_GUEST_DR7, DBREG_DR7_RESERVED1);

if (virtual_interrupt_delivery) {
error += vmwrite(VMCS_APIC_ACCESS, APIC_ACCESS_ADDRESS);
if (tpr_shadowing) {
error += vmwrite(VMCS_VIRTUAL_APIC,
vtophys(&vmx->apic_page[i]));
}

if (virtual_interrupt_delivery) {
error += vmwrite(VMCS_APIC_ACCESS, APIC_ACCESS_ADDRESS);
error += vmwrite(VMCS_EOI_EXIT0, 0);
error += vmwrite(VMCS_EOI_EXIT1, 0);
error += vmwrite(VMCS_EOI_EXIT2, 0);
Expand Down Expand Up @@ -2661,6 +2675,12 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
SDT_PROBE3(vmm, vmx, exit, mwait, vmx, vcpu, vmexit);
vmexit->exitcode = VM_EXITCODE_MWAIT;
break;
case EXIT_REASON_TPR:
vlapic = vm_lapic(vmx->vm, vcpu);
vlapic_sync_tpr(vlapic);
vmexit->inst_length = 0;
handled = HANDLED;
break;
case EXIT_REASON_VMCALL:
case EXIT_REASON_VMCLEAR:
case EXIT_REASON_VMLAUNCH:
Expand Down Expand Up @@ -2946,6 +2966,16 @@ vmx_run(void *arg, int vcpu, register_t rip, pmap_t pmap,
break;
}

/*
* If TPR Shadowing is enabled, the TPR Threshold
* must be updated right before entering the guest.
*/
if (tpr_shadowing && !virtual_interrupt_delivery) {
if ((vmx->cap[vcpu].proc_ctls & PROCBASED_USE_TPR_SHADOW) != 0) {
vmcs_write(VMCS_TPR_THRESHOLD, vlapic_get_cr8(vlapic));
}
}

/*
* VM exits restore the base address but not the
* limits of GDTR and IDTR. The VMCS only stores the
Expand Down Expand Up @@ -3634,7 +3664,30 @@ vmx_set_tmr(struct vlapic *vlapic, int vector, bool level)
}

static void
vmx_enable_x2apic_mode(struct vlapic *vlapic)
vmx_enable_x2apic_mode_ts(struct vlapic *vlapic)
{
struct vmx *vmx;
struct vmcs *vmcs;
uint32_t proc_ctls;
int vcpuid;

vcpuid = vlapic->vcpuid;
vmx = ((struct vlapic_vtx *)vlapic)->vmx;
vmcs = &vmx->vmcs[vcpuid];

proc_ctls = vmx->cap[vcpuid].proc_ctls;
proc_ctls &= ~PROCBASED_USE_TPR_SHADOW;
proc_ctls |= PROCBASED_CR8_LOAD_EXITING;
proc_ctls |= PROCBASED_CR8_STORE_EXITING;
vmx->cap[vcpuid].proc_ctls = proc_ctls;

VMPTRLD(vmcs);
vmcs_write(VMCS_PRI_PROC_BASED_CTLS, proc_ctls);
VMCLEAR(vmcs);
}

static void
vmx_enable_x2apic_mode_vid(struct vlapic *vlapic)
{
struct vmx *vmx;
struct vmcs *vmcs;
Expand Down Expand Up @@ -3795,12 +3848,16 @@ vmx_vlapic_init(void *arg, int vcpuid)
vlapic_vtx->pir_desc = &vmx->pir_desc[vcpuid];
vlapic_vtx->vmx = vmx;

if (tpr_shadowing) {
vlapic->ops.enable_x2apic_mode = vmx_enable_x2apic_mode_ts;
}

if (virtual_interrupt_delivery) {
vlapic->ops.set_intr_ready = vmx_set_intr_ready;
vlapic->ops.pending_intr = vmx_pending_intr;
vlapic->ops.intr_accepted = vmx_intr_accepted;
vlapic->ops.set_tmr = vmx_set_tmr;
vlapic->ops.enable_x2apic_mode = vmx_enable_x2apic_mode;
vlapic->ops.enable_x2apic_mode = vmx_enable_x2apic_mode_vid;
}

if (posted_interrupts)
Expand Down
9 changes: 8 additions & 1 deletion sys/amd64/vmm/io/vlapic.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,12 @@ vlapic_update_ppr(struct vlapic *vlapic)
VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr);
}

void
vlapic_sync_tpr(struct vlapic *vlapic)
{
vlapic_update_ppr(vlapic);
}

static VMM_STAT(VLAPIC_GRATUITOUS_EOI, "EOI without any in-service interrupt");

static void
Expand Down Expand Up @@ -1079,6 +1085,8 @@ vlapic_pending_intr(struct vlapic *vlapic, int *vecptr)
int idx, i, bitpos, vector;
uint32_t *irrptr, val;

vlapic_update_ppr(vlapic);

if (vlapic->ops.pending_intr)
return ((*vlapic->ops.pending_intr)(vlapic, vecptr));

Expand Down Expand Up @@ -1136,7 +1144,6 @@ vlapic_intr_accepted(struct vlapic *vlapic, int vector)
panic("isrvec_stk_top overflow %d", stk_top);

vlapic->isrvec_stk[stk_top] = vector;
vlapic_update_ppr(vlapic);
}

void
Expand Down
2 changes: 2 additions & 0 deletions sys/amd64/vmm/io/vlapic.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ void vlapic_post_intr(struct vlapic *vlapic, int hostcpu, int ipinum);
void vlapic_fire_cmci(struct vlapic *vlapic);
int vlapic_trigger_lvt(struct vlapic *vlapic, int vector);

void vlapic_sync_tpr(struct vlapic *vlapic);

uint64_t vlapic_get_apicbase(struct vlapic *vlapic);
int vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val);
void vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state s);
Expand Down

0 comments on commit b7a1d0c

Please sign in to comment.