in hv_util.c [185:281]
static void shutdown_onchannelcallback(void *context)
{
struct vmbus_channel *channel = context;
struct work_struct *work = NULL;
u32 recvlen;
u64 requestid;
u8 *shut_txf_buf = util_shutdown.recv_buffer;
struct shutdown_msg_data *shutdown_msg;
struct icmsg_hdr *icmsghdrp;
if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) {
pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n");
return;
}
if (!recvlen)
return;
/* Ensure recvlen is big enough to read header data */
if (recvlen < ICMSG_HDR) {
pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n",
recvlen);
return;
}
icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
if (vmbus_prep_negotiate_resp(icmsghdrp,
shut_txf_buf, recvlen,
fw_versions, FW_VER_COUNT,
sd_versions, SD_VER_COUNT,
NULL, &sd_srv_version)) {
pr_info("Shutdown IC version %d.%d\n",
sd_srv_version >> 16,
sd_srv_version & 0xFFFF);
}
} else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) {
/* Ensure recvlen is big enough to contain shutdown_msg_data struct */
if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) {
pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n",
recvlen);
return;
}
shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR];
/*
* shutdown_msg->flags can be 0(shut down), 2(reboot),
* or 4(hibernate). It may bitwise-OR 1, which means
* performing the request by force. Linux always tries
* to perform the request by force.
*/
switch (shutdown_msg->flags) {
case 0:
case 1:
icmsghdrp->status = HV_S_OK;
work = &shutdown_work;
pr_info("Shutdown request received - graceful shutdown initiated\n");
break;
case 2:
case 3:
icmsghdrp->status = HV_S_OK;
work = &restart_work;
pr_info("Restart request received - graceful restart initiated\n");
break;
case 4:
case 5:
pr_info("Hibernation request received\n");
icmsghdrp->status = hibernation_supported ?
HV_S_OK : HV_E_FAIL;
if (hibernation_supported)
work = &hibernate_context.work;
break;
default:
icmsghdrp->status = HV_E_FAIL;
pr_info("Shutdown request received - Invalid request\n");
break;
}
} else {
icmsghdrp->status = HV_E_FAIL;
pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n",
icmsghdrp->icmsgtype);
}
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE;
vmbus_sendpacket(channel, shut_txf_buf,
recvlen, requestid,
VM_PKT_DATA_INBAND, 0);
if (work)
schedule_work(work);
}