18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * An implementation of host initiated guest snapshot.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013, Microsoft, Inc.
68c2ecf20Sopenharmony_ci * Author : K. Y. Srinivasan <kys@microsoft.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/net.h>
118c2ecf20Sopenharmony_ci#include <linux/nls.h>
128c2ecf20Sopenharmony_ci#include <linux/connector.h>
138c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
148c2ecf20Sopenharmony_ci#include <linux/hyperv.h>
158c2ecf20Sopenharmony_ci#include <asm/hyperv-tlfs.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h"
188c2ecf20Sopenharmony_ci#include "hv_utils_transport.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define VSS_MAJOR  5
218c2ecf20Sopenharmony_ci#define VSS_MINOR  0
228c2ecf20Sopenharmony_ci#define VSS_VERSION    (VSS_MAJOR << 16 | VSS_MINOR)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define VSS_VER_COUNT 1
258c2ecf20Sopenharmony_cistatic const int vss_versions[] = {
268c2ecf20Sopenharmony_ci	VSS_VERSION
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define FW_VER_COUNT 1
308c2ecf20Sopenharmony_cistatic const int fw_versions[] = {
318c2ecf20Sopenharmony_ci	UTIL_FW_VERSION
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * Timeout values are based on expecations from host
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#define VSS_FREEZE_TIMEOUT (15 * 60)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Global state maintained for transaction that is being processed. For a class
418c2ecf20Sopenharmony_ci * of integration services, including the "VSS service", the specified protocol
428c2ecf20Sopenharmony_ci * is a "request/response" protocol which means that there can only be single
438c2ecf20Sopenharmony_ci * outstanding transaction from the host at any given point in time. We use
448c2ecf20Sopenharmony_ci * this to simplify memory management in this driver - we cache and process
458c2ecf20Sopenharmony_ci * only one message at a time.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * While the request/response protocol is guaranteed by the host, we further
488c2ecf20Sopenharmony_ci * ensure this by serializing packet processing in this driver - we do not
498c2ecf20Sopenharmony_ci * read additional packets from the VMBUs until the current packet is fully
508c2ecf20Sopenharmony_ci * handled.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic struct {
548c2ecf20Sopenharmony_ci	int state;   /* hvutil_device_state */
558c2ecf20Sopenharmony_ci	int recv_len; /* number of bytes received. */
568c2ecf20Sopenharmony_ci	struct vmbus_channel *recv_channel; /* chn we got the request */
578c2ecf20Sopenharmony_ci	u64 recv_req_id; /* request ID. */
588c2ecf20Sopenharmony_ci	struct hv_vss_msg  *msg; /* current message */
598c2ecf20Sopenharmony_ci} vss_transaction;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void vss_respond_to_host(int error);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * This state maintains the version number registered by the daemon.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic int dm_reg_value;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic const char vss_devname[] = "vmbus/hv_vss";
708c2ecf20Sopenharmony_cistatic __u8 *recv_buffer;
718c2ecf20Sopenharmony_cistatic struct hvutil_transport *hvt;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void vss_timeout_func(struct work_struct *dummy);
748c2ecf20Sopenharmony_cistatic void vss_handle_request(struct work_struct *dummy);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func);
778c2ecf20Sopenharmony_cistatic DECLARE_WORK(vss_handle_request_work, vss_handle_request);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void vss_poll_wrapper(void *channel)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	/* Transaction is finished, reset the state here to avoid races. */
828c2ecf20Sopenharmony_ci	vss_transaction.state = HVUTIL_READY;
838c2ecf20Sopenharmony_ci	tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * Callback when data is received from user mode.
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void vss_timeout_func(struct work_struct *dummy)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	/*
938c2ecf20Sopenharmony_ci	 * Timeout waiting for userspace component to reply happened.
948c2ecf20Sopenharmony_ci	 */
958c2ecf20Sopenharmony_ci	pr_warn("VSS: timeout waiting for daemon to reply\n");
968c2ecf20Sopenharmony_ci	vss_respond_to_host(HV_E_FAIL);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void vss_register_done(void)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
1048c2ecf20Sopenharmony_ci	pr_debug("VSS: userspace daemon registered\n");
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int vss_handle_handshake(struct hv_vss_msg *vss_msg)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u32 our_ver = VSS_OP_REGISTER1;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	switch (vss_msg->vss_hdr.operation) {
1128c2ecf20Sopenharmony_ci	case VSS_OP_REGISTER:
1138c2ecf20Sopenharmony_ci		/* Daemon doesn't expect us to reply */
1148c2ecf20Sopenharmony_ci		dm_reg_value = VSS_OP_REGISTER;
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case VSS_OP_REGISTER1:
1178c2ecf20Sopenharmony_ci		/* Daemon expects us to reply with our own version */
1188c2ecf20Sopenharmony_ci		if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
1198c2ecf20Sopenharmony_ci					  vss_register_done))
1208c2ecf20Sopenharmony_ci			return -EFAULT;
1218c2ecf20Sopenharmony_ci		dm_reg_value = VSS_OP_REGISTER1;
1228c2ecf20Sopenharmony_ci		break;
1238c2ecf20Sopenharmony_ci	default:
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	pr_info("VSS: userspace daemon ver. %d connected\n", dm_reg_value);
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int vss_on_msg(void *msg, int len)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct hv_vss_msg *vss_msg = (struct hv_vss_msg *)msg;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (len != sizeof(*vss_msg)) {
1358c2ecf20Sopenharmony_ci		pr_debug("VSS: Message size does not match length\n");
1368c2ecf20Sopenharmony_ci		return -EINVAL;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER ||
1408c2ecf20Sopenharmony_ci	    vss_msg->vss_hdr.operation == VSS_OP_REGISTER1) {
1418c2ecf20Sopenharmony_ci		/*
1428c2ecf20Sopenharmony_ci		 * Don't process registration messages if we're in the middle
1438c2ecf20Sopenharmony_ci		 * of a transaction processing.
1448c2ecf20Sopenharmony_ci		 */
1458c2ecf20Sopenharmony_ci		if (vss_transaction.state > HVUTIL_READY) {
1468c2ecf20Sopenharmony_ci			pr_debug("VSS: Got unexpected registration request\n");
1478c2ecf20Sopenharmony_ci			return -EINVAL;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		return vss_handle_handshake(vss_msg);
1518c2ecf20Sopenharmony_ci	} else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) {
1528c2ecf20Sopenharmony_ci		vss_transaction.state = HVUTIL_USERSPACE_RECV;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		if (vss_msg->vss_hdr.operation == VSS_OP_HOT_BACKUP)
1558c2ecf20Sopenharmony_ci			vss_transaction.msg->vss_cf.flags =
1568c2ecf20Sopenharmony_ci				VSS_HBU_NO_AUTO_RECOVERY;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		if (cancel_delayed_work_sync(&vss_timeout_work)) {
1598c2ecf20Sopenharmony_ci			vss_respond_to_host(vss_msg->error);
1608c2ecf20Sopenharmony_ci			/* Transaction is finished, reset the state. */
1618c2ecf20Sopenharmony_ci			hv_poll_channel(vss_transaction.recv_channel,
1628c2ecf20Sopenharmony_ci					vss_poll_wrapper);
1638c2ecf20Sopenharmony_ci		}
1648c2ecf20Sopenharmony_ci	} else {
1658c2ecf20Sopenharmony_ci		/* This is a spurious call! */
1668c2ecf20Sopenharmony_ci		pr_debug("VSS: Transaction not active\n");
1678c2ecf20Sopenharmony_ci		return -EINVAL;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void vss_send_op(void)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	int op = vss_transaction.msg->vss_hdr.operation;
1758c2ecf20Sopenharmony_ci	int rc;
1768c2ecf20Sopenharmony_ci	struct hv_vss_msg *vss_msg;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* The transaction state is wrong. */
1798c2ecf20Sopenharmony_ci	if (vss_transaction.state != HVUTIL_HOSTMSG_RECEIVED) {
1808c2ecf20Sopenharmony_ci		pr_debug("VSS: Unexpected attempt to send to daemon\n");
1818c2ecf20Sopenharmony_ci		return;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL);
1858c2ecf20Sopenharmony_ci	if (!vss_msg)
1868c2ecf20Sopenharmony_ci		return;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	vss_msg->vss_hdr.operation = op;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	vss_transaction.state = HVUTIL_USERSPACE_REQ;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	schedule_delayed_work(&vss_timeout_work, op == VSS_OP_FREEZE ?
1938c2ecf20Sopenharmony_ci			VSS_FREEZE_TIMEOUT * HZ : HV_UTIL_TIMEOUT * HZ);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
1968c2ecf20Sopenharmony_ci	if (rc) {
1978c2ecf20Sopenharmony_ci		pr_warn("VSS: failed to communicate to the daemon: %d\n", rc);
1988c2ecf20Sopenharmony_ci		if (cancel_delayed_work_sync(&vss_timeout_work)) {
1998c2ecf20Sopenharmony_ci			vss_respond_to_host(HV_E_FAIL);
2008c2ecf20Sopenharmony_ci			vss_transaction.state = HVUTIL_READY;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	kfree(vss_msg);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void vss_handle_request(struct work_struct *dummy)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	switch (vss_transaction.msg->vss_hdr.operation) {
2108c2ecf20Sopenharmony_ci	/*
2118c2ecf20Sopenharmony_ci	 * Initiate a "freeze/thaw" operation in the guest.
2128c2ecf20Sopenharmony_ci	 * We respond to the host once the operation is complete.
2138c2ecf20Sopenharmony_ci	 *
2148c2ecf20Sopenharmony_ci	 * We send the message to the user space daemon and the operation is
2158c2ecf20Sopenharmony_ci	 * performed in the daemon.
2168c2ecf20Sopenharmony_ci	 */
2178c2ecf20Sopenharmony_ci	case VSS_OP_THAW:
2188c2ecf20Sopenharmony_ci	case VSS_OP_FREEZE:
2198c2ecf20Sopenharmony_ci	case VSS_OP_HOT_BACKUP:
2208c2ecf20Sopenharmony_ci		if (vss_transaction.state < HVUTIL_READY) {
2218c2ecf20Sopenharmony_ci			/* Userspace is not registered yet */
2228c2ecf20Sopenharmony_ci			pr_debug("VSS: Not ready for request.\n");
2238c2ecf20Sopenharmony_ci			vss_respond_to_host(HV_E_FAIL);
2248c2ecf20Sopenharmony_ci			return;
2258c2ecf20Sopenharmony_ci		}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		pr_debug("VSS: Received request for op code: %d\n",
2288c2ecf20Sopenharmony_ci			vss_transaction.msg->vss_hdr.operation);
2298c2ecf20Sopenharmony_ci		vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
2308c2ecf20Sopenharmony_ci		vss_send_op();
2318c2ecf20Sopenharmony_ci		return;
2328c2ecf20Sopenharmony_ci	case VSS_OP_GET_DM_INFO:
2338c2ecf20Sopenharmony_ci		vss_transaction.msg->dm_info.flags = 0;
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	default:
2368c2ecf20Sopenharmony_ci		break;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	vss_respond_to_host(0);
2408c2ecf20Sopenharmony_ci	hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/*
2448c2ecf20Sopenharmony_ci * Send a response back to the host.
2458c2ecf20Sopenharmony_ci */
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic void
2488c2ecf20Sopenharmony_civss_respond_to_host(int error)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct icmsg_hdr *icmsghdrp;
2518c2ecf20Sopenharmony_ci	u32	buf_len;
2528c2ecf20Sopenharmony_ci	struct vmbus_channel *channel;
2538c2ecf20Sopenharmony_ci	u64	req_id;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/*
2568c2ecf20Sopenharmony_ci	 * Copy the global state for completing the transaction. Note that
2578c2ecf20Sopenharmony_ci	 * only one transaction can be active at a time.
2588c2ecf20Sopenharmony_ci	 */
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	buf_len = vss_transaction.recv_len;
2618c2ecf20Sopenharmony_ci	channel = vss_transaction.recv_channel;
2628c2ecf20Sopenharmony_ci	req_id = vss_transaction.recv_req_id;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	icmsghdrp = (struct icmsg_hdr *)
2658c2ecf20Sopenharmony_ci			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (channel->onchannel_callback == NULL)
2688c2ecf20Sopenharmony_ci		/*
2698c2ecf20Sopenharmony_ci		 * We have raced with util driver being unloaded;
2708c2ecf20Sopenharmony_ci		 * silently return.
2718c2ecf20Sopenharmony_ci		 */
2728c2ecf20Sopenharmony_ci		return;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	icmsghdrp->status = error;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
2798c2ecf20Sopenharmony_ci				VM_PKT_DATA_INBAND, 0);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/*
2848c2ecf20Sopenharmony_ci * This callback is invoked when we get a VSS message from the host.
2858c2ecf20Sopenharmony_ci * The host ensures that only one VSS transaction can be active at a time.
2868c2ecf20Sopenharmony_ci */
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_civoid hv_vss_onchannelcallback(void *context)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct vmbus_channel *channel = context;
2918c2ecf20Sopenharmony_ci	u32 recvlen;
2928c2ecf20Sopenharmony_ci	u64 requestid;
2938c2ecf20Sopenharmony_ci	struct hv_vss_msg *vss_msg;
2948c2ecf20Sopenharmony_ci	int vss_srv_version;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	struct icmsg_hdr *icmsghdrp;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (vss_transaction.state > HVUTIL_READY)
2998c2ecf20Sopenharmony_ci		return;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen,
3028c2ecf20Sopenharmony_ci			 &requestid);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (recvlen > 0) {
3058c2ecf20Sopenharmony_ci		icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
3068c2ecf20Sopenharmony_ci			sizeof(struct vmbuspipe_hdr)];
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
3098c2ecf20Sopenharmony_ci			if (vmbus_prep_negotiate_resp(icmsghdrp,
3108c2ecf20Sopenharmony_ci				 recv_buffer, fw_versions, FW_VER_COUNT,
3118c2ecf20Sopenharmony_ci				 vss_versions, VSS_VER_COUNT,
3128c2ecf20Sopenharmony_ci				 NULL, &vss_srv_version)) {
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci				pr_info("VSS IC version %d.%d\n",
3158c2ecf20Sopenharmony_ci					vss_srv_version >> 16,
3168c2ecf20Sopenharmony_ci					vss_srv_version & 0xFFFF);
3178c2ecf20Sopenharmony_ci			}
3188c2ecf20Sopenharmony_ci		} else {
3198c2ecf20Sopenharmony_ci			vss_msg = (struct hv_vss_msg *)&recv_buffer[
3208c2ecf20Sopenharmony_ci				sizeof(struct vmbuspipe_hdr) +
3218c2ecf20Sopenharmony_ci				sizeof(struct icmsg_hdr)];
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci			/*
3248c2ecf20Sopenharmony_ci			 * Stash away this global state for completing the
3258c2ecf20Sopenharmony_ci			 * transaction; note transactions are serialized.
3268c2ecf20Sopenharmony_ci			 */
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci			vss_transaction.recv_len = recvlen;
3298c2ecf20Sopenharmony_ci			vss_transaction.recv_req_id = requestid;
3308c2ecf20Sopenharmony_ci			vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci			schedule_work(&vss_handle_request_work);
3338c2ecf20Sopenharmony_ci			return;
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
3378c2ecf20Sopenharmony_ci			| ICMSGHDRFLAG_RESPONSE;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		vmbus_sendpacket(channel, recv_buffer,
3408c2ecf20Sopenharmony_ci				       recvlen, requestid,
3418c2ecf20Sopenharmony_ci				       VM_PKT_DATA_INBAND, 0);
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void vss_on_reset(void)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	if (cancel_delayed_work_sync(&vss_timeout_work))
3498c2ecf20Sopenharmony_ci		vss_respond_to_host(HV_E_FAIL);
3508c2ecf20Sopenharmony_ci	vss_transaction.state = HVUTIL_DEVICE_INIT;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ciint
3548c2ecf20Sopenharmony_cihv_vss_init(struct hv_util_service *srv)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	if (vmbus_proto_version < VERSION_WIN8_1) {
3578c2ecf20Sopenharmony_ci		pr_warn("Integration service 'Backup (volume snapshot)'"
3588c2ecf20Sopenharmony_ci			" not supported on this host version.\n");
3598c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci	recv_buffer = srv->recv_buffer;
3628c2ecf20Sopenharmony_ci	vss_transaction.recv_channel = srv->channel;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * When this driver loads, the user level daemon that
3668c2ecf20Sopenharmony_ci	 * processes the host requests may not yet be running.
3678c2ecf20Sopenharmony_ci	 * Defer processing channel callbacks until the daemon
3688c2ecf20Sopenharmony_ci	 * has registered.
3698c2ecf20Sopenharmony_ci	 */
3708c2ecf20Sopenharmony_ci	vss_transaction.state = HVUTIL_DEVICE_INIT;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL,
3738c2ecf20Sopenharmony_ci				    vss_on_msg, vss_on_reset);
3748c2ecf20Sopenharmony_ci	if (!hvt) {
3758c2ecf20Sopenharmony_ci		pr_warn("VSS: Failed to initialize transport\n");
3768c2ecf20Sopenharmony_ci		return -EFAULT;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic void hv_vss_cancel_work(void)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&vss_timeout_work);
3858c2ecf20Sopenharmony_ci	cancel_work_sync(&vss_handle_request_work);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ciint hv_vss_pre_suspend(void)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct vmbus_channel *channel = vss_transaction.recv_channel;
3918c2ecf20Sopenharmony_ci	struct hv_vss_msg *vss_msg;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/*
3948c2ecf20Sopenharmony_ci	 * Fake a THAW message for the user space daemon in case the daemon
3958c2ecf20Sopenharmony_ci	 * has frozen the file systems. It doesn't matter if there is already
3968c2ecf20Sopenharmony_ci	 * a message pending to be delivered to the user space since we force
3978c2ecf20Sopenharmony_ci	 * vss_transaction.state to be HVUTIL_READY, so the user space daemon's
3988c2ecf20Sopenharmony_ci	 * write() will fail with EINVAL (see vss_on_msg()), and the daemon
3998c2ecf20Sopenharmony_ci	 * will reset the device by closing and re-opening it.
4008c2ecf20Sopenharmony_ci	 */
4018c2ecf20Sopenharmony_ci	vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL);
4028c2ecf20Sopenharmony_ci	if (!vss_msg)
4038c2ecf20Sopenharmony_ci		return -ENOMEM;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	tasklet_disable(&channel->callback_event);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	vss_msg->vss_hdr.operation = VSS_OP_THAW;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* Cancel any possible pending work. */
4108c2ecf20Sopenharmony_ci	hv_vss_cancel_work();
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* We don't care about the return value. */
4138c2ecf20Sopenharmony_ci	hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	kfree(vss_msg);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	vss_transaction.state = HVUTIL_READY;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* tasklet_enable() will be called in hv_vss_pre_resume(). */
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ciint hv_vss_pre_resume(void)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct vmbus_channel *channel = vss_transaction.recv_channel;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	tasklet_enable(&channel->callback_event);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_civoid hv_vss_deinit(void)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	vss_transaction.state = HVUTIL_DEVICE_DYING;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	hv_vss_cancel_work();
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	hvutil_transport_destroy(hvt);
4398c2ecf20Sopenharmony_ci}
440