static void shutdown_onchannelcallback()

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);
}