Skip to content

Commit

Permalink
KVM: VMX: Enable memory mapped TPR shadow (FlexPriority)
Browse files Browse the repository at this point in the history
This patch based on CR8/TPR patch, and enable the TPR shadow (FlexPriority)
for 32bit Windows.  Since TPR is accessed very frequently by 32bit
Windows, especially SMP guest, with FlexPriority enabled, we saw significant
performance gain.

Signed-off-by: Sheng Yang <[email protected]>
Signed-off-by: Avi Kivity <[email protected]>
  • Loading branch information
Sheng Yang authored and avikivity committed Jan 30, 2008
1 parent a03490e commit f78e0e2
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 30 deletions.
4 changes: 4 additions & 0 deletions drivers/kvm/kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ struct kvm {
struct kvm_ioapic *vioapic;
int round_robin_prev_vcpu;
unsigned int tss_addr;
struct page *apic_access_page;
};

static inline struct kvm_pic *pic_irqchip(struct kvm *kvm)
Expand Down Expand Up @@ -522,6 +523,9 @@ int is_error_page(struct page *page);
int kvm_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
int user_alloc);
int __kvm_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
int user_alloc);
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn);
struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn);
void kvm_release_page(struct page *page);
Expand Down
56 changes: 39 additions & 17 deletions drivers/kvm/kvm_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,10 +362,12 @@ EXPORT_SYMBOL_GPL(fx_init);
* space.
*
* Discontiguous memory is allowed, mostly for framebuffers.
*
* Must be called holding kvm->lock.
*/
int kvm_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
int user_alloc)
int __kvm_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
int user_alloc)
{
int r;
gfn_t base_gfn;
Expand All @@ -392,8 +394,6 @@ int kvm_set_memory_region(struct kvm *kvm,
if (!npages)
mem->flags &= ~KVM_MEM_LOG_DIRTY_PAGES;

mutex_lock(&kvm->lock);

new = old = *memslot;

new.base_gfn = base_gfn;
Expand All @@ -403,7 +403,7 @@ int kvm_set_memory_region(struct kvm *kvm,
/* Disallow changing a memory slot's size. */
r = -EINVAL;
if (npages && old.npages && npages != old.npages)
goto out_unlock;
goto out_free;

/* Check for overlaps */
r = -EEXIST;
Expand All @@ -414,7 +414,7 @@ int kvm_set_memory_region(struct kvm *kvm,
continue;
if (!((base_gfn + npages <= s->base_gfn) ||
(base_gfn >= s->base_gfn + s->npages)))
goto out_unlock;
goto out_free;
}

/* Free page dirty bitmap if unneeded */
Expand All @@ -428,7 +428,7 @@ int kvm_set_memory_region(struct kvm *kvm,
new.rmap = vmalloc(npages * sizeof(struct page *));

if (!new.rmap)
goto out_unlock;
goto out_free;

memset(new.rmap, 0, npages * sizeof(*new.rmap));

Expand All @@ -445,7 +445,7 @@ int kvm_set_memory_region(struct kvm *kvm,
up_write(&current->mm->mmap_sem);

if (IS_ERR((void *)new.userspace_addr))
goto out_unlock;
goto out_free;
}
} else {
if (!old.user_alloc && old.rmap) {
Expand All @@ -468,7 +468,7 @@ int kvm_set_memory_region(struct kvm *kvm,

new.dirty_bitmap = vmalloc(dirty_bytes);
if (!new.dirty_bitmap)
goto out_unlock;
goto out_free;
memset(new.dirty_bitmap, 0, dirty_bytes);
}

Expand Down Expand Up @@ -498,18 +498,28 @@ int kvm_set_memory_region(struct kvm *kvm,
kvm_mmu_slot_remove_write_access(kvm, mem->slot);
kvm_flush_remote_tlbs(kvm);

mutex_unlock(&kvm->lock);

kvm_free_physmem_slot(&old, &new);
return 0;

out_unlock:
mutex_unlock(&kvm->lock);
out_free:
kvm_free_physmem_slot(&new, &old);
out:
return r;

}
EXPORT_SYMBOL_GPL(__kvm_set_memory_region);

int kvm_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
int user_alloc)
{
int r;

mutex_lock(&kvm->lock);
r = __kvm_set_memory_region(kvm, mem, user_alloc);
mutex_unlock(&kvm->lock);
return r;
}
EXPORT_SYMBOL_GPL(kvm_set_memory_region);

int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
Expand Down Expand Up @@ -888,14 +898,21 @@ static int emulator_read_emulated(unsigned long addr,
memcpy(val, vcpu->mmio_data, bytes);
vcpu->mmio_read_completed = 0;
return X86EMUL_CONTINUE;
} else if (emulator_read_std(addr, val, bytes, vcpu)
== X86EMUL_CONTINUE)
return X86EMUL_CONTINUE;
}

gpa = vcpu->mmu.gva_to_gpa(vcpu, addr);

/* For APIC access vmexit */
if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
goto mmio;

if (emulator_read_std(addr, val, bytes, vcpu)
== X86EMUL_CONTINUE)
return X86EMUL_CONTINUE;
if (gpa == UNMAPPED_GVA)
return X86EMUL_PROPAGATE_FAULT;

mmio:
/*
* Is this MMIO handled locally?
*/
Expand Down Expand Up @@ -938,9 +955,14 @@ static int emulator_write_emulated_onepage(unsigned long addr,
return X86EMUL_PROPAGATE_FAULT;
}

/* For APIC access vmexit */
if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
goto mmio;

if (emulator_write_phys(vcpu, gpa, val, bytes))
return X86EMUL_CONTINUE;

mmio:
/*
* Is this MMIO handled locally?
*/
Expand Down
117 changes: 104 additions & 13 deletions drivers/kvm/vmx.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ static struct vmcs_config {
u32 revision_id;
u32 pin_based_exec_ctrl;
u32 cpu_based_exec_ctrl;
u32 cpu_based_2nd_exec_ctrl;
u32 vmexit_ctrl;
u32 vmentry_ctrl;
} vmcs_config;
Expand Down Expand Up @@ -179,6 +180,29 @@ static inline int vm_need_tpr_shadow(struct kvm *kvm)
return ((cpu_has_vmx_tpr_shadow()) && (irqchip_in_kernel(kvm)));
}

static inline int cpu_has_secondary_exec_ctrls(void)
{
return (vmcs_config.cpu_based_exec_ctrl &
CPU_BASED_ACTIVATE_SECONDARY_CONTROLS);
}

static inline int vm_need_secondary_exec_ctrls(struct kvm *kvm)
{
return ((cpu_has_secondary_exec_ctrls()) && (irqchip_in_kernel(kvm)));
}

static inline int cpu_has_vmx_virtualize_apic_accesses(void)
{
return (vmcs_config.cpu_based_2nd_exec_ctrl &
SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES);
}

static inline int vm_need_virtualize_apic_accesses(struct kvm *kvm)
{
return ((cpu_has_vmx_virtualize_apic_accesses()) &&
(irqchip_in_kernel(kvm)));
}

static int __find_msr_index(struct vcpu_vmx *vmx, u32 msr)
{
int i;
Expand Down Expand Up @@ -918,6 +942,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
u32 min, opt;
u32 _pin_based_exec_control = 0;
u32 _cpu_based_exec_control = 0;
u32 _cpu_based_2nd_exec_control = 0;
u32 _vmexit_control = 0;
u32 _vmentry_control = 0;

Expand All @@ -935,11 +960,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
CPU_BASED_USE_IO_BITMAPS |
CPU_BASED_MOV_DR_EXITING |
CPU_BASED_USE_TSC_OFFSETING;
#ifdef CONFIG_X86_64
opt = CPU_BASED_TPR_SHADOW;
#else
opt = 0;
#endif
opt = CPU_BASED_TPR_SHADOW |
CPU_BASED_ACTIVATE_SECONDARY_CONTROLS;
if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PROCBASED_CTLS,
&_cpu_based_exec_control) < 0)
return -EIO;
Expand All @@ -948,6 +970,18 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
_cpu_based_exec_control &= ~CPU_BASED_CR8_LOAD_EXITING &
~CPU_BASED_CR8_STORE_EXITING;
#endif
if (_cpu_based_exec_control & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS) {
min = 0;
opt = SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES;
if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PROCBASED_CTLS2,
&_cpu_based_2nd_exec_control) < 0)
return -EIO;
}
#ifndef CONFIG_X86_64
if (!(_cpu_based_2nd_exec_control &
SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES))
_cpu_based_exec_control &= ~CPU_BASED_TPR_SHADOW;
#endif

