[PATCH] KVM: x86: Let userspace re-enable previously disabled exits

From: Sean Christopherson
Date: Mon Jan 10 2022 - 15:40:31 EST


Add an OVERRIDE flag to KVM_CAP_X86_DISABLE_EXITS allow userspace to
re-enable exits and/or override previous settings. There's no real use
case for the the per-VM ioctl, but a future per-vCPU variant wants to let
userspace toggle interception while the vCPU is running; add the OVERRIDE
functionality now to provide consistent between between the per-VM and
per-vCPU variants.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
Documentation/virt/kvm/api.rst | 5 +++++
arch/x86/kvm/x86.c | 37 +++++++++++++++++++++++-----------
include/uapi/linux/kvm.h | 4 +++-
3 files changed, 33 insertions(+), 13 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 9460941d38d7..c42e653891a2 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6617,6 +6617,7 @@ Valid bits in args[0] are::
#define KVM_X86_DISABLE_EXITS_HLT (1 << 1)
#define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2)
#define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3)
+ #define KVM_X86_DISABLE_EXITS_OVERRIDE (1ull << 63)

Enabling this capability on a VM provides userspace with a way to no
longer intercept some instructions for improved latency in some
@@ -6625,6 +6626,10 @@ physical CPUs. More bits can be added in the future; userspace can
just pass the KVM_CHECK_EXTENSION result to KVM_ENABLE_CAP to disable
all such vmexits.

+By default, this capability only disables exits. To re-enable an exit, or to
+override previous settings, userspace can set KVM_X86_DISABLE_EXITS_OVERRIDE,
+in which case KVM will enable/disable according to the mask (a '1' == disable).
+
Do not enable KVM_FEATURE_PV_UNHALT if you disable HLT exits.

7.14 KVM_CAP_S390_HPAGE_1M
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 7741a5980334..f611a49ceba4 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4089,11 +4089,10 @@ static inline bool kvm_can_mwait_in_guest(void)

static u64 kvm_get_allowed_disable_exits(void)
{
- u64 r = KVM_X86_DISABLE_EXITS_HLT | KVM_X86_DISABLE_EXITS_PAUSE |
- KVM_X86_DISABLE_EXITS_CSTATE;
+ u64 r = KVM_X86_DISABLE_VALID_EXITS;

- if(kvm_can_mwait_in_guest())
- r |= KVM_X86_DISABLE_EXITS_MWAIT;
+ if (!kvm_can_mwait_in_guest())
+ r &= ~KVM_X86_DISABLE_EXITS_MWAIT;

return r;
}
@@ -5051,6 +5050,26 @@ static int kvm_vcpu_ioctl_device_attr(struct kvm_vcpu *vcpu,
return r;
}

+
+#define kvm_ioctl_disable_exits(a, mask) \
+({ \
+ if ((mask) & KVM_X86_DISABLE_EXITS_OVERRIDE) { \
+ (a).mwait_in_guest = (mask) & KVM_X86_DISABLE_EXITS_MWAIT; \
+ (a).hlt_in_guest = (mask) & KVM_X86_DISABLE_EXITS_HLT; \
+ (a).pause_in_guest = (mask) & KVM_X86_DISABLE_EXITS_PAUSE; \
+ (a).cstate_in_guest = (mask) & KVM_X86_DISABLE_EXITS_CSTATE; \
+ } else { \
+ if ((mask) & KVM_X86_DISABLE_EXITS_MWAIT) \
+ (a).mwait_in_guest = true; \
+ if ((mask) & KVM_X86_DISABLE_EXITS_HLT) \
+ (a).hlt_in_guest = true; \
+ if ((mask) & KVM_X86_DISABLE_EXITS_PAUSE) \
+ (a).pause_in_guest = true; \
+ if ((mask) & KVM_X86_DISABLE_EXITS_CSTATE) \
+ (a).cstate_in_guest = true; \
+ } \
+})
+
static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
struct kvm_enable_cap *cap)
{
@@ -5794,14 +5813,8 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
if (kvm->created_vcpus)
goto disable_exits_unlock;

- if (cap->args[0] & KVM_X86_DISABLE_EXITS_MWAIT)
- kvm->arch.mwait_in_guest = true;
- if (cap->args[0] & KVM_X86_DISABLE_EXITS_HLT)
- kvm->arch.hlt_in_guest = true;
- if (cap->args[0] & KVM_X86_DISABLE_EXITS_PAUSE)
- kvm->arch.pause_in_guest = true;
- if (cap->args[0] & KVM_X86_DISABLE_EXITS_CSTATE)
- kvm->arch.cstate_in_guest = true;
+ kvm_ioctl_disable_exits(kvm->arch, cap->args[0]);
+
r = 0;
disable_exits_unlock:
mutex_unlock(&kvm->lock);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index fbfd70d965c6..5e0ffc87a578 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -798,10 +798,12 @@ struct kvm_ioeventfd {
#define KVM_X86_DISABLE_EXITS_HLT (1 << 1)
#define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2)
#define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3)
+#define KVM_X86_DISABLE_EXITS_OVERRIDE (1ull << 63)
#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \
KVM_X86_DISABLE_EXITS_HLT | \
KVM_X86_DISABLE_EXITS_PAUSE | \
- KVM_X86_DISABLE_EXITS_CSTATE)
+ KVM_X86_DISABLE_EXITS_CSTATE | \
+ KVM_X86_DISABLE_EXITS_OVERRIDE)

/* for KVM_ENABLE_CAP */
struct kvm_enable_cap {
--
2.34.1.575.g55b058a8bb-goog


--tQ3jC/mhVwyhSXsr--