summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDexuan Cui <decui@microsoft.com>2019-09-05 19:01:16 -0400
committerSasha Levin <sashal@kernel.org>2019-09-06 14:52:44 -0400
commit63ecc6d22ce46643165c391a9c90ba67e22e1c0f (patch)
tree3d0f5dacce230af9b863215332cf10bb55ca2b66
parentdba61cda30469a6c4fed0f8d5bf2b6001ca80a51 (diff)
Drivers: hv: vmbus: Suspend/resume the synic for hibernation
This is needed when we resume the old kernel from the "current" kernel. Note: when hv_synic_suspend() and hv_synic_resume() run, all the non-boot CPUs have been offlined, and interrupts are disabled on CPU0. Signed-off-by: Dexuan Cui <decui@microsoft.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--drivers/hv/vmbus_drv.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index ebd35fc35290..2ef375ce58ac 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -30,6 +30,7 @@
30#include <linux/kdebug.h> 30#include <linux/kdebug.h>
31#include <linux/efi.h> 31#include <linux/efi.h>
32#include <linux/random.h> 32#include <linux/random.h>
33#include <linux/syscore_ops.h>
33#include <clocksource/hyperv_timer.h> 34#include <clocksource/hyperv_timer.h>
34#include "hyperv_vmbus.h" 35#include "hyperv_vmbus.h"
35 36
@@ -2086,6 +2087,47 @@ static void hv_crash_handler(struct pt_regs *regs)
2086 hyperv_cleanup(); 2087 hyperv_cleanup();
2087}; 2088};
2088 2089
2090static int hv_synic_suspend(void)
2091{
2092 /*
2093 * When we reach here, all the non-boot CPUs have been offlined, and
2094 * the stimers on them have been unbound in hv_synic_cleanup() ->
2095 * hv_stimer_cleanup() -> clockevents_unbind_device().
2096 *
2097 * hv_synic_suspend() only runs on CPU0 with interrupts disabled. Here
2098 * we do not unbind the stimer on CPU0 because: 1) it's unnecessary
2099 * because the interrupts remain disabled between syscore_suspend()
2100 * and syscore_resume(): see create_image() and resume_target_kernel();
2101 * 2) the stimer on CPU0 is automatically disabled later by
2102 * syscore_suspend() -> timekeeping_suspend() -> tick_suspend() -> ...
2103 * -> clockevents_shutdown() -> ... -> hv_ce_shutdown(); 3) a warning
2104 * would be triggered if we call clockevents_unbind_device(), which
2105 * may sleep, in an interrupts-disabled context. So, we intentionally
2106 * don't call hv_stimer_cleanup(0) here.
2107 */
2108
2109 hv_synic_disable_regs(0);
2110
2111 return 0;
2112}
2113
2114static void hv_synic_resume(void)
2115{
2116 hv_synic_enable_regs(0);
2117
2118 /*
2119 * Note: we don't need to call hv_stimer_init(0), because the timer
2120 * on CPU0 is not unbound in hv_synic_suspend(), and the timer is
2121 * automatically re-enabled in timekeeping_resume().
2122 */
2123}
2124
2125/* The callbacks run only on CPU0, with irqs_disabled. */
2126static struct syscore_ops hv_synic_syscore_ops = {
2127 .suspend = hv_synic_suspend,
2128 .resume = hv_synic_resume,
2129};
2130
2089static int __init hv_acpi_init(void) 2131static int __init hv_acpi_init(void)
2090{ 2132{
2091 int ret, t; 2133 int ret, t;
@@ -2116,6 +2158,8 @@ static int __init hv_acpi_init(void)
2116 hv_setup_kexec_handler(hv_kexec_handler); 2158 hv_setup_kexec_handler(hv_kexec_handler);
2117 hv_setup_crash_handler(hv_crash_handler); 2159 hv_setup_crash_handler(hv_crash_handler);
2118 2160
2161 register_syscore_ops(&hv_synic_syscore_ops);
2162
2119 return 0; 2163 return 0;
2120 2164
2121cleanup: 2165cleanup:
@@ -2128,6 +2172,8 @@ static void __exit vmbus_exit(void)
2128{ 2172{
2129 int cpu; 2173 int cpu;
2130 2174
2175 unregister_syscore_ops(&hv_synic_syscore_ops);
2176
2131 hv_remove_kexec_handler(); 2177 hv_remove_kexec_handler();
2132 hv_remove_crash_handler(); 2178 hv_remove_crash_handler();
2133 vmbus_connection.conn_state = DISCONNECTED; 2179 vmbus_connection.conn_state = DISCONNECTED;