min = 0;
#ifdef CONFIG_X86_64
Expand Down Expand Up @@ -985,6 +1019,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)

vmcs_conf->pin_based_exec_ctrl = _pin_based_exec_control;
vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control;
vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control;
vmcs_conf->vmexit_ctrl = _vmexit_control;
vmcs_conf->vmentry_ctrl = _vmentry_control;

Expand Down Expand Up @@ -1427,6 +1462,27 @@ static void seg_setup(int seg)
vmcs_write32(sf->ar_bytes, 0x93);
}

static int alloc_apic_access_page(struct kvm *kvm)
{
struct kvm_userspace_memory_region kvm_userspace_mem;
int r = 0;

mutex_lock(&kvm->lock);
if (kvm->apic_access_page)
goto out;
kvm_userspace_mem.slot = APIC_ACCESS_PAGE_PRIVATE_MEMSLOT;
kvm_userspace_mem.flags = 0;
kvm_userspace_mem.guest_phys_addr = 0xfee00000ULL;
kvm_userspace_mem.memory_size = PAGE_SIZE;
r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, 0);
if (r)
goto out;
kvm->apic_access_page = gfn_to_page(kvm, 0xfee00);
out:
mutex_unlock(&kvm->lock);
return r;
}

/*
* Sets up the vmcs for emulated real mode.
*/
Expand Down Expand Up @@ -1458,8 +1514,14 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
CPU_BASED_CR8_LOAD_EXITING;
#endif
}
if (!vm_need_secondary_exec_ctrls(vmx->vcpu.kvm))
exec_control &= ~CPU_BASED_ACTIVATE_SECONDARY_CONTROLS;
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, exec_control);

