[PATCH 1/2] x86: notify hypervisor about guest entering s2idle state

Grzegorz Jaszczyk
2022-06-09T11:04:17Z

From: Zide Chen <zide.chen@intel.corp-partner.google.com>

Implement a new "system s2idle" hypercall allowing to notify the
hypervisor that the guest is entering s2idle power state.

Without introducing this hypercall, hypervisor can not trap on any
register write or any other VM exit while the guest is entering s2idle
state.

Co-developed-by: Peter Fang <peter.fang@intel.corp-partner.google.com>
Signed-off-by: Peter Fang <peter.fang@intel.corp-partner.google.com>
Co-developed-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Zide Chen <zide.chen@intel.corp-partner.google.com>
Co-developed-by: Grzegorz Jaszczyk <jaz@semihalf.com>
Signed-off-by: Grzegorz Jaszczyk <jaz@semihalf.com>
---
Documentation/virt/kvm/x86/hypercalls.rst | 7 +++++++
arch/x86/kvm/x86.c | 3 +++
drivers/acpi/x86/s2idle.c | 8 ++++++++
include/linux/suspend.h | 1 +
include/uapi/linux/kvm_para.h | 1 +
kernel/power/suspend.c | 4 ++++
6 files changed, 24 insertions(+)

diff --git a/Documentation/virt/kvm/x86/hypercalls.rst b/Documentation/virt/kvm/x86/hypercalls.rst
index e56fa8b9cfca..9d1836c837e3 100644
--- a/Documentation/virt/kvm/x86/hypercalls.rst
+++ b/Documentation/virt/kvm/x86/hypercalls.rst
@@ -190,3 +190,10 @@ the KVM_CAP_EXIT_HYPERCALL capability. Userspace must enable that capability
before advertising KVM_FEATURE_HC_MAP_GPA_RANGE in the guest CPUID. In
addition, if the guest supports KVM_FEATURE_MIGRATION_CONTROL, userspace
must also set up an MSR filter to process writes to MSR_KVM_MIGRATION_CONTROL.
+
+9. KVM_HC_SYSTEM_S2IDLE
+------------------------
+
+:Architecture: x86
+:Status: active
+:Purpose: Notify the hypervisor that the guest is entering s2idle state.
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e9473c7c7390..6ed4bd6e762b 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -9306,6 +9306,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
vcpu->arch.complete_userspace_io = complete_hypercall_exit;
return 0;
}
+ case KVM_HC_SYSTEM_S2IDLE:
+ ret = 0;
+ break;
default:
ret = -KVM_ENOSYS;
break;
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index 2963229062f8..0ae5e11380d2 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -18,6 +18,7 @@
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/suspend.h>
+#include <uapi/linux/kvm_para.h>

#include "../sleep.h"

@@ -520,10 +521,17 @@ void acpi_s2idle_restore_early(void)
lps0_dsm_func_mask, lps0_dsm_guid);
}

+static void s2idle_hypervisor_notify(void)
+{
+ if (static_cpu_has(X86_FEATURE_HYPERVISOR))
+ kvm_hypercall0(KVM_HC_SYSTEM_S2IDLE);
+}
+
static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
.begin = acpi_s2idle_begin,
.prepare = acpi_s2idle_prepare,
.prepare_late = acpi_s2idle_prepare_late,
+ .hypervisor_notify = s2idle_hypervisor_notify,
.wake = acpi_s2idle_wake,
.restore_early = acpi_s2idle_restore_early,
.restore = acpi_s2idle_restore,
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 70f2921e2e70..42e04e0fe8b1 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -191,6 +191,7 @@ struct platform_s2idle_ops {
int (*begin)(void);
int (*prepare)(void);
int (*prepare_late)(void);
+ void (*hypervisor_notify)(void);
bool (*wake)(void);
void (*restore_early)(void);
void (*restore)(void);
diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h
index 960c7e93d1a9..072e77e40f89 100644
--- a/include/uapi/linux/kvm_para.h
+++ b/include/uapi/linux/kvm_para.h
@@ -30,6 +30,7 @@
#define KVM_HC_SEND_IPI 10
#define KVM_HC_SCHED_YIELD 11
#define KVM_HC_MAP_GPA_RANGE 12
+#define KVM_HC_SYSTEM_S2IDLE 13

/*
* hypercalls use architecture specific
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 827075944d28..c641c643290b 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -100,6 +100,10 @@ static void s2idle_enter(void)

/* Push all the CPUs into the idle loop. */
wake_up_all_idle_cpus();
+
+ if (s2idle_ops && s2idle_ops->hypervisor_notify)
+ s2idle_ops->hypervisor_notify();
+
/* Make the current CPU wait so it can enter the idle loop too. */
swait_event_exclusive(s2idle_wait_head,
s2idle_state == S2IDLE_STATE_WAKE);
--
2.36.1.476.g0c4daa206d-goog