[RFC PATCH 4/8] kvm: x86: boost vcpu threads on latency sensitive paths

Vineeth Pillai (Google)
2023-12-14T02:47:39Z

Proactively boost the vcpu thread when delivering an interrupt so
that the guest vcpu gets to run with minimum latency and service the
interrupt. The host knows that guest vcpu is going to service an irq/nmi
as soon as its delivered and boosting the priority will help the guest
to avoid latencies. Timer interrupt is one common scenario which
benefits from this.

When a vcpu resumes from halt, it would be because of an event like
timer or irq/nmi and is latency sensitive. So, boosting the priority
of vcpu thread when it goes idle makes sense as the wakeup would be
because of a latency sensitive event and this boosting will not hurt
the host as the thread is scheduled out.

Co-developed-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: Vineeth Pillai (Google) <vineeth@bitbyteword.org>
---
arch/x86/kvm/i8259.c | 2 +-
arch/x86/kvm/lapic.c | 8 ++++----
arch/x86/kvm/svm/svm.c | 2 +-
arch/x86/kvm/vmx/vmx.c | 2 +-
include/linux/kvm_host.h | 8 ++++++++
virt/kvm/kvm_main.c | 8 ++++++++
6 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c
index 8dec646e764b..6841ed802f00 100644
--- a/arch/x86/kvm/i8259.c
+++ b/arch/x86/kvm/i8259.c
@@ -62,7 +62,7 @@ static void pic_unlock(struct kvm_pic *s)
kvm_for_each_vcpu(i, vcpu, s->kvm) {
if (kvm_apic_accept_pic_intr(vcpu)) {
kvm_make_request(KVM_REQ_EVENT, vcpu);
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
return;
}
}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index e74e223f46aa..ae25176fddc8 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1309,12 +1309,12 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
result = 1;
vcpu->arch.pv.pv_unhalted = 1;
kvm_make_request(KVM_REQ_EVENT, vcpu);
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
break;

case APIC_DM_SMI:
if (!kvm_inject_smi(vcpu)) {
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
result = 1;
}
break;
@@ -1322,7 +1322,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
case APIC_DM_NMI:
result = 1;
kvm_inject_nmi(vcpu);
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
break;

case APIC_DM_INIT:
@@ -1901,7 +1901,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)
atomic_inc(&apic->lapic_timer.pending);
kvm_make_request(KVM_REQ_UNBLOCK, vcpu);
if (from_timer_fn)
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
}

static void start_sw_tscdeadline(struct kvm_lapic *apic)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index c8466bc64b87..578c19aeef73 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3566,7 +3566,7 @@ void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode,
if (!READ_ONCE(vcpu->arch.apic->apicv_active)) {
/* Process the interrupt via kvm_check_and_inject_events(). */
kvm_make_request(KVM_REQ_EVENT, vcpu);
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
return;
}

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index bc6f0fea48b4..b786cb2eb185 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4266,7 +4266,7 @@ static void vmx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
if (vmx_deliver_posted_interrupt(vcpu, vector)) {
kvm_lapic_set_irr(vector, apic);
kvm_make_request(KVM_REQ_EVENT, vcpu);
- kvm_vcpu_kick(vcpu);
+ kvm_vcpu_kick_boost(vcpu);
} else {
trace_kvm_apicv_accept_irq(vcpu->vcpu_id, delivery_mode,
trig_mode, vector);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index c6647f6312c9..f76680fbc60d 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2296,11 +2296,19 @@ static inline bool kvm_vcpu_sched_enabled(struct kvm_vcpu *vcpu)
{
return kvm_arch_vcpu_pv_sched_enabled(&vcpu->arch);
}
+
+static inline void kvm_vcpu_kick_boost(struct kvm_vcpu *vcpu)
+{
+ kvm_vcpu_set_sched(vcpu, true);
+ kvm_vcpu_kick(vcpu);
+}
#else
static inline int kvm_vcpu_set_sched(struct kvm_vcpu *vcpu, bool boost)
{
return 0;
}
+
+#define kvm_vcpu_kick_boost kvm_vcpu_kick
#endif

#endif
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 37748e2512e1..0dd8b84ed073 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3460,6 +3460,14 @@ bool kvm_vcpu_block(struct kvm_vcpu *vcpu)
if (kvm_vcpu_check_block(vcpu) < 0)
break;

+ /*
+ * Boost before scheduling out. Wakeup happens only on
+ * an event or a signal and hence it is beneficial to
+ * be scheduled ASAP. Ultimately, guest gets to idle loop
+ * and then will request deboost.
+ */
+ kvm_vcpu_set_sched(vcpu, true);
+
waited = true;
schedule();
}
--
2.43.0