if (vm_need_secondary_exec_ctrls(vmx->vcpu.kvm))
vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
vmcs_config.cpu_based_2nd_exec_ctrl);

vmcs_write32(PAGE_FAULT_ERROR_CODE_MASK, !!bypass_guest_pf);
vmcs_write32(PAGE_FAULT_ERROR_CODE_MATCH, !!bypass_guest_pf);
vmcs_write32(CR3_TARGET_COUNT, 0); /* 22.2.1 */
Expand Down Expand Up @@ -1528,6 +1590,10 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
vmcs_writel(CR0_GUEST_HOST_MASK, ~0UL);
vmcs_writel(CR4_GUEST_HOST_MASK, KVM_GUEST_CR4_MASK);

if (vm_need_virtualize_apic_accesses(vmx->vcpu.kvm))
if (alloc_apic_access_page(vmx->vcpu.kvm) != 0)
return -ENOMEM;

return 0;
}

Expand Down Expand Up @@ -1616,13 +1682,17 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)

vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); /* 22.2.1 */

#ifdef CONFIG_X86_64
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);
if (vm_need_tpr_shadow(vmx->vcpu.kvm))
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
page_to_phys(vmx->vcpu.apic->regs_page));
vmcs_write32(TPR_THRESHOLD, 0);
#endif
if (cpu_has_vmx_tpr_shadow()) {
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);
if (vm_need_tpr_shadow(vmx->vcpu.kvm))
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
page_to_phys(vmx->vcpu.apic->regs_page));
vmcs_write32(TPR_THRESHOLD, 0);
}

if (vm_need_virtualize_apic_accesses(vmx->vcpu.kvm))
vmcs_write64(APIC_ACCESS_ADDR,
page_to_phys(vmx->vcpu.kvm->apic_access_page));

vmx->vcpu.cr0 = 0x60000010;
vmx_set_cr0(&vmx->vcpu, vmx->vcpu.cr0); /* enter rmode */
Expand Down Expand Up @@ -2094,6 +2164,26 @@ static int handle_vmcall(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
return 1;
}

static int handle_apic_access(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
u64 exit_qualification;
enum emulation_result er;
unsigned long offset;

exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
offset = exit_qualification & 0xffful;

er = emulate_instruction(vcpu, kvm_run, 0, 0, 0);

if (er != EMULATE_DONE) {
printk(KERN_ERR
"Fail to handle apic access vmexit! Offset is 0x%lx\n",
offset);
return -ENOTSUPP;
}
return 1;
}

/*
* The exit handlers return 1 if the exit was handled fully and guest execution
* may resume. Otherwise they set the kvm_run parameter to indicate what needs
Expand All @@ -2113,7 +2203,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu,
[EXIT_REASON_PENDING_INTERRUPT] = handle_interrupt_window,
[EXIT_REASON_HLT] = handle_halt,
[EXIT_REASON_VMCALL] = handle_vmcall,
[EXIT_REASON_TPR_BELOW_THRESHOLD] = handle_tpr_below_threshold
[EXIT_REASON_TPR_BELOW_THRESHOLD] = handle_tpr_below_threshold,
[EXIT_REASON_APIC_ACCESS] = handle_apic_access,
};

static const int kvm_vmx_max_exit_handlers =
Expand Down
5 changes: 5 additions & 0 deletions drivers/kvm/vmx.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ enum vmcs_field {
TSC_OFFSET_HIGH = 0x00002011,
VIRTUAL_APIC_PAGE_ADDR = 0x00002012,
VIRTUAL_APIC_PAGE_ADDR_HIGH = 0x00002013,
APIC_ACCESS_ADDR = 0x00002014,
APIC_ACCESS_ADDR_HIGH = 0x00002015,
VMCS_LINK_POINTER = 0x00002800,
VMCS_LINK_POINTER_HIGH = 0x00002801,
GUEST_IA32_DEBUGCTL = 0x00002802,
Expand Down Expand Up @@ -214,6 +216,7 @@ enum vmcs_field {
#define EXIT_REASON_MSR_WRITE 32
#define EXIT_REASON_MWAIT_INSTRUCTION 36
#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
#define EXIT_REASON_APIC_ACCESS 44

/*
* Interruption-information format
Expand Down Expand Up @@ -307,4 +310,6 @@ enum vmcs_field {
#define MSR_IA32_FEATURE_CONTROL_LOCKED 0x1
#define MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED 0x4

#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT 9

#endif

0 comments on commit f78e0e2

Please sign in to comment.