Blame view
drivers/hv/hv_fcopy.c
9.92 KB
01325476d Drivers: hv: Impl... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * An implementation of file copy service. * * Copyright (C) 2014, Microsoft, Inc. * * Author : K. Y. Srinivasan <ksrinivasan@novell.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more * details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
01325476d Drivers: hv: Impl... |
21 22 |
#include <linux/nls.h> #include <linux/workqueue.h> |
01325476d Drivers: hv: Impl... |
23 24 |
#include <linux/hyperv.h> #include <linux/sched.h> |
01325476d Drivers: hv: Impl... |
25 26 |
#include "hyperv_vmbus.h" |
c7e490fc2 Drivers: hv: fcop... |
27 |
#include "hv_utils_transport.h" |
01325476d Drivers: hv: Impl... |
28 29 30 31 |
#define WIN8_SRV_MAJOR 1 #define WIN8_SRV_MINOR 1 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) |
a16564541 Drivers: hv: vmbu... |
32 33 34 35 36 37 38 39 40 |
#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 Drivers: hv: Impl... |
41 42 43 44 45 46 47 48 49 50 51 52 |
/* * 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 Drivers: hv: Impl... |
53 54 55 |
*/ static struct { |
4c93ccccf Drivers: hv: fcop... |
56 |
int state; /* hvutil_device_state */ |
01325476d Drivers: hv: Impl... |
57 58 |
int recv_len; /* number of bytes received. */ struct hv_fcopy_hdr *fcopy_msg; /* current message */ |
01325476d Drivers: hv: Impl... |
59 60 |
struct vmbus_channel *recv_channel; /* chn we got the request */ u64 recv_req_id; /* request ID. */ |
01325476d Drivers: hv: Impl... |
61 |
} fcopy_transaction; |
01325476d Drivers: hv: Impl... |
62 |
static void fcopy_respond_to_host(int error); |
c7e490fc2 Drivers: hv: fcop... |
63 |
static void fcopy_send_data(struct work_struct *dummy); |
1d072339f Drivers: hv: fcop... |
64 65 |
static void fcopy_timeout_func(struct work_struct *dummy); static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func); |
c7e490fc2 Drivers: hv: fcop... |
66 67 |
static DECLARE_WORK(fcopy_send_work, fcopy_send_data); static const char fcopy_devname[] = "vmbus/hv_fcopy"; |
01325476d Drivers: hv: Impl... |
68 |
static u8 *recv_buffer; |
c7e490fc2 Drivers: hv: fcop... |
69 |
static struct hvutil_transport *hvt; |
a4d1ee5b0 Drivers: hv: fcop... |
70 71 72 73 |
/* * This state maintains the version number registered by the daemon. */ static int dm_reg_value; |
01325476d Drivers: hv: Impl... |
74 |
|
3cace4a61 Drivers: hv: util... |
75 76 77 78 79 80 |
static void fcopy_poll_wrapper(void *channel) { /* Transaction is finished, reset the state here to avoid races. */ fcopy_transaction.state = HVUTIL_READY; hv_fcopy_onchannelcallback(channel); } |
1d072339f Drivers: hv: fcop... |
81 |
static void fcopy_timeout_func(struct work_struct *dummy) |
01325476d Drivers: hv: Impl... |
82 83 84 85 86 87 |
{ /* * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ fcopy_respond_to_host(HV_E_FAIL); |
3cace4a61 Drivers: hv: util... |
88 |
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); |
01325476d Drivers: hv: Impl... |
89 |
} |
e0fa3e5e7 Drivers: hv: util... |
90 91 92 93 94 95 |
static void fcopy_register_done(void) { pr_debug("FCP: userspace daemon registered "); hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); } |
01325476d Drivers: hv: Impl... |
96 97 |
static int fcopy_handle_handshake(u32 version) { |
a4d1ee5b0 Drivers: hv: fcop... |
98 |
u32 our_ver = FCOPY_CURRENT_VERSION; |
01325476d Drivers: hv: Impl... |
99 |
switch (version) { |
a4d1ee5b0 Drivers: hv: fcop... |
100 101 102 103 104 105 |
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 Drivers: hv: util... |
106 107 |
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver), fcopy_register_done)) |
a4d1ee5b0 Drivers: hv: fcop... |
108 109 |
return -EFAULT; dm_reg_value = version; |
01325476d Drivers: hv: Impl... |
110 111 112 113 114 115 116 117 118 119 |
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 Drivers: hv: util... |
120 121 |
pr_debug("FCP: userspace daemon ver. %d connected ", version); |
01325476d Drivers: hv: Impl... |
122 123 |
return 0; } |
c7e490fc2 Drivers: hv: fcop... |
124 |
static void fcopy_send_data(struct work_struct *dummy) |
01325476d Drivers: hv: Impl... |
125 |
{ |
25ef06fe2 Drivers: hv: fcop... |
126 |
struct hv_start_fcopy *smsg_out = NULL; |
01325476d Drivers: hv: Impl... |
127 128 |
int operation = fcopy_transaction.fcopy_msg->operation; struct hv_start_fcopy *smsg_in; |
c7e490fc2 Drivers: hv: fcop... |
129 130 |
void *out_src; int rc, out_len; |
01325476d Drivers: hv: Impl... |
131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
/* * 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 Drivers: hv: fcop... |
145 |
out_len = sizeof(struct hv_start_fcopy); |
25ef06fe2 Drivers: hv: fcop... |
146 147 148 149 150 |
smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL); if (!smsg_out) return; smsg_out->hdr.operation = operation; |
01325476d Drivers: hv: Impl... |
151 152 153 154 |
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 Drivers: hv: fcop... |
155 |
(__u8 *)&smsg_out->file_name, W_MAX_PATH - 1); |
01325476d Drivers: hv: Impl... |
156 157 158 |
utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH, UTF16_LITTLE_ENDIAN, |
25ef06fe2 Drivers: hv: fcop... |
159 |
(__u8 *)&smsg_out->path_name, W_MAX_PATH - 1); |
01325476d Drivers: hv: Impl... |
160 |
|
25ef06fe2 Drivers: hv: fcop... |
161 162 163 |
smsg_out->copy_flags = smsg_in->copy_flags; smsg_out->file_size = smsg_in->file_size; out_src = smsg_out; |
01325476d Drivers: hv: Impl... |
164 |
break; |
549e658a0 Drivers: hv: fcop... |
165 166 167 168 |
case WRITE_TO_FILE: out_src = fcopy_transaction.fcopy_msg; out_len = sizeof(struct hv_do_fcopy); break; |
01325476d Drivers: hv: Impl... |
169 |
default: |
c7e490fc2 Drivers: hv: fcop... |
170 171 |
out_src = fcopy_transaction.fcopy_msg; out_len = fcopy_transaction.recv_len; |
01325476d Drivers: hv: Impl... |
172 173 |
break; } |
c7e490fc2 Drivers: hv: fcop... |
174 175 |
fcopy_transaction.state = HVUTIL_USERSPACE_REQ; |
e0fa3e5e7 Drivers: hv: util... |
176 |
rc = hvutil_transport_send(hvt, out_src, out_len, NULL); |
c7e490fc2 Drivers: hv: fcop... |
177 178 179 180 181 182 183 184 |
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 Drivers: hv: fcop... |
185 |
kfree(smsg_out); |
01325476d Drivers: hv: Impl... |
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
} /* * 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 Drivers: hv: Impl... |
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
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 Drivers: hv: Impl... |
234 |
int fcopy_srv_version; |
3cace4a61 Drivers: hv: util... |
235 |
if (fcopy_transaction.state > HVUTIL_READY) |
01325476d Drivers: hv: Impl... |
236 |
return; |
01325476d Drivers: hv: Impl... |
237 238 239 240 241 242 243 244 245 |
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, &requestid); if (recvlen <= 0) return; icmsghdr = (struct icmsg_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
1274a690f Drivers: hv: Log ... |
246 |
if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, |
a16564541 Drivers: hv: vmbu... |
247 248 |
fw_versions, FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT, |
1274a690f Drivers: hv: Log ... |
249 250 251 252 253 254 255 |
NULL, &fcopy_srv_version)) { pr_info("FCopy IC version %d.%d ", fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF); } |
01325476d Drivers: hv: Impl... |
256 257 258 259 260 261 262 263 264 |
} 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 Drivers: hv: Impl... |
265 |
fcopy_transaction.recv_len = recvlen; |
01325476d Drivers: hv: Impl... |
266 267 |
fcopy_transaction.recv_req_id = requestid; fcopy_transaction.fcopy_msg = fcopy_msg; |
4c93ccccf Drivers: hv: fcop... |
268 269 270 271 272 273 |
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 Drivers: hv: Impl... |
274 275 276 |
/* * Send the information to the user-level daemon. */ |
c7e490fc2 Drivers: hv: fcop... |
277 |
schedule_work(&fcopy_send_work); |
c0b200cfb Drivers: hv: util... |
278 279 |
schedule_delayed_work(&fcopy_timeout_work, HV_UTIL_TIMEOUT * HZ); |
01325476d Drivers: hv: Impl... |
280 281 282 283 284 285 |
return; } icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, VM_PKT_DATA_INBAND, 0); } |
c7e490fc2 Drivers: hv: fcop... |
286 287 |
/* Callback when data is received from userspace */ static int fcopy_on_msg(void *msg, int len) |
01325476d Drivers: hv: Impl... |
288 |
{ |
c7e490fc2 Drivers: hv: fcop... |
289 |
int *val = (int *)msg; |
01325476d Drivers: hv: Impl... |
290 |
|
c7e490fc2 Drivers: hv: fcop... |
291 |
if (len != sizeof(int)) |
01325476d Drivers: hv: Impl... |
292 |
return -EINVAL; |
c7e490fc2 Drivers: hv: fcop... |
293 294 |
if (fcopy_transaction.state == HVUTIL_DEVICE_INIT) return fcopy_handle_handshake(*val); |
01325476d Drivers: hv: Impl... |
295 |
|
4c93ccccf Drivers: hv: fcop... |
296 297 |
if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ) return -EINVAL; |
01325476d Drivers: hv: Impl... |
298 299 300 301 |
/* * Complete the transaction by forwarding the result * to the host. But first, cancel the timeout. */ |
4c93ccccf Drivers: hv: fcop... |
302 303 |
if (cancel_delayed_work_sync(&fcopy_timeout_work)) { fcopy_transaction.state = HVUTIL_USERSPACE_RECV; |
c7e490fc2 Drivers: hv: fcop... |
304 |
fcopy_respond_to_host(*val); |
3cace4a61 Drivers: hv: util... |
305 306 |
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); |
242f31221 Drivers: hv: fcop... |
307 |
} |
01325476d Drivers: hv: Impl... |
308 |
|
01325476d Drivers: hv: Impl... |
309 310 |
return 0; } |
c7e490fc2 Drivers: hv: fcop... |
311 |
static void fcopy_on_reset(void) |
01325476d Drivers: hv: Impl... |
312 313 314 315 |
{ /* * The daemon has exited; reset the state. */ |
4c93ccccf Drivers: hv: fcop... |
316 |
fcopy_transaction.state = HVUTIL_DEVICE_INIT; |
d9b165294 hv: hv_fcopy: dro... |
317 |
|
c7e490fc2 Drivers: hv: fcop... |
318 |
if (cancel_delayed_work_sync(&fcopy_timeout_work)) |
d9b165294 hv: hv_fcopy: dro... |
319 |
fcopy_respond_to_host(HV_E_FAIL); |
01325476d Drivers: hv: Impl... |
320 321 322 323 324 |
} int hv_fcopy_init(struct hv_util_service *srv) { recv_buffer = srv->recv_buffer; |
b9830d120 Drivers: hv: util... |
325 |
fcopy_transaction.recv_channel = srv->channel; |
01325476d Drivers: hv: Impl... |
326 327 328 329 330 331 332 |
/* * 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 Drivers: hv: fcop... |
333 |
fcopy_transaction.state = HVUTIL_DEVICE_INIT; |
01325476d Drivers: hv: Impl... |
334 |
|
c7e490fc2 Drivers: hv: fcop... |
335 336 337 338 339 340 |
hvt = hvutil_transport_init(fcopy_devname, 0, 0, fcopy_on_msg, fcopy_on_reset); if (!hvt) return -EFAULT; return 0; |
01325476d Drivers: hv: Impl... |
341 342 343 344 |
} void hv_fcopy_deinit(void) { |
4c93ccccf Drivers: hv: fcop... |
345 |
fcopy_transaction.state = HVUTIL_DEVICE_DYING; |
1d072339f Drivers: hv: fcop... |
346 |
cancel_delayed_work_sync(&fcopy_timeout_work); |
c7e490fc2 Drivers: hv: fcop... |
347 |
hvutil_transport_destroy(hvt); |
01325476d Drivers: hv: Impl... |
348 |
} |