cpuidle-haltpoll: disable host side polling when kvm virtualized
authorMarcelo Tosatti <mtosatti@redhat.com>
Wed, 3 Jul 2019 23:51:29 +0000 (20:51 -0300)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 30 Jul 2019 15:27:37 +0000 (17:27 +0200)
When performing guest side polling, it is not necessary to
also perform host side polling.

So disable host side polling, via the new MSR interface,
when loading cpuidle-haltpoll driver.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
arch/x86/Kconfig
arch/x86/include/asm/cpuidle_haltpoll.h [new file with mode: 0644]
arch/x86/kernel/kvm.c
drivers/cpuidle/cpuidle-haltpoll.c
include/linux/cpuidle_haltpoll.h [new file with mode: 0644]

index 222855cc0158422f1d6dddc2d9307cb606433a37..05e78acb187c726cd77dc0b979fe84f0aae32d4c 100644 (file)
@@ -794,6 +794,7 @@ config KVM_GUEST
        bool "KVM Guest support (including kvmclock)"
        depends on PARAVIRT
        select PARAVIRT_CLOCK
+       select ARCH_CPUIDLE_HALTPOLL
        default y
        ---help---
          This option enables various optimizations for running under the KVM
@@ -802,6 +803,12 @@ config KVM_GUEST
          underlying device model, the host provides the guest with
          timing infrastructure such as time of day, and system time
 
+config ARCH_CPUIDLE_HALTPOLL
+        def_bool n
+        prompt "Disable host haltpoll when loading haltpoll driver"
+        help
+         If virtualized under KVM, disable host haltpoll.
+
 config PVH
        bool "Support for running PVH guests"
        ---help---
diff --git a/arch/x86/include/asm/cpuidle_haltpoll.h b/arch/x86/include/asm/cpuidle_haltpoll.h
new file mode 100644 (file)
index 0000000..ff8607d
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ARCH_HALTPOLL_H
+#define _ARCH_HALTPOLL_H
+
+void arch_haltpoll_enable(void);
+void arch_haltpoll_disable(void);
+
+#endif
index b7f34fe2171e472f7f64bf7f93c9bdfcaefba710..f48401be8ce00a2bb29826e3464df6597ac48ad7 100644 (file)
@@ -875,3 +875,45 @@ void __init kvm_spinlock_init(void)
 }
 
 #endif /* CONFIG_PARAVIRT_SPINLOCKS */
+
+#ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL
+
+static void kvm_disable_host_haltpoll(void *i)
+{
+       wrmsrl(MSR_KVM_POLL_CONTROL, 0);
+}
+
+static void kvm_enable_host_haltpoll(void *i)
+{
+       wrmsrl(MSR_KVM_POLL_CONTROL, 1);
+}
+
+void arch_haltpoll_enable(void)
+{
+       if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) {
+               printk(KERN_ERR "kvm: host does not support poll control\n");
+               printk(KERN_ERR "kvm: host upgrade recommended\n");
+               return;
+       }
+
+       preempt_disable();
+       /* Enable guest halt poll disables host halt poll */
+       kvm_disable_host_haltpoll(NULL);
+       smp_call_function(kvm_disable_host_haltpoll, NULL, 1);
+       preempt_enable();
+}
+EXPORT_SYMBOL_GPL(arch_haltpoll_enable);
+
+void arch_haltpoll_disable(void)
+{
+       if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL))
+               return;
+
+       preempt_disable();
+       /* Enable guest halt poll disables host halt poll */
+       kvm_enable_host_haltpoll(NULL);
+       smp_call_function(kvm_enable_host_haltpoll, NULL, 1);
+       preempt_enable();
+}
+EXPORT_SYMBOL_GPL(arch_haltpoll_disable);
+#endif
index 35cfb53e928704082e0c76add9585eb272172e20..9ac093dcbb01f26593b9f989ee6b3faa619fc035 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/sched/idle.h>
 #include <linux/kvm_para.h>
+#include <linux/cpuidle_haltpoll.h>
 
 static int default_enter_idle(struct cpuidle_device *dev,
                              struct cpuidle_driver *drv, int index)
@@ -47,6 +48,7 @@ static struct cpuidle_driver haltpoll_driver = {
 
 static int __init haltpoll_init(void)
 {
+       int ret;
        struct cpuidle_driver *drv = &haltpoll_driver;
 
        cpuidle_poll_state_init(drv);
@@ -54,11 +56,16 @@ static int __init haltpoll_init(void)
        if (!kvm_para_available())
                return 0;
 
-       return cpuidle_register(&haltpoll_driver, NULL);
+       ret = cpuidle_register(&haltpoll_driver, NULL);
+       if (ret == 0)
+               arch_haltpoll_enable();
+
+       return ret;
 }
 
 static void __exit haltpoll_exit(void)
 {
+       arch_haltpoll_disable();
        cpuidle_unregister(&haltpoll_driver);
 }
 
diff --git a/include/linux/cpuidle_haltpoll.h b/include/linux/cpuidle_haltpoll.h
new file mode 100644 (file)
index 0000000..fe5954c
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CPUIDLE_HALTPOLL_H
+#define _CPUIDLE_HALTPOLL_H
+
+#ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL
+#include <asm/cpuidle_haltpoll.h>
+#else
+static inline void arch_haltpoll_enable(void)
+{
+}
+
+static inline void arch_haltpoll_disable(void)
+{
+}
+#endif
+#endif