Update the Shutdown IC version to 3.2, which is required for the host to send the hibernation request. The user is expected to create the program "/sbin/hyperv-hibernate", which is called on the host-initiated hibernation request. The program can be a script like test@localhost:~$ cat /sbin/hyperv-hibernate #!/bin/bash echo disk > /sys/power/state Signed-off-by: Dexuan Cui <decui@xxxxxxxxxxxxx> --- drivers/hv/hv_util.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 039c752..9e98c5d 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -24,6 +24,8 @@ #define SD_MAJOR 3 #define SD_MINOR 0 +#define SD_MINOR_2 2 +#define SD_VERSION_3_2 (SD_MAJOR << 16 | SD_MINOR_2) #define SD_VERSION (SD_MAJOR << 16 | SD_MINOR) #define SD_MAJOR_1 1 @@ -50,8 +52,9 @@ static int ts_srv_version; static int hb_srv_version; -#define SD_VER_COUNT 2 +#define SD_VER_COUNT 3 static const int sd_versions[] = { + SD_VERSION_3_2, SD_VERSION, SD_VERSION_1 }; @@ -75,9 +78,30 @@ UTIL_WS2K8_FW_VERSION }; +static bool execute_hibernate; +static int hv_shutdown_init(struct hv_util_service *srv) +{ +#if 0 + /* + * The patch to implement hv_is_hibernation_supported() is going + * through the tip tree. For now, let's hardcode execute_hibernate + * to true -- this doesn't break anything since hibernation for + * Linux VM on Hyper-V never worked before. We'll remove the + * conditional compilation as soon as hv_is_hibernation_supported() + * is available in the mainline tree. + */ + execute_hibernate = hv_is_hibernation_supported(); +#else + execute_hibernate = true; +#endif + + return 0; +} + static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, + .util_init = hv_shutdown_init, }; static int hv_timesync_init(struct hv_util_service *srv); @@ -123,11 +147,38 @@ static void perform_shutdown(struct work_struct *dummy) orderly_poweroff(true); } +static void perform_hibernation(struct work_struct *dummy) +{ + /* + * The user is expected to create the program, which can be a simple + * script containing two lines: + * #!/bin/bash + * echo disk > /sys/power/state + */ + static char hibernate_cmd[PATH_MAX] = "/sbin/hyperv-hibernate"; + + static char *envp[] = { + NULL, + }; + + static char *argv[] = { + hibernate_cmd, + NULL, + }; + + call_usermodehelper(hibernate_cmd, argv, envp, UMH_NO_WAIT); +} + /* * Perform the shutdown operation in a thread context. */ static DECLARE_WORK(shutdown_work, perform_shutdown); +/* + * Perform the hibernation operation in a thread context. + */ +static DECLARE_WORK(hibernate_work, perform_hibernation); + static void shutdown_onchannelcallback(void *context) { struct vmbus_channel *channel = context; @@ -171,6 +222,19 @@ static void shutdown_onchannelcallback(void *context) pr_info("Shutdown request received -" " graceful shutdown initiated\n"); break; + case 4: + case 5: + pr_info("Hibernation request received -" + " hibernation %sinitiated\n", + execute_hibernate ? "" : "not "); + + if (execute_hibernate) { + icmsghdrp->status = HV_S_OK; + schedule_work(&hibernate_work); + } else { + icmsghdrp->status = HV_E_FAIL; + } + break; default: icmsghdrp->status = HV_E_FAIL; execute_shutdown = false; -- 1.8.3.1