2024-02-23 20:22:12

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH v2 0/3] KVM: VMX: MSR intercept/passthrough cleanup and simplification

Clean up VMX's MSR passthrough code, in particular the portion that deals with
userspace MSR filters (KVM needs to force interception if userspace wants to
intercept even if KVM wants to passthrough). As pointed out by Dongli, KVM
does a linear walk twice in quick succession, which is wasteful, confuing, and
unnecessarily brittle.

Same exact idea as Dongli's v1[*], just a different approach to cleaning up the
API for dealing with MSR filters.

v2: Combine "check and get" into a single API instead of adding an out param.

Dongli Zhang (2):
KVM: VMX: fix comment to add LBR to passthrough MSRs
KVM: VMX: return early if msr_bitmap is not supported

Sean Christopherson (1):
KVM: VMX: Combine "check" and "get" APIs for passthrough MSR lookups

arch/x86/kvm/vmx/vmx.c | 68 ++++++++++++++++++------------------------
1 file changed, 29 insertions(+), 39 deletions(-)


base-commit: ec1e3d33557babed2c2c2c7da6e84293c2f56f58
--
2.44.0.rc0.258.g7320e95886-goog



2024-02-23 20:22:25

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH v2 1/3] KVM: VMX: fix comment to add LBR to passthrough MSRs

From: Dongli Zhang <[email protected]>

According to the is_valid_passthrough_msr(), the LBR MSRs are also
passthrough MSRs, since the commit 1b5ac3226a1a ("KVM: vmx/pmu:
Pass-through LBR msrs when the guest LBR event is ACTIVE").

Signed-off-by: Dongli Zhang <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/vmx/vmx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 9239a89dea22..7470a7fb1014 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -159,7 +159,7 @@ module_param(allow_smaller_maxphyaddr, bool, S_IRUGO);

/*
* List of MSRs that can be directly passed to the guest.
- * In addition to these x2apic and PT MSRs are handled specially.
+ * In addition to these x2apic, PT and LBR MSRs are handled specially.
*/
static u32 vmx_possible_passthrough_msrs[MAX_POSSIBLE_PASSTHROUGH_MSRS] = {
MSR_IA32_SPEC_CTRL,
--
2.44.0.rc0.258.g7320e95886-goog


2024-02-23 20:22:42

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH v2 2/3] KVM: VMX: return early if msr_bitmap is not supported

From: Dongli Zhang <[email protected]>

The vmx_msr_filter_changed() may directly/indirectly calls only
vmx_enable_intercept_for_msr() or vmx_disable_intercept_for_msr(). Those
two functions may exit immediately if !cpu_has_vmx_msr_bitmap().

vmx_msr_filter_changed()
-> vmx_disable_intercept_for_msr()
-> pt_update_intercept_for_msr()
-> vmx_set_intercept_for_msr()
-> vmx_enable_intercept_for_msr()
-> vmx_disable_intercept_for_msr()

Therefore, we exit early if !cpu_has_vmx_msr_bitmap().

Signed-off-by: Dongli Zhang <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/vmx/vmx.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 7470a7fb1014..014cf47dc66b 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4127,6 +4127,9 @@ static void vmx_msr_filter_changed(struct kvm_vcpu *vcpu)
struct vcpu_vmx *vmx = to_vmx(vcpu);
u32 i;

+ if (!cpu_has_vmx_msr_bitmap())
+ return;
+
/*
* Redo intercept permissions for MSRs that KVM is passing through to
* the guest. Disabling interception will check the new MSR filter and
--
2.44.0.rc0.258.g7320e95886-goog


2024-02-23 20:23:07

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH v2 3/3] KVM: VMX: Combine "check" and "get" APIs for passthrough MSR lookups

Combine possible_passthrough_msr_slot() and is_valid_passthrough_msr()
into a single function, vmx_get_passthrough_msr_slot(), and have the
combined helper return the slot on success, using a negative value to
indicate "failure".

Combining the operations avoids iterating over the array of passthrough
MSRs twice for relevant MSRs.

Suggested-by: Dongli Zhang <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/vmx/vmx.c | 63 +++++++++++++++++-------------------------
1 file changed, 25 insertions(+), 38 deletions(-)

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 014cf47dc66b..969fd3aa0da3 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -658,25 +658,14 @@ static inline bool cpu_need_virtualize_apic_accesses(struct kvm_vcpu *vcpu)
return flexpriority_enabled && lapic_in_kernel(vcpu);
}

-static int possible_passthrough_msr_slot(u32 msr)
+static int vmx_get_passthrough_msr_slot(u32 msr)
{
- u32 i;
-
- for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++)
- if (vmx_possible_passthrough_msrs[i] == msr)
- return i;
-
- return -ENOENT;
-}
-
-static bool is_valid_passthrough_msr(u32 msr)
-{
- bool r;
+ int i;

switch (msr) {
case 0x800 ... 0x8ff:
/* x2APIC MSRs. These are handled in vmx_update_msr_bitmap_x2apic() */
- return true;
+ return -ENOENT;
case MSR_IA32_RTIT_STATUS:
case MSR_IA32_RTIT_OUTPUT_BASE:
case MSR_IA32_RTIT_OUTPUT_MASK:
@@ -691,14 +680,16 @@ static bool is_valid_passthrough_msr(u32 msr)
case MSR_LBR_CORE_FROM ... MSR_LBR_CORE_FROM + 8:
case MSR_LBR_CORE_TO ... MSR_LBR_CORE_TO + 8:
/* LBR MSRs. These are handled in vmx_update_intercept_for_lbr_msrs() */
- return true;
+ return -ENOENT;
}

