Blame view

drivers/hv/hv_fcopy.c 10.8 KB
43aa31327   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
2
3
4
5
6
7
  /*
   * An implementation of file copy service.
   *
   * Copyright (C) 2014, Microsoft, Inc.
   *
   * Author : K. Y. Srinivasan <ksrinivasan@novell.com>
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
8
9
10
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
11
12
  #include <linux/nls.h>
  #include <linux/workqueue.h>
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
13
14
  #include <linux/hyperv.h>
  #include <linux/sched.h>
b14d749ac   Himadri Pandya   Drivers: hv: Spec...
15
  #include <asm/hyperv-tlfs.h>
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
16
17
  
  #include "hyperv_vmbus.h"
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
18
  #include "hv_utils_transport.h"
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
19
20
21
22
  
  #define WIN8_SRV_MAJOR		1
  #define WIN8_SRV_MINOR		1
  #define WIN8_SRV_VERSION	(WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
a16564541   Alex Ng   Drivers: hv: vmbu...
23
24
25
26
27
28
29
30
31
  #define FCOPY_VER_COUNT 1
  static const int fcopy_versions[] = {
  	WIN8_SRV_VERSION
  };
  
  #define FW_VER_COUNT 1
  static const int fw_versions[] = {
  	UTIL_FW_VERSION
  };
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
32
33
34
35
36
37
38
39
40
41
42
43
  /*
   * Global state maintained for transaction that is being processed.
   * For a class of integration services, including the "file copy service",
   * the specified protocol is a "request/response" protocol which means that
   * there can only be single outstanding transaction from the host at any
   * given point in time. We use this to simplify memory management in this
   * driver - we cache and process only one message at a time.
   *
   * While the request/response protocol is guaranteed by the host, we further
   * ensure this by serializing packet processing in this driver - we do not
   * read additional packets from the VMBUs until the current packet is fully
   * handled.
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
44
45
46
   */
  
  static struct {
4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
47
  	int state;   /* hvutil_device_state */
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
48
49
  	int recv_len; /* number of bytes received. */
  	struct hv_fcopy_hdr  *fcopy_msg; /* current message */
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
50
51
  	struct vmbus_channel *recv_channel; /* chn we got the request */
  	u64 recv_req_id; /* request ID. */
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
52
  } fcopy_transaction;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
53
  static void fcopy_respond_to_host(int error);
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
54
  static void fcopy_send_data(struct work_struct *dummy);
1d072339f   Vitaly Kuznetsov   Drivers: hv: fcop...
55
56
  static void fcopy_timeout_func(struct work_struct *dummy);
  static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func);
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
57
58
  static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
  static const char fcopy_devname[] = "vmbus/hv_fcopy";
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
59
  static u8 *recv_buffer;
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
60
  static struct hvutil_transport *hvt;
a4d1ee5b0   Vitaly Kuznetsov   Drivers: hv: fcop...
61
62
63
64
  /*
   * This state maintains the version number registered by the daemon.
   */
  static int dm_reg_value;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
65

3cace4a61   Olaf Hering   Drivers: hv: util...
66
67
68
69
  static void fcopy_poll_wrapper(void *channel)
  {
  	/* Transaction is finished, reset the state here to avoid races. */
  	fcopy_transaction.state = HVUTIL_READY;
238d2ed8f   Andrea Parri (Microsoft)   hv_utils: Always ...
70
  	tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
3cace4a61   Olaf Hering   Drivers: hv: util...
71
  }
1d072339f   Vitaly Kuznetsov   Drivers: hv: fcop...
72
  static void fcopy_timeout_func(struct work_struct *dummy)
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
73
74
75
76
77
78
  {
  	/*
  	 * If the timer fires, the user-mode component has not responded;
  	 * process the pending transaction.
  	 */
  	fcopy_respond_to_host(HV_E_FAIL);
3cace4a61   Olaf Hering   Drivers: hv: util...
79
  	hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
80
  }
