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