- r = possible_passthrough_msr_slot(msr) != -ENOENT;
-
- WARN(!r, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);
+ for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) {
+ if (vmx_possible_passthrough_msrs[i] == msr)
+ return i;
+ }

- return r;
+ WARN(1, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);
+ return -ENOENT;
}

struct vmx_uret_msr *vmx_find_uret_msr(struct vcpu_vmx *vmx, u32 msr)
@@ -3954,6 +3945,7 @@ void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
+ int idx;

if (!cpu_has_vmx_msr_bitmap())
return;
@@ -3963,16 +3955,13 @@ void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
/*
* Mark the desired intercept state in shadow bitmap, this is needed
* for resync when the MSR filters change.
- */
- if (is_valid_passthrough_msr(msr)) {
- int idx = possible_passthrough_msr_slot(msr);
-
- if (idx != -ENOENT) {
- if (type & MSR_TYPE_R)
- clear_bit(idx, vmx->shadow_msr_intercept.read);
- if (type & MSR_TYPE_W)
- clear_bit(idx, vmx->shadow_msr_intercept.write);
- }
+ */
+ idx = vmx_get_passthrough_msr_slot(msr);
+ if (idx >= 0) {
+ if (type & MSR_TYPE_R)
+ clear_bit(idx, vmx->shadow_msr_intercept.read);
+ if (type & MSR_TYPE_W)
+ clear_bit(idx, vmx->shadow_msr_intercept.write);
}

if ((type & MSR_TYPE_R) &&
@@ -3998,6 +3987,7 @@ void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
+ int idx;

if (!cpu_has_vmx_msr_bitmap())
return;
@@ -4008,15 +3998,12 @@ void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
* Mark the desired intercept state in shadow bitmap, this is needed
* for resync when the MSR filter changes.
*/
- if (is_valid_passthrough_msr(msr)) {
- int idx = possible_passthrough_msr_slot(msr);
-
- if (idx != -ENOENT) {
- if (type & MSR_TYPE_R)
- set_bit(idx, vmx->shadow_msr_intercept.read);
- if (type & MSR_TYPE_W)
- set_bit(idx, vmx->shadow_msr_intercept.write);
- }
+ idx = vmx_get_passthrough_msr_slot(msr);
+ if (idx >= 0) {
+ if (type & MSR_TYPE_R)
+ set_bit(idx, vmx->shadow_msr_intercept.read);
+ if (type & MSR_TYPE_W)
+ set_bit(idx, vmx->shadow_msr_intercept.write);
}

if (type & MSR_TYPE_R)
--
2.44.0.rc0.258.g7320e95886-goog


2024-02-24 03:15:13

by Dongli Zhang

[permalink] [raw]
Subject: Re: [PATCH v2 3/3] KVM: VMX: Combine "check" and "get" APIs for passthrough MSR lookups



On 2/23/24 12:21, Sean Christopherson wrote:
> Combine possible_passthrough_msr_slot() and is_valid_passthrough_msr()
> into a single function, vmx_get_passthrough_msr_slot(), and have the
> combined helper return the slot on success, using a negative value to
> indicate "failure".
>
> Combining the operations avoids iterating over the array of passthrough
> MSRs twice for relevant MSRs.
>
> Suggested-by: Dongli Zhang <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
> arch/x86/kvm/vmx/vmx.c | 63 +++++++++++++++++-------------------------
> 1 file changed, 25 insertions(+), 38 deletions(-)
>
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index 014cf47dc66b..969fd3aa0da3 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -658,25 +658,14 @@ static inline bool cpu_need_virtualize_apic_accesses(struct kvm_vcpu *vcpu)
> return flexpriority_enabled && lapic_in_kernel(vcpu);
> }
>
> -static int possible_passthrough_msr_slot(u32 msr)
> +static int vmx_get_passthrough_msr_slot(u32 msr)
> {
> - u32 i;
> -
> - for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++)
> - if (vmx_possible_passthrough_msrs[i] == msr)
> - return i;
> -
> - return -ENOENT;
> -}
> -
> -static bool is_valid_passthrough_msr(u32 msr)
> -{
> - bool r;
> + int i;
>
> switch (msr) {
> case 0x800 ... 0x8ff:
> /* x2APIC MSRs. These are handled in vmx_update_msr_bitmap_x2apic() */
> - return true;
> + return -ENOENT;
> case MSR_IA32_RTIT_STATUS:
> case MSR_IA32_RTIT_OUTPUT_BASE:
> case MSR_IA32_RTIT_OUTPUT_MASK:
> @@ -691,14 +680,16 @@ static bool is_valid_passthrough_msr(u32 msr)
> case MSR_LBR_CORE_FROM ... MSR_LBR_CORE_FROM + 8:
> case MSR_LBR_CORE_TO ... MSR_LBR_CORE_TO + 8:
> /* LBR MSRs. These are handled in vmx_update_intercept_for_lbr_msrs() */
> - return true;
> + return -ENOENT;
> }
>
> - r = possible_passthrough_msr_slot(msr) != -ENOENT;
> -
> - WARN(!r, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);
> + for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) {
> + if (vmx_possible_passthrough_msrs[i] == msr)
> + return i;
> + }
>
> - return r;
> + WARN(1, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);

Reviewed-by: Dongli Zhang <[email protected]>

Not sure which is better:

WARN(1 ... , or WARN(true ...

Thank you very much!

Dongli Zhang

2024-02-24 03:19:07

by Dongli Zhang

[permalink] [raw]
Subject: Re: [PATCH v2 3/3] KVM: VMX: Combine "check" and "get" APIs for passthrough MSR lookups



On 2/23/24 12:21, Sean Christopherson wrote:
> Combine possible_passthrough_msr_slot() and is_valid_passthrough_msr()
> into a single function, vmx_get_passthrough_msr_slot(), and have the
> combined helper return the slot on success, using a negative value to
> indicate "failure".
>
> Combining the operations avoids iterating over the array of passthrough
> MSRs twice for relevant MSRs.
>
> Suggested-by: Dongli Zhang <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
> arch/x86/kvm/vmx/vmx.c | 63 +++++++++++++++++-------------------------
> 1 file changed, 25 insertions(+), 38 deletions(-)
>
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index 014cf47dc66b..969fd3aa0da3 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -658,25 +658,14 @@ static inline bool cpu_need_virtualize_apic_accesses(struct kvm_vcpu *vcpu)
> return flexpriority_enabled && lapic_in_kernel(vcpu);
> }
>
> -static int possible_passthrough_msr_slot(u32 msr)
> +static int vmx_get_passthrough_msr_slot(u32 msr)
> {
> - u32 i;
> -
> - for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++)
> - if (vmx_possible_passthrough_msrs[i] == msr)
> - return i;
> -
> - return -ENOENT;
> -}
> -
> -static bool is_valid_passthrough_msr(u32 msr)
> -{
> - bool r;
> + int i;
>
> switch (msr) {
> case 0x800 ... 0x8ff:
> /* x2APIC MSRs. These are handled in vmx_update_msr_bitmap_x2apic() */
> - return true;
> + return -ENOENT;
> case MSR_IA32_RTIT_STATUS:
> case MSR_IA32_RTIT_OUTPUT_BASE:
> case MSR_IA32_RTIT_OUTPUT_MASK:
> @@ -691,14 +680,16 @@ static bool is_valid_passthrough_msr(u32 msr)
> case MSR_LBR_CORE_FROM ... MSR_LBR_CORE_FROM + 8:
> case MSR_LBR_CORE_TO ... MSR_LBR_CORE_TO + 8:
> /* LBR MSRs. These are handled in vmx_update_intercept_for_lbr_msrs() */
> - return true;
> + return -ENOENT;
> }
>
> - r = possible_passthrough_msr_slot(msr) != -ENOENT;
> -
> - WARN(!r, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);
> + for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) {
> + if (vmx_possible_passthrough_msrs[i] == msr)
> + return i;
> + }
>
> - return r;
> + WARN(1, "Invalid MSR %x, please adapt vmx_possible_passthrough_msrs[]", msr);
> + return -ENOENT;
> }
>
> struct vmx_uret_msr *vmx_find_uret_msr(struct vcpu_vmx *vmx, u32 msr)
> @@ -3954,6 +3945,7 @@ void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
> {
> struct vcpu_vmx *vmx = to_vmx(vcpu);
> unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
> + int idx;
>
> if (!cpu_has_vmx_msr_bitmap())
> return;
> @@ -3963,16 +3955,13 @@ void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
> /*
> * Mark the desired intercept state in shadow bitmap, this is needed
> * for resync when the MSR filters change.
> - */
> - if (is_valid_passthrough_msr(msr)) {
> - int idx = possible_passthrough_msr_slot(msr);
> -
> - if (idx != -ENOENT) {
> - if (type & MSR_TYPE_R)
> - clear_bit(idx, vmx->shadow_msr_intercept.read);
> - if (type & MSR_TYPE_W)
> - clear_bit(idx, vmx->shadow_msr_intercept.write);
> - }
> + */
> + idx = vmx_get_passthrough_msr_slot(msr);
> + if (idx >= 0) {
> + if (type & MSR_TYPE_R)
> + clear_bit(idx, vmx->shadow_msr_intercept.read);
> + if (type & MSR_TYPE_W)
> + clear_bit(idx, vmx->shadow_msr_intercept.write);
> }
>
> if ((type & MSR_TYPE_R) &&
> @@ -3998,6 +3987,7 @@ void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
> {
> struct vcpu_vmx *vmx = to_vmx(vcpu);
> unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap;
> + int idx;
>
> if (!cpu_has_vmx_msr_bitmap())
> return;
> @@ -4008,15 +3998,12 @@ void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type)
> * Mark the desired intercept state in shadow bitmap, this is needed
> * for resync when the MSR filter changes.
> */

BTW, perhaps fix above the above indentation issue too? I did not notice that
when working on the initial patch.

Thank you very much!

Dongli Zhang

2024-02-27 22:07:50

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH v2 0/3] KVM: VMX: MSR intercept/passthrough cleanup and simplification

On Fri, 23 Feb 2024 12:21:01 -0800, Sean Christopherson wrote:
> Clean up VMX's MSR passthrough code, in particular the portion that deals with
> userspace MSR filters (KVM needs to force interception if userspace wants to
> intercept even if KVM wants to passthrough). As pointed out by Dongli, KVM
> does a linear walk twice in quick succession, which is wasteful, confuing, and
> unnecessarily brittle.
>
> Same exact idea as Dongli's v1[*], just a different approach to cleaning up the
> API for dealing with MSR filters.
>
> [...]

Applied to kvm-x86 vmx, with Dongli's suggested comment indentation fixup.

Thanks!

[1/3] KVM: VMX: fix comment to add LBR to passthrough MSRs
https://github.com/kvm-x86/linux/commit/8e24eeedfda3
[2/3] KVM: VMX: return early if msr_bitmap is not supported
https://github.com/kvm-x86/linux/commit/bab22040d7fd
[3/3] KVM: VMX: Combine "check" and "get" APIs for passthrough MSR lookups
https://github.com/kvm-x86/linux/commit/259720c37d51
--
https://github.com/kvm-x86/linux/tree/next