e0fa3e5e7   Vitaly Kuznetsov   Drivers: hv: util...
81
82
83
84
85
86
  static void fcopy_register_done(void)
  {
  	pr_debug("FCP: userspace daemon registered
  ");
  	hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
  }
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
87
88
  static int fcopy_handle_handshake(u32 version)
  {
a4d1ee5b0   Vitaly Kuznetsov   Drivers: hv: fcop...
89
  	u32 our_ver = FCOPY_CURRENT_VERSION;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
90
  	switch (version) {
a4d1ee5b0   Vitaly Kuznetsov   Drivers: hv: fcop...
91
92
93
94
95
96
  	case FCOPY_VERSION_0:
  		/* Daemon doesn't expect us to reply */
  		dm_reg_value = version;
  		break;
  	case FCOPY_VERSION_1:
  		/* Daemon expects us to reply with our own version */
e0fa3e5e7   Vitaly Kuznetsov   Drivers: hv: util...
97
98
  		if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
  		    fcopy_register_done))
a4d1ee5b0   Vitaly Kuznetsov   Drivers: hv: fcop...
99
100
  			return -EFAULT;
  		dm_reg_value = version;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
101
102
103
104
105
106
107
108
109
110
  		break;
  	default:
  		/*
  		 * For now we will fail the registration.
  		 * If and when we have multiple versions to
  		 * deal with, we will be backward compatible.
  		 * We will add this code when needed.
  		 */
  		return -EINVAL;
  	}
e0fa3e5e7   Vitaly Kuznetsov   Drivers: hv: util...
111
112
  	pr_debug("FCP: userspace daemon ver. %d connected
  ", version);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
113
114
  	return 0;
  }
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
115
  static void fcopy_send_data(struct work_struct *dummy)
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
116
  {
25ef06fe2   Vitaly Kuznetsov   Drivers: hv: fcop...
117
  	struct hv_start_fcopy *smsg_out = NULL;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
118
119
  	int operation = fcopy_transaction.fcopy_msg->operation;
  	struct hv_start_fcopy *smsg_in;
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
120
121
  	void *out_src;
  	int rc, out_len;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  
  	/*
  	 * The  strings sent from the host are encoded in
  	 * in utf16; convert it to utf8 strings.
  	 * The host assures us that the utf16 strings will not exceed
  	 * the max lengths specified. We will however, reserve room
  	 * for the string terminating character - in the utf16s_utf8s()
  	 * function we limit the size of the buffer where the converted
  	 * string is placed to W_MAX_PATH -1 to guarantee
  	 * that the strings can be properly terminated!
  	 */
  
  	switch (operation) {
  	case START_FILE_COPY:
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
136
  		out_len = sizeof(struct hv_start_fcopy);
25ef06fe2   Vitaly Kuznetsov   Drivers: hv: fcop...
137
138
139
140
141
  		smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL);
  		if (!smsg_out)
  			return;
  
  		smsg_out->hdr.operation = operation;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
142
143
144
145
  		smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg;
  
  		utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
  				UTF16_LITTLE_ENDIAN,
25ef06fe2   Vitaly Kuznetsov   Drivers: hv: fcop...
146
  				(__u8 *)&smsg_out->file_name, W_MAX_PATH - 1);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
147
148
149
  
  		utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
  				UTF16_LITTLE_ENDIAN,
25ef06fe2   Vitaly Kuznetsov   Drivers: hv: fcop...
150
  				(__u8 *)&smsg_out->path_name, W_MAX_PATH - 1);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
151

25ef06fe2   Vitaly Kuznetsov   Drivers: hv: fcop...
152
153
154
  		smsg_out->copy_flags = smsg_in->copy_flags;
  		smsg_out->file_size = smsg_in->file_size;
  		out_src = smsg_out;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
155
  		break;
549e658a0   Olaf Hering   Drivers: hv: fcop...
156
157
158
159
  	case WRITE_TO_FILE:
  		out_src = fcopy_transaction.fcopy_msg;
  		out_len = sizeof(struct hv_do_fcopy);
  		break;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
160
  	default:
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
161
162
  		out_src = fcopy_transaction.fcopy_msg;
  		out_len = fcopy_transaction.recv_len;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
163
164
  		break;
  	}
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
165
166
  
  	fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
e0fa3e5e7   Vitaly Kuznetsov   Drivers: hv: util...
167
  	rc = hvutil_transport_send(hvt, out_src, out_len, NULL);
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
168
169
170
171
172
173
174
175
  	if (rc) {
  		pr_debug("FCP: failed to communicate to the daemon: %d
  ", rc);
  		if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
  			fcopy_respond_to_host(HV_E_FAIL);
  			fcopy_transaction.state = HVUTIL_READY;
  		}
  	}
