Skip to content

Commit 844d69c

Browse files
sean-jcbonzini
authored andcommitted
KVM: SVM: Delay restoration of host MSR_TSC_AUX until return to userspace
Use KVM's "user return MSRs" framework to defer restoring the host's MSR_TSC_AUX until the CPU returns to userspace. Add/improve comments to clarify why MSR_TSC_AUX is intercepted on both RDMSR and WRMSR, and why it's safe for KVM to keep the guest's value loaded even if KVM is scheduled out. Cc: Reiji Watanabe <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent dbd6127 commit 844d69c

File tree

2 files changed

+24
-36
lines changed

2 files changed

+24
-36
lines changed

arch/x86/kvm/svm/svm.c

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,15 @@ struct kvm_ldttss_desc {
213213

214214
DEFINE_PER_CPU(struct svm_cpu_data *, svm_data);
215215

216+
/*
217+
* Only MSR_TSC_AUX is switched via the user return hook. EFER is switched via
218+
* the VMCB, and the SYSCALL/SYSENTER MSRs are handled by VMLOAD/VMSAVE.
219+
*
220+
* RDTSCP and RDPID are not used in the kernel, specifically to allow KVM to
221+
* defer the restoration of TSC_AUX until the CPU returns to userspace.
222+
*/
223+
#define TSC_AUX_URET_SLOT 0
224+
216225
static const u32 msrpm_ranges[] = {0, 0xc0000000, 0xc0010000};
217226

218227
#define NUM_MSR_MAPS ARRAY_SIZE(msrpm_ranges)
@@ -958,6 +967,9 @@ static __init int svm_hardware_setup(void)
958967
kvm_tsc_scaling_ratio_frac_bits = 32;
959968
}
960969

970+
if (boot_cpu_has(X86_FEATURE_RDTSCP))
971+
kvm_define_user_return_msr(TSC_AUX_URET_SLOT, MSR_TSC_AUX);
972+
961973
/* Check for pause filtering support */
962974
if (!boot_cpu_has(X86_FEATURE_PAUSEFILTER)) {
963975
pause_filter_count = 0;
@@ -1423,19 +1435,10 @@ static void svm_prepare_guest_switch(struct kvm_vcpu *vcpu)
14231435
{
14241436
struct vcpu_svm *svm = to_svm(vcpu);
14251437
struct svm_cpu_data *sd = per_cpu(svm_data, vcpu->cpu);
1426-
unsigned int i;
14271438

14281439
if (svm->guest_state_loaded)
14291440
return;
14301441

1431-
/*
1432-
* Certain MSRs are restored on VMEXIT (sev-es), or vmload of host save
1433-
* area (non-sev-es). Save ones that aren't so we can restore them
1434-
* individually later.
1435-
*/
1436-
for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
1437-
rdmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
1438-
14391442
/*
14401443
* Save additional host state that will be restored on VMEXIT (sev-es)
14411444
* or subsequent vmload of host save area.
@@ -1454,29 +1457,15 @@ static void svm_prepare_guest_switch(struct kvm_vcpu *vcpu)
14541457
}
14551458
}
14561459

1457-
/* This assumes that the kernel never uses MSR_TSC_AUX */
14581460
if (static_cpu_has(X86_FEATURE_RDTSCP))
1459-
wrmsrl(MSR_TSC_AUX, svm->tsc_aux);
1461+
kvm_set_user_return_msr(TSC_AUX_URET_SLOT, svm->tsc_aux, -1ull);
14601462

14611463
svm->guest_state_loaded = true;
14621464
}
14631465

14641466
static void svm_prepare_host_switch(struct kvm_vcpu *vcpu)
14651467
{
1466-
struct vcpu_svm *svm = to_svm(vcpu);
1467-
unsigned int i;
1468-
1469-
if (!svm->guest_state_loaded)
1470-
return;
1471-
1472-
/*
1473-
* Certain MSRs are restored on VMEXIT (sev-es), or vmload of host save
1474-
* area (non-sev-es). Restore the ones that weren't.
1475-
*/
1476-
for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
1477-
wrmsrl(host_save_user_msrs[i], svm->host_user_msrs[i]);
1478-
1479-
svm->guest_state_loaded = false;
1468+
to_svm(vcpu)->guest_state_loaded = false;
14801469
}
14811470

14821471
static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
@@ -2790,6 +2779,7 @@ static int svm_set_vm_cr(struct kvm_vcpu *vcpu, u64 data)
27902779
static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
27912780
{
27922781
struct vcpu_svm *svm = to_svm(vcpu);
2782+
int r;
27932783

27942784
u32 ecx = msr->index;
27952785
u64 data = msr->data;
@@ -2910,11 +2900,16 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
29102900
data = (u32)data;
29112901

29122902
/*
2913-
* This is rare, so we update the MSR here instead of using
2914-
* direct_access_msrs. Doing that would require a rdmsr in
2915-
* svm_vcpu_put.
2903+
* TSC_AUX is usually changed only during boot and never read
2904+
* directly. Intercept TSC_AUX instead of exposing it to the
2905+
* guest via direct_access_msrs, and switch it via user return.
29162906
*/
2917-
wrmsrl(MSR_TSC_AUX, data);
2907+
preempt_disable();
2908+
r = kvm_set_user_return_msr(TSC_AUX_URET_SLOT, data, -1ull);
2909+
preempt_enable();
2910+
if (r)
2911+
return 1;
2912+
29182913
svm->tsc_aux = data;
29192914
break;
29202915
case MSR_IA32_DEBUGCTLMSR:

arch/x86/kvm/svm/svm.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323

2424
#define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
2525

26-
static const u32 host_save_user_msrs[] = {
27-
MSR_TSC_AUX,
28-
};
29-
#define NR_HOST_SAVE_USER_MSRS ARRAY_SIZE(host_save_user_msrs)
30-
3126
#define IOPM_SIZE PAGE_SIZE * 3
3227
#define MSRPM_SIZE PAGE_SIZE * 2
3328

@@ -129,8 +124,6 @@ struct vcpu_svm {
129124

130125
u64 next_rip;
131126

132-
u64 host_user_msrs[NR_HOST_SAVE_USER_MSRS];
133-
134127
u64 spec_ctrl;
135128
/*
136129
* Contains guest-controlled bits of VIRT_SPEC_CTRL, which will be

0 commit comments

Comments
 (0)