xref: /openbmc/linux/drivers/hv/hv_fcopy.c (revision c7e490fc)
101325476SK. Y. Srinivasan /*
201325476SK. Y. Srinivasan  * An implementation of file copy service.
301325476SK. Y. Srinivasan  *
401325476SK. Y. Srinivasan  * Copyright (C) 2014, Microsoft, Inc.
501325476SK. Y. Srinivasan  *
601325476SK. Y. Srinivasan  * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
701325476SK. Y. Srinivasan  *
801325476SK. Y. Srinivasan  * This program is free software; you can redistribute it and/or modify it
901325476SK. Y. Srinivasan  * under the terms of the GNU General Public License version 2 as published
1001325476SK. Y. Srinivasan  * by the Free Software Foundation.
1101325476SK. Y. Srinivasan  *
1201325476SK. Y. Srinivasan  * This program is distributed in the hope that it will be useful, but
1301325476SK. Y. Srinivasan  * WITHOUT ANY WARRANTY; without even the implied warranty of
1401325476SK. Y. Srinivasan  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
1501325476SK. Y. Srinivasan  * NON INFRINGEMENT.  See the GNU General Public License for more
1601325476SK. Y. Srinivasan  * details.
1701325476SK. Y. Srinivasan  *
1801325476SK. Y. Srinivasan  */
1901325476SK. Y. Srinivasan 
2001325476SK. Y. Srinivasan #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2101325476SK. Y. Srinivasan 
2201325476SK. Y. Srinivasan #include <linux/nls.h>
2301325476SK. Y. Srinivasan #include <linux/workqueue.h>
2401325476SK. Y. Srinivasan #include <linux/hyperv.h>
2501325476SK. Y. Srinivasan #include <linux/sched.h>
2601325476SK. Y. Srinivasan 
2701325476SK. Y. Srinivasan #include "hyperv_vmbus.h"
28c7e490fcSVitaly Kuznetsov #include "hv_utils_transport.h"
2901325476SK. Y. Srinivasan 
3001325476SK. Y. Srinivasan #define WIN8_SRV_MAJOR		1
3101325476SK. Y. Srinivasan #define WIN8_SRV_MINOR		1
3201325476SK. Y. Srinivasan #define WIN8_SRV_VERSION	(WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
3301325476SK. Y. Srinivasan 
3401325476SK. Y. Srinivasan /*
3501325476SK. Y. Srinivasan  * Global state maintained for transaction that is being processed.
3601325476SK. Y. Srinivasan  * For a class of integration services, including the "file copy service",
3701325476SK. Y. Srinivasan  * the specified protocol is a "request/response" protocol which means that
3801325476SK. Y. Srinivasan  * there can only be single outstanding transaction from the host at any
3901325476SK. Y. Srinivasan  * given point in time. We use this to simplify memory management in this
4001325476SK. Y. Srinivasan  * driver - we cache and process only one message at a time.
4101325476SK. Y. Srinivasan  *
4201325476SK. Y. Srinivasan  * While the request/response protocol is guaranteed by the host, we further
4301325476SK. Y. Srinivasan  * ensure this by serializing packet processing in this driver - we do not
4401325476SK. Y. Srinivasan  * read additional packets from the VMBUs until the current packet is fully
4501325476SK. Y. Srinivasan  * handled.
4601325476SK. Y. Srinivasan  */
4701325476SK. Y. Srinivasan 
4801325476SK. Y. Srinivasan static struct {
494c93ccccSVitaly Kuznetsov 	int state;   /* hvutil_device_state */
5001325476SK. Y. Srinivasan 	int recv_len; /* number of bytes received. */
5101325476SK. Y. Srinivasan 	struct hv_fcopy_hdr  *fcopy_msg; /* current message */
5201325476SK. Y. Srinivasan 	struct vmbus_channel *recv_channel; /* chn we got the request */
5301325476SK. Y. Srinivasan 	u64 recv_req_id; /* request ID. */
5401325476SK. Y. Srinivasan 	void *fcopy_context; /* for the channel callback */
5501325476SK. Y. Srinivasan } fcopy_transaction;
5601325476SK. Y. Srinivasan 
5701325476SK. Y. Srinivasan static void fcopy_respond_to_host(int error);
58c7e490fcSVitaly Kuznetsov static void fcopy_send_data(struct work_struct *dummy);
591d072339SVitaly Kuznetsov static void fcopy_timeout_func(struct work_struct *dummy);
601d072339SVitaly Kuznetsov static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func);
61c7e490fcSVitaly Kuznetsov static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
62c7e490fcSVitaly Kuznetsov static const char fcopy_devname[] = "vmbus/hv_fcopy";
6301325476SK. Y. Srinivasan static u8 *recv_buffer;
64c7e490fcSVitaly Kuznetsov static struct hvutil_transport *hvt;
6501325476SK. Y. Srinivasan 
661d072339SVitaly Kuznetsov static void fcopy_timeout_func(struct work_struct *dummy)
6701325476SK. Y. Srinivasan {
6801325476SK. Y. Srinivasan 	/*
6901325476SK. Y. Srinivasan 	 * If the timer fires, the user-mode component has not responded;
7001325476SK. Y. Srinivasan 	 * process the pending transaction.
7101325476SK. Y. Srinivasan 	 */
7201325476SK. Y. Srinivasan 	fcopy_respond_to_host(HV_E_FAIL);
73d9b16529SDexuan Cui 
744c93ccccSVitaly Kuznetsov 	/* Transaction is finished, reset the state. */
754c93ccccSVitaly Kuznetsov 	if (fcopy_transaction.state > HVUTIL_READY)
764c93ccccSVitaly Kuznetsov 		fcopy_transaction.state = HVUTIL_READY;
774c93ccccSVitaly Kuznetsov 
78242f3122SVitaly Kuznetsov 	hv_poll_channel(fcopy_transaction.fcopy_context,
79242f3122SVitaly Kuznetsov 			hv_fcopy_onchannelcallback);
8001325476SK. Y. Srinivasan }
8101325476SK. Y. Srinivasan 
8201325476SK. Y. Srinivasan static int fcopy_handle_handshake(u32 version)
8301325476SK. Y. Srinivasan {
8401325476SK. Y. Srinivasan 	switch (version) {
8501325476SK. Y. Srinivasan 	case FCOPY_CURRENT_VERSION:
8601325476SK. Y. Srinivasan 		break;
8701325476SK. Y. Srinivasan 	default:
8801325476SK. Y. Srinivasan 		/*
8901325476SK. Y. Srinivasan 		 * For now we will fail the registration.
9001325476SK. Y. Srinivasan 		 * If and when we have multiple versions to
9101325476SK. Y. Srinivasan 		 * deal with, we will be backward compatible.
9201325476SK. Y. Srinivasan 		 * We will add this code when needed.
9301325476SK. Y. Srinivasan 		 */
9401325476SK. Y. Srinivasan 		return -EINVAL;
9501325476SK. Y. Srinivasan 	}
9601325476SK. Y. Srinivasan 	pr_info("FCP: user-mode registering done. Daemon version: %d\n",
9701325476SK. Y. Srinivasan 		version);
984c93ccccSVitaly Kuznetsov 	fcopy_transaction.state = HVUTIL_READY;
99242f3122SVitaly Kuznetsov 	hv_poll_channel(fcopy_transaction.fcopy_context,
100242f3122SVitaly Kuznetsov 			hv_fcopy_onchannelcallback);
10101325476SK. Y. Srinivasan 	return 0;
10201325476SK. Y. Srinivasan }
10301325476SK. Y. Srinivasan 
104c7e490fcSVitaly Kuznetsov static void fcopy_send_data(struct work_struct *dummy)
10501325476SK. Y. Srinivasan {
106c7e490fcSVitaly Kuznetsov 	struct hv_start_fcopy smsg_out;
10701325476SK. Y. Srinivasan 	int operation = fcopy_transaction.fcopy_msg->operation;
10801325476SK. Y. Srinivasan 	struct hv_start_fcopy *smsg_in;
109c7e490fcSVitaly Kuznetsov 	void *out_src;
110c7e490fcSVitaly Kuznetsov 	int rc, out_len;
11101325476SK. Y. Srinivasan 
11201325476SK. Y. Srinivasan 	/*
11301325476SK. Y. Srinivasan 	 * The  strings sent from the host are encoded in
11401325476SK. Y. Srinivasan 	 * in utf16; convert it to utf8 strings.
11501325476SK. Y. Srinivasan 	 * The host assures us that the utf16 strings will not exceed
11601325476SK. Y. Srinivasan 	 * the max lengths specified. We will however, reserve room
11701325476SK. Y. Srinivasan 	 * for the string terminating character - in the utf16s_utf8s()
11801325476SK. Y. Srinivasan 	 * function we limit the size of the buffer where the converted
11901325476SK. Y. Srinivasan 	 * string is placed to W_MAX_PATH -1 to guarantee
12001325476SK. Y. Srinivasan 	 * that the strings can be properly terminated!
12101325476SK. Y. Srinivasan 	 */
12201325476SK. Y. Srinivasan 
12301325476SK. Y. Srinivasan 	switch (operation) {
12401325476SK. Y. Srinivasan 	case START_FILE_COPY:
125c7e490fcSVitaly Kuznetsov 		out_len = sizeof(struct hv_start_fcopy);
126c7e490fcSVitaly Kuznetsov 		memset(&smsg_out, 0, out_len);
127c7e490fcSVitaly Kuznetsov 		smsg_out.hdr.operation = operation;
12801325476SK. Y. Srinivasan 		smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg;
12901325476SK. Y. Srinivasan 
13001325476SK. Y. Srinivasan 		utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
13101325476SK. Y. Srinivasan 				UTF16_LITTLE_ENDIAN,
132c7e490fcSVitaly Kuznetsov 				(__u8 *)&smsg_out.file_name, W_MAX_PATH - 1);
13301325476SK. Y. Srinivasan 
13401325476SK. Y. Srinivasan 		utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
13501325476SK. Y. Srinivasan 				UTF16_LITTLE_ENDIAN,
136c7e490fcSVitaly Kuznetsov 				(__u8 *)&smsg_out.path_name, W_MAX_PATH - 1);
13701325476SK. Y. Srinivasan 
138c7e490fcSVitaly Kuznetsov 		smsg_out.copy_flags = smsg_in->copy_flags;
139c7e490fcSVitaly Kuznetsov 		smsg_out.file_size = smsg_in->file_size;
140c7e490fcSVitaly Kuznetsov 		out_src = &smsg_out;
14101325476SK. Y. Srinivasan 		break;
14201325476SK. Y. Srinivasan 
14301325476SK. Y. Srinivasan 	default:
144c7e490fcSVitaly Kuznetsov 		out_src = fcopy_transaction.fcopy_msg;
145c7e490fcSVitaly Kuznetsov 		out_len = fcopy_transaction.recv_len;
14601325476SK. Y. Srinivasan 		break;
14701325476SK. Y. Srinivasan 	}
148c7e490fcSVitaly Kuznetsov 
149c7e490fcSVitaly Kuznetsov 	fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
150c7e490fcSVitaly Kuznetsov 	rc = hvutil_transport_send(hvt, out_src, out_len);
151c7e490fcSVitaly Kuznetsov 	if (rc) {
152c7e490fcSVitaly Kuznetsov 		pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
153c7e490fcSVitaly Kuznetsov 		if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
154c7e490fcSVitaly Kuznetsov 			fcopy_respond_to_host(HV_E_FAIL);
155c7e490fcSVitaly Kuznetsov 			fcopy_transaction.state = HVUTIL_READY;
156c7e490fcSVitaly Kuznetsov 		}
157c7e490fcSVitaly Kuznetsov 	}
15801325476SK. Y. Srinivasan 	return;
15901325476SK. Y. Srinivasan }
16001325476SK. Y. Srinivasan 
16101325476SK. Y. Srinivasan /*
16201325476SK. Y. Srinivasan  * Send a response back to the host.
16301325476SK. Y. Srinivasan  */
16401325476SK. Y. Srinivasan 
16501325476SK. Y. Srinivasan static void
16601325476SK. Y. Srinivasan fcopy_respond_to_host(int error)
16701325476SK. Y. Srinivasan {
16801325476SK. Y. Srinivasan 	struct icmsg_hdr *icmsghdr;
16901325476SK. Y. Srinivasan 	u32 buf_len;
17001325476SK. Y. Srinivasan 	struct vmbus_channel *channel;
17101325476SK. Y. Srinivasan 	u64 req_id;
17201325476SK. Y. Srinivasan 
17301325476SK. Y. Srinivasan 	/*
17401325476SK. Y. Srinivasan 	 * Copy the global state for completing the transaction. Note that
17501325476SK. Y. Srinivasan 	 * only one transaction can be active at a time. This is guaranteed
17601325476SK. Y. Srinivasan 	 * by the file copy protocol implemented by the host. Furthermore,
17701325476SK. Y. Srinivasan 	 * the "transaction active" state we maintain ensures that there can
17801325476SK. Y. Srinivasan 	 * only be one active transaction at a time.
17901325476SK. Y. Srinivasan 	 */
18001325476SK. Y. Srinivasan 
18101325476SK. Y. Srinivasan 	buf_len = fcopy_transaction.recv_len;
18201325476SK. Y. Srinivasan 	channel = fcopy_transaction.recv_channel;
18301325476SK. Y. Srinivasan 	req_id = fcopy_transaction.recv_req_id;
18401325476SK. Y. Srinivasan 
18501325476SK. Y. Srinivasan 	icmsghdr = (struct icmsg_hdr *)
18601325476SK. Y. Srinivasan 			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
18701325476SK. Y. Srinivasan 
18801325476SK. Y. Srinivasan 	if (channel->onchannel_callback == NULL)
18901325476SK. Y. Srinivasan 		/*
19001325476SK. Y. Srinivasan 		 * We have raced with util driver being unloaded;
19101325476SK. Y. Srinivasan 		 * silently return.
19201325476SK. Y. Srinivasan 		 */
19301325476SK. Y. Srinivasan 		return;
19401325476SK. Y. Srinivasan 
19501325476SK. Y. Srinivasan 	icmsghdr->status = error;
19601325476SK. Y. Srinivasan 	icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
19701325476SK. Y. Srinivasan 	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
19801325476SK. Y. Srinivasan 				VM_PKT_DATA_INBAND, 0);
19901325476SK. Y. Srinivasan }
20001325476SK. Y. Srinivasan 
20101325476SK. Y. Srinivasan void hv_fcopy_onchannelcallback(void *context)
20201325476SK. Y. Srinivasan {
20301325476SK. Y. Srinivasan 	struct vmbus_channel *channel = context;
20401325476SK. Y. Srinivasan 	u32 recvlen;
20501325476SK. Y. Srinivasan 	u64 requestid;
20601325476SK. Y. Srinivasan 	struct hv_fcopy_hdr *fcopy_msg;
20701325476SK. Y. Srinivasan 	struct icmsg_hdr *icmsghdr;
20801325476SK. Y. Srinivasan 	struct icmsg_negotiate *negop = NULL;
20901325476SK. Y. Srinivasan 	int util_fw_version;
21001325476SK. Y. Srinivasan 	int fcopy_srv_version;
21101325476SK. Y. Srinivasan 
2124c93ccccSVitaly Kuznetsov 	if (fcopy_transaction.state > HVUTIL_READY) {
21301325476SK. Y. Srinivasan 		/*
21401325476SK. Y. Srinivasan 		 * We will defer processing this callback once
21501325476SK. Y. Srinivasan 		 * the current transaction is complete.
21601325476SK. Y. Srinivasan 		 */
21701325476SK. Y. Srinivasan 		fcopy_transaction.fcopy_context = context;
21801325476SK. Y. Srinivasan 		return;
21901325476SK. Y. Srinivasan 	}
220242f3122SVitaly Kuznetsov 	fcopy_transaction.fcopy_context = NULL;
22101325476SK. Y. Srinivasan 
22201325476SK. Y. Srinivasan 	vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
22301325476SK. Y. Srinivasan 			 &requestid);
22401325476SK. Y. Srinivasan 	if (recvlen <= 0)
22501325476SK. Y. Srinivasan 		return;
22601325476SK. Y. Srinivasan 
22701325476SK. Y. Srinivasan 	icmsghdr = (struct icmsg_hdr *)&recv_buffer[
22801325476SK. Y. Srinivasan 			sizeof(struct vmbuspipe_hdr)];
22901325476SK. Y. Srinivasan 	if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
23001325476SK. Y. Srinivasan 		util_fw_version = UTIL_FW_VERSION;
23101325476SK. Y. Srinivasan 		fcopy_srv_version = WIN8_SRV_VERSION;
23201325476SK. Y. Srinivasan 		vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer,
23301325476SK. Y. Srinivasan 				util_fw_version, fcopy_srv_version);
23401325476SK. Y. Srinivasan 	} else {
23501325476SK. Y. Srinivasan 		fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[
23601325476SK. Y. Srinivasan 				sizeof(struct vmbuspipe_hdr) +
23701325476SK. Y. Srinivasan 				sizeof(struct icmsg_hdr)];
23801325476SK. Y. Srinivasan 
23901325476SK. Y. Srinivasan 		/*
24001325476SK. Y. Srinivasan 		 * Stash away this global state for completing the
24101325476SK. Y. Srinivasan 		 * transaction; note transactions are serialized.
24201325476SK. Y. Srinivasan 		 */
24301325476SK. Y. Srinivasan 
24401325476SK. Y. Srinivasan 		fcopy_transaction.recv_len = recvlen;
24501325476SK. Y. Srinivasan 		fcopy_transaction.recv_channel = channel;
24601325476SK. Y. Srinivasan 		fcopy_transaction.recv_req_id = requestid;
24701325476SK. Y. Srinivasan 		fcopy_transaction.fcopy_msg = fcopy_msg;
24801325476SK. Y. Srinivasan 
2494c93ccccSVitaly Kuznetsov 		if (fcopy_transaction.state < HVUTIL_READY) {
2504c93ccccSVitaly Kuznetsov 			/* Userspace is not registered yet */
2514c93ccccSVitaly Kuznetsov 			fcopy_respond_to_host(HV_E_FAIL);
2524c93ccccSVitaly Kuznetsov 			return;
2534c93ccccSVitaly Kuznetsov 		}
2544c93ccccSVitaly Kuznetsov 		fcopy_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
2554c93ccccSVitaly Kuznetsov 
25601325476SK. Y. Srinivasan 		/*
25701325476SK. Y. Srinivasan 		 * Send the information to the user-level daemon.
25801325476SK. Y. Srinivasan 		 */
259c7e490fcSVitaly Kuznetsov 		schedule_work(&fcopy_send_work);
2601d072339SVitaly Kuznetsov 		schedule_delayed_work(&fcopy_timeout_work, 5*HZ);
26101325476SK. Y. Srinivasan 		return;
26201325476SK. Y. Srinivasan 	}
26301325476SK. Y. Srinivasan 	icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
26401325476SK. Y. Srinivasan 	vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
26501325476SK. Y. Srinivasan 			VM_PKT_DATA_INBAND, 0);
26601325476SK. Y. Srinivasan }
26701325476SK. Y. Srinivasan 
268c7e490fcSVitaly Kuznetsov /* Callback when data is received from userspace */
269c7e490fcSVitaly Kuznetsov static int fcopy_on_msg(void *msg, int len)
27001325476SK. Y. Srinivasan {
271c7e490fcSVitaly Kuznetsov 	int *val = (int *)msg;
27201325476SK. Y. Srinivasan 
273c7e490fcSVitaly Kuznetsov 	if (len != sizeof(int))
27401325476SK. Y. Srinivasan 		return -EINVAL;
27501325476SK. Y. Srinivasan 
276c7e490fcSVitaly Kuznetsov 	if (fcopy_transaction.state == HVUTIL_DEVICE_INIT)
277c7e490fcSVitaly Kuznetsov 		return fcopy_handle_handshake(*val);
27801325476SK. Y. Srinivasan 
2794c93ccccSVitaly Kuznetsov 	if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ)
2804c93ccccSVitaly Kuznetsov 		return -EINVAL;
2814c93ccccSVitaly Kuznetsov 
28201325476SK. Y. Srinivasan 	/*
28301325476SK. Y. Srinivasan 	 * Complete the transaction by forwarding the result
28401325476SK. Y. Srinivasan 	 * to the host. But first, cancel the timeout.
28501325476SK. Y. Srinivasan 	 */
2864c93ccccSVitaly Kuznetsov 	if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
2874c93ccccSVitaly Kuznetsov 		fcopy_transaction.state = HVUTIL_USERSPACE_RECV;
288c7e490fcSVitaly Kuznetsov 		fcopy_respond_to_host(*val);
2894c93ccccSVitaly Kuznetsov 		fcopy_transaction.state = HVUTIL_READY;
290242f3122SVitaly Kuznetsov 		hv_poll_channel(fcopy_transaction.fcopy_context,
291242f3122SVitaly Kuznetsov 				hv_fcopy_onchannelcallback);
292242f3122SVitaly Kuznetsov 	}
29301325476SK. Y. Srinivasan 
29401325476SK. Y. Srinivasan 	return 0;
29501325476SK. Y. Srinivasan }
29601325476SK. Y. Srinivasan 
297c7e490fcSVitaly Kuznetsov static void fcopy_on_reset(void)
29801325476SK. Y. Srinivasan {
29901325476SK. Y. Srinivasan 	/*
30001325476SK. Y. Srinivasan 	 * The daemon has exited; reset the state.
30101325476SK. Y. Srinivasan 	 */
3024c93ccccSVitaly Kuznetsov 	fcopy_transaction.state = HVUTIL_DEVICE_INIT;
303d9b16529SDexuan Cui 
304c7e490fcSVitaly Kuznetsov 	if (cancel_delayed_work_sync(&fcopy_timeout_work))
305d9b16529SDexuan Cui 		fcopy_respond_to_host(HV_E_FAIL);
306d9b16529SDexuan Cui }
30701325476SK. Y. Srinivasan 
30801325476SK. Y. Srinivasan int hv_fcopy_init(struct hv_util_service *srv)
30901325476SK. Y. Srinivasan {
31001325476SK. Y. Srinivasan 	recv_buffer = srv->recv_buffer;
31101325476SK. Y. Srinivasan 
31201325476SK. Y. Srinivasan 	/*
31301325476SK. Y. Srinivasan 	 * When this driver loads, the user level daemon that
31401325476SK. Y. Srinivasan 	 * processes the host requests may not yet be running.
31501325476SK. Y. Srinivasan 	 * Defer processing channel callbacks until the daemon
31601325476SK. Y. Srinivasan 	 * has registered.
31701325476SK. Y. Srinivasan 	 */
3184c93ccccSVitaly Kuznetsov 	fcopy_transaction.state = HVUTIL_DEVICE_INIT;
31901325476SK. Y. Srinivasan 
320c7e490fcSVitaly Kuznetsov 	hvt = hvutil_transport_init(fcopy_devname, 0, 0,
321c7e490fcSVitaly Kuznetsov 				    fcopy_on_msg, fcopy_on_reset);
322c7e490fcSVitaly Kuznetsov 	if (!hvt)
323c7e490fcSVitaly Kuznetsov 		return -EFAULT;
324c7e490fcSVitaly Kuznetsov 
325c7e490fcSVitaly Kuznetsov 	return 0;
32601325476SK. Y. Srinivasan }
32701325476SK. Y. Srinivasan 
32801325476SK. Y. Srinivasan void hv_fcopy_deinit(void)
32901325476SK. Y. Srinivasan {
3304c93ccccSVitaly Kuznetsov 	fcopy_transaction.state = HVUTIL_DEVICE_DYING;
3311d072339SVitaly Kuznetsov 	cancel_delayed_work_sync(&fcopy_timeout_work);
332c7e490fcSVitaly Kuznetsov 	hvutil_transport_destroy(hvt);
33301325476SK. Y. Srinivasan }
334