25ef06fe2   Vitaly Kuznetsov   Drivers: hv: fcop...
176
  	kfree(smsg_out);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
  }
  
  /*
   * Send a response back to the host.
   */
  
  static void
  fcopy_respond_to_host(int error)
  {
  	struct icmsg_hdr *icmsghdr;
  	u32 buf_len;
  	struct vmbus_channel *channel;
  	u64 req_id;
  
  	/*
  	 * Copy the global state for completing the transaction. Note that
  	 * only one transaction can be active at a time. This is guaranteed
  	 * by the file copy protocol implemented by the host. Furthermore,
  	 * the "transaction active" state we maintain ensures that there can
  	 * only be one active transaction at a time.
  	 */
  
  	buf_len = fcopy_transaction.recv_len;
  	channel = fcopy_transaction.recv_channel;
  	req_id = fcopy_transaction.recv_req_id;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  	icmsghdr = (struct icmsg_hdr *)
  			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
  
  	if (channel->onchannel_callback == NULL)
  		/*
  		 * We have raced with util driver being unloaded;
  		 * silently return.
  		 */
  		return;
  
  	icmsghdr->status = error;
  	icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
  	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
  				VM_PKT_DATA_INBAND, 0);
  }
  
  void hv_fcopy_onchannelcallback(void *context)
  {
  	struct vmbus_channel *channel = context;
  	u32 recvlen;
  	u64 requestid;
  	struct hv_fcopy_hdr *fcopy_msg;
  	struct icmsg_hdr *icmsghdr;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
225
  	int fcopy_srv_version;
3cace4a61   Olaf Hering   Drivers: hv: util...
226
  	if (fcopy_transaction.state > HVUTIL_READY)
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
227
  		return;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
228

b14d749ac   Himadri Pandya   Drivers: hv: Spec...
229
  	vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen,
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
230
231
232
233
234
235
236
  			 &requestid);
  	if (recvlen <= 0)
  		return;
  
  	icmsghdr = (struct icmsg_hdr *)&recv_buffer[
  			sizeof(struct vmbuspipe_hdr)];
  	if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
1274a690f   Alex Ng   Drivers: hv: Log ...
237
  		if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer,
a16564541   Alex Ng   Drivers: hv: vmbu...
238
239
  				fw_versions, FW_VER_COUNT,
  				fcopy_versions, FCOPY_VER_COUNT,
1274a690f   Alex Ng   Drivers: hv: Log ...
240
241
242
243
244
245
246
  				NULL, &fcopy_srv_version)) {
  
  			pr_info("FCopy IC version %d.%d
  ",
  				fcopy_srv_version >> 16,
  				fcopy_srv_version & 0xFFFF);
  		}
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
247
248
249
250
251
252
253
254
255
  	} else {
  		fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[
  				sizeof(struct vmbuspipe_hdr) +
  				sizeof(struct icmsg_hdr)];
  
  		/*
  		 * Stash away this global state for completing the
  		 * transaction; note transactions are serialized.
  		 */
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
256
  		fcopy_transaction.recv_len = recvlen;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
257
258
  		fcopy_transaction.recv_req_id = requestid;
  		fcopy_transaction.fcopy_msg = fcopy_msg;
4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
259
260
261
262
263
264
  		if (fcopy_transaction.state < HVUTIL_READY) {
  			/* Userspace is not registered yet */
  			fcopy_respond_to_host(HV_E_FAIL);
  			return;
  		}
  		fcopy_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
265
266
267
  		/*
  		 * Send the information to the user-level daemon.
  		 */
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
268
  		schedule_work(&fcopy_send_work);
c0b200cfb   K. Y. Srinivasan   Drivers: hv: util...
269
270
  		schedule_delayed_work(&fcopy_timeout_work,
  				      HV_UTIL_TIMEOUT * HZ);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
271
272
273
274
275
276
  		return;
  	}
  	icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
  	vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
  			VM_PKT_DATA_INBAND, 0);
  }
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
277
278
  /* Callback when data is received from userspace */
  static int fcopy_on_msg(void *msg, int len)
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
279
  {
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
280
  	int *val = (int *)msg;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
281

c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
282
  	if (len != sizeof(int))
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
283
  		return -EINVAL;
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
284
285
  	if (fcopy_transaction.state == HVUTIL_DEVICE_INIT)
  		return fcopy_handle_handshake(*val);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
286

4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
287
288
  	if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ)
  		return -EINVAL;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
289
290
291
292
  	/*
  	 * Complete the transaction by forwarding the result
  	 * to the host. But first, cancel the timeout.
  	 */
