18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * An implementation of file copy service. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014, Microsoft, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author : K. Y. Srinivasan <ksrinivasan@novell.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/nls.h> 138c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 148c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 158c2ecf20Sopenharmony_ci#include <linux/sched.h> 168c2ecf20Sopenharmony_ci#include <asm/hyperv-tlfs.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h" 198c2ecf20Sopenharmony_ci#include "hv_utils_transport.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define WIN8_SRV_MAJOR 1 228c2ecf20Sopenharmony_ci#define WIN8_SRV_MINOR 1 238c2ecf20Sopenharmony_ci#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define FCOPY_VER_COUNT 1 268c2ecf20Sopenharmony_cistatic const int fcopy_versions[] = { 278c2ecf20Sopenharmony_ci WIN8_SRV_VERSION 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define FW_VER_COUNT 1 318c2ecf20Sopenharmony_cistatic const int fw_versions[] = { 328c2ecf20Sopenharmony_ci UTIL_FW_VERSION 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Global state maintained for transaction that is being processed. 378c2ecf20Sopenharmony_ci * For a class of integration services, including the "file copy service", 388c2ecf20Sopenharmony_ci * the specified protocol is a "request/response" protocol which means that 398c2ecf20Sopenharmony_ci * there can only be single outstanding transaction from the host at any 408c2ecf20Sopenharmony_ci * given point in time. We use this to simplify memory management in this 418c2ecf20Sopenharmony_ci * driver - we cache and process only one message at a time. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * While the request/response protocol is guaranteed by the host, we further 448c2ecf20Sopenharmony_ci * ensure this by serializing packet processing in this driver - we do not 458c2ecf20Sopenharmony_ci * read additional packets from the VMBUs until the current packet is fully 468c2ecf20Sopenharmony_ci * handled. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct { 508c2ecf20Sopenharmony_ci int state; /* hvutil_device_state */ 518c2ecf20Sopenharmony_ci int recv_len; /* number of bytes received. */ 528c2ecf20Sopenharmony_ci struct hv_fcopy_hdr *fcopy_msg; /* current message */ 538c2ecf20Sopenharmony_ci struct vmbus_channel *recv_channel; /* chn we got the request */ 548c2ecf20Sopenharmony_ci u64 recv_req_id; /* request ID. */ 558c2ecf20Sopenharmony_ci} fcopy_transaction; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void fcopy_respond_to_host(int error); 588c2ecf20Sopenharmony_cistatic void fcopy_send_data(struct work_struct *dummy); 598c2ecf20Sopenharmony_cistatic void fcopy_timeout_func(struct work_struct *dummy); 608c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func); 618c2ecf20Sopenharmony_cistatic DECLARE_WORK(fcopy_send_work, fcopy_send_data); 628c2ecf20Sopenharmony_cistatic const char fcopy_devname[] = "vmbus/hv_fcopy"; 638c2ecf20Sopenharmony_cistatic u8 *recv_buffer; 648c2ecf20Sopenharmony_cistatic struct hvutil_transport *hvt; 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * This state maintains the version number registered by the daemon. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic int dm_reg_value; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void fcopy_poll_wrapper(void *channel) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci /* Transaction is finished, reset the state here to avoid races. */ 738c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_READY; 748c2ecf20Sopenharmony_ci tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void fcopy_timeout_func(struct work_struct *dummy) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * If the timer fires, the user-mode component has not responded; 818c2ecf20Sopenharmony_ci * process the pending transaction. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci fcopy_respond_to_host(HV_E_FAIL); 848c2ecf20Sopenharmony_ci hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void fcopy_register_done(void) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci pr_debug("FCP: userspace daemon registered\n"); 908c2ecf20Sopenharmony_ci hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int fcopy_handle_handshake(u32 version) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u32 our_ver = FCOPY_CURRENT_VERSION; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci switch (version) { 988c2ecf20Sopenharmony_ci case FCOPY_VERSION_0: 998c2ecf20Sopenharmony_ci /* Daemon doesn't expect us to reply */ 1008c2ecf20Sopenharmony_ci dm_reg_value = version; 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci case FCOPY_VERSION_1: 1038c2ecf20Sopenharmony_ci /* Daemon expects us to reply with our own version */ 1048c2ecf20Sopenharmony_ci if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver), 1058c2ecf20Sopenharmony_ci fcopy_register_done)) 1068c2ecf20Sopenharmony_ci return -EFAULT; 1078c2ecf20Sopenharmony_ci dm_reg_value = version; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci default: 1108c2ecf20Sopenharmony_ci /* 1118c2ecf20Sopenharmony_ci * For now we will fail the registration. 1128c2ecf20Sopenharmony_ci * If and when we have multiple versions to 1138c2ecf20Sopenharmony_ci * deal with, we will be backward compatible. 1148c2ecf20Sopenharmony_ci * We will add this code when needed. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci return -EINVAL; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci pr_debug("FCP: userspace daemon ver. %d connected\n", version); 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void fcopy_send_data(struct work_struct *dummy) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct hv_start_fcopy *smsg_out = NULL; 1258c2ecf20Sopenharmony_ci int operation = fcopy_transaction.fcopy_msg->operation; 1268c2ecf20Sopenharmony_ci struct hv_start_fcopy *smsg_in; 1278c2ecf20Sopenharmony_ci void *out_src; 1288c2ecf20Sopenharmony_ci int rc, out_len; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * The strings sent from the host are encoded in 1328c2ecf20Sopenharmony_ci * in utf16; convert it to utf8 strings. 1338c2ecf20Sopenharmony_ci * The host assures us that the utf16 strings will not exceed 1348c2ecf20Sopenharmony_ci * the max lengths specified. We will however, reserve room 1358c2ecf20Sopenharmony_ci * for the string terminating character - in the utf16s_utf8s() 1368c2ecf20Sopenharmony_ci * function we limit the size of the buffer where the converted 1378c2ecf20Sopenharmony_ci * string is placed to W_MAX_PATH -1 to guarantee 1388c2ecf20Sopenharmony_ci * that the strings can be properly terminated! 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci switch (operation) { 1428c2ecf20Sopenharmony_ci case START_FILE_COPY: 1438c2ecf20Sopenharmony_ci out_len = sizeof(struct hv_start_fcopy); 1448c2ecf20Sopenharmony_ci smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL); 1458c2ecf20Sopenharmony_ci if (!smsg_out) 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci smsg_out->hdr.operation = operation; 1498c2ecf20Sopenharmony_ci smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH, 1528c2ecf20Sopenharmony_ci UTF16_LITTLE_ENDIAN, 1538c2ecf20Sopenharmony_ci (__u8 *)&smsg_out->file_name, W_MAX_PATH - 1); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH, 1568c2ecf20Sopenharmony_ci UTF16_LITTLE_ENDIAN, 1578c2ecf20Sopenharmony_ci (__u8 *)&smsg_out->path_name, W_MAX_PATH - 1); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci smsg_out->copy_flags = smsg_in->copy_flags; 1608c2ecf20Sopenharmony_ci smsg_out->file_size = smsg_in->file_size; 1618c2ecf20Sopenharmony_ci out_src = smsg_out; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci case WRITE_TO_FILE: 1658c2ecf20Sopenharmony_ci out_src = fcopy_transaction.fcopy_msg; 1668c2ecf20Sopenharmony_ci out_len = sizeof(struct hv_do_fcopy); 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci default: 1698c2ecf20Sopenharmony_ci out_src = fcopy_transaction.fcopy_msg; 1708c2ecf20Sopenharmony_ci out_len = fcopy_transaction.recv_len; 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_USERSPACE_REQ; 1758c2ecf20Sopenharmony_ci rc = hvutil_transport_send(hvt, out_src, out_len, NULL); 1768c2ecf20Sopenharmony_ci if (rc) { 1778c2ecf20Sopenharmony_ci pr_debug("FCP: failed to communicate to the daemon: %d\n", rc); 1788c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&fcopy_timeout_work)) { 1798c2ecf20Sopenharmony_ci fcopy_respond_to_host(HV_E_FAIL); 1808c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_READY; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci kfree(smsg_out); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * Send a response back to the host. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void 1918c2ecf20Sopenharmony_cifcopy_respond_to_host(int error) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct icmsg_hdr *icmsghdr; 1948c2ecf20Sopenharmony_ci u32 buf_len; 1958c2ecf20Sopenharmony_ci struct vmbus_channel *channel; 1968c2ecf20Sopenharmony_ci u64 req_id; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Copy the global state for completing the transaction. Note that 2008c2ecf20Sopenharmony_ci * only one transaction can be active at a time. This is guaranteed 2018c2ecf20Sopenharmony_ci * by the file copy protocol implemented by the host. Furthermore, 2028c2ecf20Sopenharmony_ci * the "transaction active" state we maintain ensures that there can 2038c2ecf20Sopenharmony_ci * only be one active transaction at a time. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci buf_len = fcopy_transaction.recv_len; 2078c2ecf20Sopenharmony_ci channel = fcopy_transaction.recv_channel; 2088c2ecf20Sopenharmony_ci req_id = fcopy_transaction.recv_req_id; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci icmsghdr = (struct icmsg_hdr *) 2118c2ecf20Sopenharmony_ci &recv_buffer[sizeof(struct vmbuspipe_hdr)]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (channel->onchannel_callback == NULL) 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * We have raced with util driver being unloaded; 2168c2ecf20Sopenharmony_ci * silently return. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci icmsghdr->status = error; 2218c2ecf20Sopenharmony_ci icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 2228c2ecf20Sopenharmony_ci vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, 2238c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid hv_fcopy_onchannelcallback(void *context) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct vmbus_channel *channel = context; 2298c2ecf20Sopenharmony_ci u32 recvlen; 2308c2ecf20Sopenharmony_ci u64 requestid; 2318c2ecf20Sopenharmony_ci struct hv_fcopy_hdr *fcopy_msg; 2328c2ecf20Sopenharmony_ci struct icmsg_hdr *icmsghdr; 2338c2ecf20Sopenharmony_ci int fcopy_srv_version; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (fcopy_transaction.state > HVUTIL_READY) 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, 2398c2ecf20Sopenharmony_ci &requestid); 2408c2ecf20Sopenharmony_ci if (recvlen <= 0) 2418c2ecf20Sopenharmony_ci return; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci icmsghdr = (struct icmsg_hdr *)&recv_buffer[ 2448c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr)]; 2458c2ecf20Sopenharmony_ci if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { 2468c2ecf20Sopenharmony_ci if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, 2478c2ecf20Sopenharmony_ci fw_versions, FW_VER_COUNT, 2488c2ecf20Sopenharmony_ci fcopy_versions, FCOPY_VER_COUNT, 2498c2ecf20Sopenharmony_ci NULL, &fcopy_srv_version)) { 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_info("FCopy IC version %d.%d\n", 2528c2ecf20Sopenharmony_ci fcopy_srv_version >> 16, 2538c2ecf20Sopenharmony_ci fcopy_srv_version & 0xFFFF); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ 2578c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr) + 2588c2ecf20Sopenharmony_ci sizeof(struct icmsg_hdr)]; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Stash away this global state for completing the 2628c2ecf20Sopenharmony_ci * transaction; note transactions are serialized. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci fcopy_transaction.recv_len = recvlen; 2668c2ecf20Sopenharmony_ci fcopy_transaction.recv_req_id = requestid; 2678c2ecf20Sopenharmony_ci fcopy_transaction.fcopy_msg = fcopy_msg; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (fcopy_transaction.state < HVUTIL_READY) { 2708c2ecf20Sopenharmony_ci /* Userspace is not registered yet */ 2718c2ecf20Sopenharmony_ci fcopy_respond_to_host(HV_E_FAIL); 2728c2ecf20Sopenharmony_ci return; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_HOSTMSG_RECEIVED; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * Send the information to the user-level daemon. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci schedule_work(&fcopy_send_work); 2808c2ecf20Sopenharmony_ci schedule_delayed_work(&fcopy_timeout_work, 2818c2ecf20Sopenharmony_ci HV_UTIL_TIMEOUT * HZ); 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; 2858c2ecf20Sopenharmony_ci vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, 2868c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* Callback when data is received from userspace */ 2908c2ecf20Sopenharmony_cistatic int fcopy_on_msg(void *msg, int len) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int *val = (int *)msg; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (len != sizeof(int)) 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (fcopy_transaction.state == HVUTIL_DEVICE_INIT) 2988c2ecf20Sopenharmony_ci return fcopy_handle_handshake(*val); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ) 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* 3048c2ecf20Sopenharmony_ci * Complete the transaction by forwarding the result 3058c2ecf20Sopenharmony_ci * to the host. But first, cancel the timeout. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&fcopy_timeout_work)) { 3088c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_USERSPACE_RECV; 3098c2ecf20Sopenharmony_ci fcopy_respond_to_host(*val); 3108c2ecf20Sopenharmony_ci hv_poll_channel(fcopy_transaction.recv_channel, 3118c2ecf20Sopenharmony_ci fcopy_poll_wrapper); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void fcopy_on_reset(void) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * The daemon has exited; reset the state. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_DEVICE_INIT; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (cancel_delayed_work_sync(&fcopy_timeout_work)) 3258c2ecf20Sopenharmony_ci fcopy_respond_to_host(HV_E_FAIL); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ciint hv_fcopy_init(struct hv_util_service *srv) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci recv_buffer = srv->recv_buffer; 3318c2ecf20Sopenharmony_ci fcopy_transaction.recv_channel = srv->channel; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * When this driver loads, the user level daemon that 3358c2ecf20Sopenharmony_ci * processes the host requests may not yet be running. 3368c2ecf20Sopenharmony_ci * Defer processing channel callbacks until the daemon 3378c2ecf20Sopenharmony_ci * has registered. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_DEVICE_INIT; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci hvt = hvutil_transport_init(fcopy_devname, 0, 0, 3428c2ecf20Sopenharmony_ci fcopy_on_msg, fcopy_on_reset); 3438c2ecf20Sopenharmony_ci if (!hvt) 3448c2ecf20Sopenharmony_ci return -EFAULT; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void hv_fcopy_cancel_work(void) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&fcopy_timeout_work); 3528c2ecf20Sopenharmony_ci cancel_work_sync(&fcopy_send_work); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciint hv_fcopy_pre_suspend(void) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct vmbus_channel *channel = fcopy_transaction.recv_channel; 3588c2ecf20Sopenharmony_ci struct hv_fcopy_hdr *fcopy_msg; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * Fake a CANCEL_FCOPY message for the user space daemon in case the 3628c2ecf20Sopenharmony_ci * daemon is in the middle of copying some file. It doesn't matter if 3638c2ecf20Sopenharmony_ci * there is already a message pending to be delivered to the user 3648c2ecf20Sopenharmony_ci * space since we force fcopy_transaction.state to be HVUTIL_READY, so 3658c2ecf20Sopenharmony_ci * the user space daemon's write() will fail with EINVAL (see 3668c2ecf20Sopenharmony_ci * fcopy_on_msg()), and the daemon will reset the device by closing 3678c2ecf20Sopenharmony_ci * and re-opening it. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci fcopy_msg = kzalloc(sizeof(*fcopy_msg), GFP_KERNEL); 3708c2ecf20Sopenharmony_ci if (!fcopy_msg) 3718c2ecf20Sopenharmony_ci return -ENOMEM; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci tasklet_disable(&channel->callback_event); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci fcopy_msg->operation = CANCEL_FCOPY; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci hv_fcopy_cancel_work(); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* We don't care about the return value. */ 3808c2ecf20Sopenharmony_ci hvutil_transport_send(hvt, fcopy_msg, sizeof(*fcopy_msg), NULL); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci kfree(fcopy_msg); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_READY; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* tasklet_enable() will be called in hv_fcopy_pre_resume(). */ 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciint hv_fcopy_pre_resume(void) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct vmbus_channel *channel = fcopy_transaction.recv_channel; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci tasklet_enable(&channel->callback_event); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_civoid hv_fcopy_deinit(void) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci fcopy_transaction.state = HVUTIL_DEVICE_DYING; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci hv_fcopy_cancel_work(); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci hvutil_transport_destroy(hvt); 4068c2ecf20Sopenharmony_ci} 407