162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * An implementation of host initiated guest snapshot.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013, Microsoft, Inc.
662306a36Sopenharmony_ci * Author : K. Y. Srinivasan <kys@microsoft.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/net.h>
1162306a36Sopenharmony_ci#include <linux/nls.h>
1262306a36Sopenharmony_ci#include <linux/connector.h>
1362306a36Sopenharmony_ci#include <linux/workqueue.h>
1462306a36Sopenharmony_ci#include <linux/hyperv.h>
1562306a36Sopenharmony_ci#include <asm/hyperv-tlfs.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "hyperv_vmbus.h"
1862306a36Sopenharmony_ci#include "hv_utils_transport.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define VSS_MAJOR  5
2162306a36Sopenharmony_ci#define VSS_MINOR  0
2262306a36Sopenharmony_ci#define VSS_VERSION    (VSS_MAJOR << 16 | VSS_MINOR)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define VSS_VER_COUNT 1
2562306a36Sopenharmony_cistatic const int vss_versions[] = {
2662306a36Sopenharmony_ci	VSS_VERSION
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define FW_VER_COUNT 1
3062306a36Sopenharmony_cistatic const int fw_versions[] = {
3162306a36Sopenharmony_ci	UTIL_FW_VERSION
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* See comment with struct hv_vss_msg regarding the max VMbus packet size */
3562306a36Sopenharmony_ci#define VSS_MAX_PKT_SIZE (HV_HYP_PAGE_SIZE * 2)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * Timeout values are based on expecations from host
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci#define VSS_FREEZE_TIMEOUT (15 * 60)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Global state maintained for transaction that is being processed. For a class
4462306a36Sopenharmony_ci * of integration services, including the "VSS service", the specified protocol
4562306a36Sopenharmony_ci * is a "request/response" protocol which means that there can only be single
4662306a36Sopenharmony_ci * outstanding transaction from the host at any given point in time. We use
4762306a36Sopenharmony_ci * this to simplify memory management in this driver - we cache and process
4862306a36Sopenharmony_ci * only one message at a time.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * While the request/response protocol is guaranteed by the host, we further
5162306a36Sopenharmony_ci * ensure this by serializing packet processing in this driver - we do not
5262306a36Sopenharmony_ci * read additional packets from the VMBUs until the current packet is fully
5362306a36Sopenharmony_ci * handled.
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic struct {
5762306a36Sopenharmony_ci	int state;   /* hvutil_device_state */
5862306a36Sopenharmony_ci	int recv_len; /* number of bytes received. */
5962306a36Sopenharmony_ci	struct vmbus_channel *recv_channel; /* chn we got the request */
6062306a36Sopenharmony_ci	u64 recv_req_id; /* request ID. */
6162306a36Sopenharmony_ci	struct hv_vss_msg  *msg; /* current message */
6262306a36Sopenharmony_ci} vss_transaction;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void vss_respond_to_host(int error);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * This state maintains the version number registered by the daemon.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic int dm_reg_value;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic const char vss_devname[] = "vmbus/hv_vss";
7362306a36Sopenharmony_cistatic __u8 *recv_buffer;
7462306a36Sopenharmony_cistatic struct hvutil_transport *hvt;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void vss_timeout_func(struct work_struct *dummy);
7762306a36Sopenharmony_cistatic void vss_handle_request(struct work_struct *dummy);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func);
8062306a36Sopenharmony_cistatic DECLARE_WORK(vss_handle_request_work, vss_handle_request);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void vss_poll_wrapper(void *channel)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	/* Transaction is finished, reset the state here to avoid races. */
8562306a36Sopenharmony_ci	vss_transaction.state = HVUTIL_READY;
8662306a36Sopenharmony_ci	tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * Callback when data is received from user mode.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void vss_timeout_func(struct work_struct *dummy)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * Timeout waiting for userspace component to reply happened.
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	pr_warn("VSS: timeout waiting for daemon to reply\n");
9962306a36Sopenharmony_ci	vss_respond_to_host(HV_E_FAIL);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void vss_register_done(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
10762306a36Sopenharmony_ci	pr_debug("VSS: userspace daemon registered\n");
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int vss_handle_handshake(struct hv_vss_msg *vss_msg)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	u32 our_ver = VSS_OP_REGISTER1;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	switch (vss_msg->vss_hdr.operation) {
11562306a36Sopenharmony_ci	case VSS_OP_REGISTER:
11662306a36Sopenharmony_ci		/* Daemon doesn't expect us to reply */
11762306a36Sopenharmony_ci		dm_reg_value = VSS_OP_REGISTER;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	case VSS_OP_REGISTER1:
12062306a36Sopenharmony_ci		/* Daemon expects us to reply with our own version */
12162306a36Sopenharmony_ci		if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
12262306a36Sopenharmony_ci					  vss_register_done))
12362306a36Sopenharmony_ci			return -EFAULT;
12462306a36Sopenharmony_ci		dm_reg_value = VSS_OP_REGISTER1;
12562306a36Sopenharmony_ci		break;
12662306a36Sopenharmony_ci	default:
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	pr_info("VSS: userspace daemon ver. %d connected\n", dm_reg_value);
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int vss_on_msg(void *msg, int len)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct hv_vss_msg *vss_msg = (struct hv_vss_msg *)msg;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (len != sizeof(*vss_msg)) {
13862306a36Sopenharmony_ci		pr_debug("VSS: Message size does not match length\n");
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER ||
14362306a36Sopenharmony_ci	    vss_msg->vss_hdr.operation == VSS_OP_REGISTER1) {
14462306a36Sopenharmony_ci		/*
14562306a36Sopenharmony_ci		 * Don't process registration messages if we're in the middle
14662306a36Sopenharmony_ci		 * of a transaction processing.
14762306a36Sopenharmony_ci		 */
14862306a36Sopenharmony_ci		if (vss_transaction.state > HVUTIL_READY) {
14962306a36Sopenharmony_ci			pr_debug("VSS: Got unexpected registration request\n");
15062306a36Sopenharmony_ci			return -EINVAL;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		return vss_handle_handshake(vss_msg);
15462306a36Sopenharmony_ci	} else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) {
15562306a36Sopenharmony_ci		vss_transaction.state = HVUTIL_USERSPACE_RECV;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		if (vss_msg->vss_hdr.operation == VSS_OP_HOT_BACKUP)
15862306a36Sopenharmony_ci			vss_transaction.msg->vss_cf.flags =
15962306a36Sopenharmony_ci				VSS_HBU_NO_AUTO_RECOVERY;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if (cancel_delayed_work_sync(&vss_timeout_work)) {
16262306a36Sopenharmony_ci			vss_respond_to_host(vss_msg->error);
16362306a36Sopenharmony_ci			/* Transaction is finished, reset the state. */
16462306a36Sopenharmony_ci			hv_poll_channel(vss_transaction.recv_channel,
16562306a36Sopenharmony_ci					vss_poll_wrapper);
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	} else {
16862306a36Sopenharmony_ci		/* This is a spurious call! */
16962306a36Sopenharmony_ci		pr_debug("VSS: Transaction not active\n");
17062306a36Sopenharmony_ci		return -EINVAL;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void vss_send_op(void)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	int op = vss_transaction.msg->vss_hdr.operation;
17862306a36Sopenharmony_ci	int rc;
17962306a36Sopenharmony_ci	struct hv_vss_msg *vss_msg;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* The transaction state is wrong. */
18262306a36Sopenharmony_ci	if (vss_transaction.state != HVUTIL_HOSTMSG_RECEIVED) {
18362306a36Sopenharmony_ci		pr_debug("VSS: Unexpected attempt to send to daemon\n");
18462306a36Sopenharmony_ci		return;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL);
18862306a36Sopenharmony_ci	if (!vss_msg)
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	vss_msg->vss_hdr.operation = op;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	vss_transaction.state = HVUTIL_USERSPACE_REQ;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	schedule_delayed_work(&vss_timeout_work, op == VSS_OP_FREEZE ?
19662306a36Sopenharmony_ci			VSS_FREEZE_TIMEOUT * HZ : HV_UTIL_TIMEOUT * HZ);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
19962306a36Sopenharmony_ci	if (rc) {
20062306a36Sopenharmony_ci		pr_warn("VSS: failed to communicate to the daemon: %d\n", rc);
20162306a36Sopenharmony_ci		if (cancel_delayed_work_sync(&vss_timeout_work)) {
20262306a36Sopenharmony_ci			vss_respond_to_host(HV_E_FAIL);
20362306a36Sopenharmony_ci			vss_transaction.state = HVUTIL_READY;
20462306a36Sopenharmony_ci		}
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	kfree(vss_msg);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void vss_handle_request(struct work_struct *dummy)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	switch (vss_transaction.msg->vss_hdr.operation) {
21362306a36Sopenharmony_ci	/*
21462306a36Sopenharmony_ci	 * Initiate a "freeze/thaw" operation in the guest.
21562306a36Sopenharmony_ci	 * We respond to the host once the operation is complete.
21662306a36Sopenharmony_ci	 *
21762306a36Sopenharmony_ci	 * We send the message to the user space daemon and the operation is
21862306a36Sopenharmony_ci	 * performed in the daemon.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	case VSS_OP_THAW:
22162306a36Sopenharmony_ci	case VSS_OP_FREEZE:
22262306a36Sopenharmony_ci	case VSS_OP_HOT_BACKUP:
22362306a36Sopenharmony_ci		if (vss_transaction.state < HVUTIL_READY) {
22462306a36Sopenharmony_ci			/* Userspace is not registered yet */
22562306a36Sopenharmony_ci			pr_debug("VSS: Not ready for request.\n");
22662306a36Sopenharmony_ci			vss_respond_to_host(HV_E_FAIL);
22762306a36Sopenharmony_ci			return;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		pr_debug("VSS: Received request for op code: %d\n",
23162306a36Sopenharmony_ci			vss_transaction.msg->vss_hdr.operation);
23262306a36Sopenharmony_ci		vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
23362306a36Sopenharmony_ci		vss_send_op();
23462306a36Sopenharmony_ci		return;
23562306a36Sopenharmony_ci	case VSS_OP_GET_DM_INFO:
23662306a36Sopenharmony_ci		vss_transaction.msg->dm_info.flags = 0;
23762306a36Sopenharmony_ci		break;
23862306a36Sopenharmony_ci	default:
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	vss_respond_to_host(0);
24362306a36Sopenharmony_ci	hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/*
24762306a36Sopenharmony_ci * Send a response back to the host.
24862306a36Sopenharmony_ci */
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void
25162306a36Sopenharmony_civss_respond_to_host(int error)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct icmsg_hdr *icmsghdrp;
25462306a36Sopenharmony_ci	u32	buf_len;
25562306a36Sopenharmony_ci	struct vmbus_channel *channel;
25662306a36Sopenharmony_ci	u64	req_id;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/*
25962306a36Sopenharmony_ci	 * Copy the global state for completing the transaction. Note that
26062306a36Sopenharmony_ci	 * only one transaction can be active at a time.
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	buf_len = vss_transaction.recv_len;
26462306a36Sopenharmony_ci	channel = vss_transaction.recv_channel;
26562306a36Sopenharmony_ci	req_id = vss_transaction.recv_req_id;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	icmsghdrp = (struct icmsg_hdr *)
26862306a36Sopenharmony_ci			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (channel->onchannel_callback == NULL)
27162306a36Sopenharmony_ci		/*
27262306a36Sopenharmony_ci		 * We have raced with util driver being unloaded;
27362306a36Sopenharmony_ci		 * silently return.
27462306a36Sopenharmony_ci		 */
27562306a36Sopenharmony_ci		return;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	icmsghdrp->status = error;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
28262306a36Sopenharmony_ci				VM_PKT_DATA_INBAND, 0);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * This callback is invoked when we get a VSS message from the host.
28862306a36Sopenharmony_ci * The host ensures that only one VSS transaction can be active at a time.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_civoid hv_vss_onchannelcallback(void *context)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct vmbus_channel *channel = context;
29462306a36Sopenharmony_ci	u32 recvlen;
29562306a36Sopenharmony_ci	u64 requestid;
29662306a36Sopenharmony_ci	struct hv_vss_msg *vss_msg;
29762306a36Sopenharmony_ci	int vss_srv_version;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	struct icmsg_hdr *icmsghdrp;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (vss_transaction.state > HVUTIL_READY)
30262306a36Sopenharmony_ci		return;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (vmbus_recvpacket(channel, recv_buffer, VSS_MAX_PKT_SIZE, &recvlen, &requestid)) {
30562306a36Sopenharmony_ci		pr_err_ratelimited("VSS request received. Could not read into recv buf\n");
30662306a36Sopenharmony_ci		return;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!recvlen)
31062306a36Sopenharmony_ci		return;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* Ensure recvlen is big enough to read header data */
31362306a36Sopenharmony_ci	if (recvlen < ICMSG_HDR) {
31462306a36Sopenharmony_ci		pr_err_ratelimited("VSS request received. Packet length too small: %d\n",
31562306a36Sopenharmony_ci				   recvlen);
31662306a36Sopenharmony_ci		return;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)];
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
32262306a36Sopenharmony_ci		if (vmbus_prep_negotiate_resp(icmsghdrp,
32362306a36Sopenharmony_ci				recv_buffer, recvlen,
32462306a36Sopenharmony_ci				fw_versions, FW_VER_COUNT,
32562306a36Sopenharmony_ci				vss_versions, VSS_VER_COUNT,
32662306a36Sopenharmony_ci				NULL, &vss_srv_version)) {
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci			pr_info("VSS IC version %d.%d\n",
32962306a36Sopenharmony_ci				vss_srv_version >> 16,
33062306a36Sopenharmony_ci				vss_srv_version & 0xFFFF);
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci	} else if (icmsghdrp->icmsgtype == ICMSGTYPE_VSS) {
33362306a36Sopenharmony_ci		/* Ensure recvlen is big enough to contain hv_vss_msg */
33462306a36Sopenharmony_ci		if (recvlen < ICMSG_HDR + sizeof(struct hv_vss_msg)) {
33562306a36Sopenharmony_ci			pr_err_ratelimited("Invalid VSS msg. Packet length too small: %u\n",
33662306a36Sopenharmony_ci					   recvlen);
33762306a36Sopenharmony_ci			return;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci		vss_msg = (struct hv_vss_msg *)&recv_buffer[ICMSG_HDR];
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		/*
34262306a36Sopenharmony_ci		 * Stash away this global state for completing the
34362306a36Sopenharmony_ci		 * transaction; note transactions are serialized.
34462306a36Sopenharmony_ci		 */
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		vss_transaction.recv_len = recvlen;
34762306a36Sopenharmony_ci		vss_transaction.recv_req_id = requestid;
34862306a36Sopenharmony_ci		vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		schedule_work(&vss_handle_request_work);
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci	} else {
35362306a36Sopenharmony_ci		pr_err_ratelimited("VSS request received. Invalid msg type: %d\n",
35462306a36Sopenharmony_ci				   icmsghdrp->icmsgtype);
35562306a36Sopenharmony_ci		return;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION |
35962306a36Sopenharmony_ci		ICMSGHDRFLAG_RESPONSE;
36062306a36Sopenharmony_ci	vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
36162306a36Sopenharmony_ci			 VM_PKT_DATA_INBAND, 0);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void vss_on_reset(void)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	if (cancel_delayed_work_sync(&vss_timeout_work))
36762306a36Sopenharmony_ci		vss_respond_to_host(HV_E_FAIL);
36862306a36Sopenharmony_ci	vss_transaction.state = HVUTIL_DEVICE_INIT;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ciint
37262306a36Sopenharmony_cihv_vss_init(struct hv_util_service *srv)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	if (vmbus_proto_version < VERSION_WIN8_1) {
37562306a36Sopenharmony_ci		pr_warn("Integration service 'Backup (volume snapshot)'"
37662306a36Sopenharmony_ci			" not supported on this host version.\n");
37762306a36Sopenharmony_ci		return -ENOTSUPP;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	recv_buffer = srv->recv_buffer;
38062306a36Sopenharmony_ci	vss_transaction.recv_channel = srv->channel;
38162306a36Sopenharmony_ci	vss_transaction.recv_channel->max_pkt_size = VSS_MAX_PKT_SIZE;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * When this driver loads, the user level daemon that
38562306a36Sopenharmony_ci	 * processes the host requests may not yet be running.
38662306a36Sopenharmony_ci	 * Defer processing channel callbacks until the daemon
38762306a36Sopenharmony_ci	 * has registered.
38862306a36Sopenharmony_ci	 */
38962306a36Sopenharmony_ci	vss_transaction.state = HVUTIL_DEVICE_INIT;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL,
39262306a36Sopenharmony_ci				    vss_on_msg, vss_on_reset);
39362306a36Sopenharmony_ci	if (!hvt) {
39462306a36Sopenharmony_ci		pr_warn("VSS: Failed to initialize transport\n");
39562306a36Sopenharmony_ci		return -EFAULT;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void hv_vss_cancel_work(void)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	cancel_delayed_work_sync(&vss_timeout_work);
40462306a36Sopenharmony_ci	cancel_work_sync(&vss_handle_request_work);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ciint hv_vss_pre_suspend(void)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct vmbus_channel *channel = vss_transaction.recv_channel;
41062306a36Sopenharmony_ci	struct hv_vss_msg *vss_msg;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/*
41362306a36Sopenharmony_ci	 * Fake a THAW message for the user space daemon in case the daemon
41462306a36Sopenharmony_ci	 * has frozen the file systems. It doesn't matter if there is already
41562306a36Sopenharmony_ci	 * a message pending to be delivered to the user space since we force
41662306a36Sopenharmony_ci	 * vss_transaction.state to be HVUTIL_READY, so the user space daemon's
41762306a36Sopenharmony_ci	 * write() will fail with EINVAL (see vss_on_msg()), and the daemon
41862306a36Sopenharmony_ci	 * will reset the device by closing and re-opening it.
41962306a36Sopenharmony_ci	 */
42062306a36Sopenharmony_ci	vss_msg = kzalloc(sizeof(*vss_msg), GFP_KERNEL);
42162306a36Sopenharmony_ci	if (!vss_msg)
42262306a36Sopenharmony_ci		return -ENOMEM;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	tasklet_disable(&channel->callback_event);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	vss_msg->vss_hdr.operation = VSS_OP_THAW;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* Cancel any possible pending work. */
42962306a36Sopenharmony_ci	hv_vss_cancel_work();
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* We don't care about the return value. */
43262306a36Sopenharmony_ci	hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	kfree(vss_msg);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	vss_transaction.state = HVUTIL_READY;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* tasklet_enable() will be called in hv_vss_pre_resume(). */
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciint hv_vss_pre_resume(void)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct vmbus_channel *channel = vss_transaction.recv_channel;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	tasklet_enable(&channel->callback_event);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return 0;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_civoid hv_vss_deinit(void)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	vss_transaction.state = HVUTIL_DEVICE_DYING;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	hv_vss_cancel_work();
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	hvutil_transport_destroy(hvt);
45862306a36Sopenharmony_ci}
459