4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
293
294
  	if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
  		fcopy_transaction.state = HVUTIL_USERSPACE_RECV;
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
295
  		fcopy_respond_to_host(*val);
3cace4a61   Olaf Hering   Drivers: hv: util...
296
297
  		hv_poll_channel(fcopy_transaction.recv_channel,
  				fcopy_poll_wrapper);
242f31221   Vitaly Kuznetsov   Drivers: hv: fcop...
298
  	}
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
299

01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
300
301
  	return 0;
  }
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
302
  static void fcopy_on_reset(void)
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
303
304
305
306
  {
  	/*
  	 * The daemon has exited; reset the state.
  	 */
4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
307
  	fcopy_transaction.state = HVUTIL_DEVICE_INIT;
d9b165294   Dexuan Cui   hv: hv_fcopy: dro...
308

c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
309
  	if (cancel_delayed_work_sync(&fcopy_timeout_work))
d9b165294   Dexuan Cui   hv: hv_fcopy: dro...
310
  		fcopy_respond_to_host(HV_E_FAIL);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
311
312
313
314
315
  }
  
  int hv_fcopy_init(struct hv_util_service *srv)
  {
  	recv_buffer = srv->recv_buffer;
b9830d120   K. Y. Srinivasan   Drivers: hv: util...
316
  	fcopy_transaction.recv_channel = srv->channel;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
317
318
319
320
321
322
323
  
  	/*
  	 * When this driver loads, the user level daemon that
  	 * processes the host requests may not yet be running.
  	 * Defer processing channel callbacks until the daemon
  	 * has registered.
  	 */
4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
324
  	fcopy_transaction.state = HVUTIL_DEVICE_INIT;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
325

c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
326
327
328
329
330
331
  	hvt = hvutil_transport_init(fcopy_devname, 0, 0,
  				    fcopy_on_msg, fcopy_on_reset);
  	if (!hvt)
  		return -EFAULT;
  
  	return 0;
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
332
  }
54e19d340   Dexuan Cui   hv_utils: Add the...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  static void hv_fcopy_cancel_work(void)
  {
  	cancel_delayed_work_sync(&fcopy_timeout_work);
  	cancel_work_sync(&fcopy_send_work);
  }
  
  int hv_fcopy_pre_suspend(void)
  {
  	struct vmbus_channel *channel = fcopy_transaction.recv_channel;
  	struct hv_fcopy_hdr *fcopy_msg;
  
  	/*
  	 * Fake a CANCEL_FCOPY message for the user space daemon in case the
  	 * daemon is in the middle of copying some file. It doesn't matter if
  	 * there is already a message pending to be delivered to the user
  	 * space since we force fcopy_transaction.state to be HVUTIL_READY, so
  	 * the user space daemon's write() will fail with EINVAL (see
  	 * fcopy_on_msg()), and the daemon will reset the device by closing
  	 * and re-opening it.
  	 */
  	fcopy_msg = kzalloc(sizeof(*fcopy_msg), GFP_KERNEL);
  	if (!fcopy_msg)
  		return -ENOMEM;
  
  	tasklet_disable(&channel->callback_event);
  
  	fcopy_msg->operation = CANCEL_FCOPY;
  
  	hv_fcopy_cancel_work();
  
  	/* We don't care about the return value. */
  	hvutil_transport_send(hvt, fcopy_msg, sizeof(*fcopy_msg), NULL);
  
  	kfree(fcopy_msg);
  
  	fcopy_transaction.state = HVUTIL_READY;
  
  	/* tasklet_enable() will be called in hv_fcopy_pre_resume(). */
  	return 0;
  }
  
  int hv_fcopy_pre_resume(void)
  {
  	struct vmbus_channel *channel = fcopy_transaction.recv_channel;
  
  	tasklet_enable(&channel->callback_event);
  
  	return 0;
  }
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
382
383
  void hv_fcopy_deinit(void)
  {
4c93ccccf   Vitaly Kuznetsov   Drivers: hv: fcop...
384
  	fcopy_transaction.state = HVUTIL_DEVICE_DYING;
54e19d340   Dexuan Cui   hv_utils: Add the...
385
386
  
  	hv_fcopy_cancel_work();
c7e490fc2   Vitaly Kuznetsov   Drivers: hv: fcop...
387
  	hvutil_transport_destroy(hvt);
01325476d   K. Y. Srinivasan   Drivers: hv: Impl...
388
  }