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