/* * An implementation of file copy service. * * Copyright (C) 2014, Microsoft, Inc. * * Author : K. Y. Srinivasan * * 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 #include #include #include #include #include #include #include #include #include #include "hyperv_vmbus.h" #define WIN8_SRV_MAJOR 1 #define WIN8_SRV_MINOR 1 #define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) /* * 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. */ static struct { int state; /* hvutil_device_state */ int recv_len; /* number of bytes received. */ struct hv_fcopy_hdr *fcopy_msg; /* current message */ struct hv_start_fcopy message; /* sent to daemon */ struct vmbus_channel *recv_channel; /* chn we got the request */ u64 recv_req_id; /* request ID. */ void *fcopy_context; /* for the channel callback */ struct semaphore read_sema; } fcopy_transaction; static void fcopy_send_data(void); static void fcopy_respond_to_host(int error); static void fcopy_timeout_func(struct work_struct *dummy); static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func); static u8 *recv_buffer; static void fcopy_timeout_func(struct work_struct *dummy) { /* * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ fcopy_respond_to_host(HV_E_FAIL); /* Transaction is finished, reset the state. */ if (fcopy_transaction.state > HVUTIL_READY) fcopy_transaction.state = HVUTIL_READY; /* In the case the user-space daemon crashes, hangs or is killed, we * need to down the semaphore, otherwise, after the daemon starts next * time, the obsolete data in fcopy_transaction.message or * fcopy_transaction.fcopy_msg will be used immediately. * * NOTE: fcopy_read() happens to get the semaphore (very rare)? We're * still OK, because we've reported the failure to the host. */ if (down_trylock(&fcopy_transaction.read_sema)) ; hv_poll_channel(fcopy_transaction.fcopy_context, hv_fcopy_onchannelcallback); } static int fcopy_handle_handshake(u32 version) { switch (version) { case FCOPY_CURRENT_VERSION: 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; } pr_info("FCP: user-mode registering done. Daemon version: %d\n", version); fcopy_transaction.state = HVUTIL_READY; hv_poll_channel(fcopy_transaction.fcopy_context, hv_fcopy_onchannelcallback); return 0; } static void fcopy_send_data(void) { struct hv_start_fcopy *smsg_out = &fcopy_transaction.message; int operation = fcopy_transaction.fcopy_msg->operation; struct hv_start_fcopy *smsg_in; /* * 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: memset(smsg_out, 0, sizeof(struct hv_start_fcopy)); smsg_out->hdr.operation = operation; 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, (__u8 *)smsg_out->file_name, W_MAX_PATH - 1); utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH, UTF16_LITTLE_ENDIAN, (__u8 *)smsg_out->path_name, W_MAX_PATH - 1); smsg_out->copy_flags = smsg_in->copy_flags; smsg_out->file_size = smsg_in->file_size; break; default: break; } up(&fcopy_transaction.read_sema); return; } /* * 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; 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; struct icmsg_negotiate *negop = NULL; int util_fw_version; int fcopy_srv_version; if (fcopy_transaction.state > HVUTIL_READY) { /* * We will defer processing this callback once * the current transaction is complete. */ fcopy_transaction.fcopy_context = context; return; } fcopy_transaction.fcopy_context = NULL; 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) { util_fw_version = UTIL_FW_VERSION; fcopy_srv_version = WIN8_SRV_VERSION; vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer, util_fw_version, fcopy_srv_version); } 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. */ fcopy_transaction.recv_len = recvlen; fcopy_transaction.recv_channel = channel; fcopy_transaction.recv_req_id = requestid; fcopy_transaction.fcopy_msg = fcopy_msg; 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; /* * Send the information to the user-level daemon. */ schedule_delayed_work(&fcopy_timeout_work, 5*HZ); fcopy_send_data(); return; } icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, VM_PKT_DATA_INBAND, 0); } /* * Create a char device that can support read/write for passing * the payload. */ static ssize_t fcopy_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { void *src; size_t copy_size; int operation; /* * Wait until there is something to be read. */ if (down_interruptible(&fcopy_transaction.read_sema)) return -EINTR; /* * The channel may be rescinded and in this case, we will wakeup the * the thread blocked on the semaphore and we will use the state to * correctly handle this case. */ if (fcopy_transaction.state != HVUTIL_HOSTMSG_RECEIVED) return -ENODEV; operation = fcopy_transaction.fcopy_msg->operation; if (operation == START_FILE_COPY) { src = &fcopy_transaction.message; copy_size = sizeof(struct hv_start_fcopy); if (count < copy_size) return 0; } else { src = fcopy_transaction.fcopy_msg; copy_size = sizeof(struct hv_do_fcopy); if (count < copy_size) return 0; } if (copy_to_user(buf, src, copy_size)) return -EFAULT; fcopy_transaction.state = HVUTIL_USERSPACE_REQ; return copy_size; } static ssize_t fcopy_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int response = 0; if (count != sizeof(int)) return -EINVAL; if (copy_from_user(&response, buf, sizeof(int))) return -EFAULT; if (fcopy_transaction.state == HVUTIL_DEVICE_INIT) { if (fcopy_handle_handshake(response)) return -EINVAL; return sizeof(int); } if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ) return -EINVAL; /* * Complete the transaction by forwarding the result * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&fcopy_timeout_work)) { fcopy_transaction.state = HVUTIL_USERSPACE_RECV; fcopy_respond_to_host(response); fcopy_transaction.state = HVUTIL_READY; hv_poll_channel(fcopy_transaction.fcopy_context, hv_fcopy_onchannelcallback); } return sizeof(int); } static int fcopy_open(struct inode *inode, struct file *f) { /* * The user level daemon that will open this device is * really an extension of this driver. We can have only * active open at a time. */ if (fcopy_transaction.state != HVUTIL_DEVICE_INIT) return -EBUSY; return 0; } /* XXX: there are still some tricky corner cases, e.g., * In an SMP guest, when fcopy_release() runs between * schedule_delayed_work() and fcopy_send_data(), there is * still a chance an obsolete message will be queued. */ static int fcopy_release(struct inode *inode, struct file *f) { /* * The daemon has exited; reset the state. */ fcopy_transaction.state = HVUTIL_DEVICE_INIT; if (cancel_delayed_work_sync(&fcopy_timeout_work)) { /* We haven't up()-ed the semaphore(very rare)? */ if (down_trylock(&fcopy_transaction.read_sema)) ; fcopy_respond_to_host(HV_E_FAIL); } return 0; } static const struct file_operations fcopy_fops = { .owner = THIS_MODULE, .read = fcopy_read, .write = fcopy_write, .release = fcopy_release, .open = fcopy_open, }; static struct miscdevice fcopy_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "vmbus/hv_fcopy", .fops = &fcopy_fops, }; static int fcopy_dev_init(void) { return misc_register(&fcopy_misc); } static void fcopy_dev_deinit(void) { /* * Signal the semaphore as the device is * going away. */ up(&fcopy_transaction.read_sema); misc_deregister(&fcopy_misc); } int hv_fcopy_init(struct hv_util_service *srv) { recv_buffer = srv->recv_buffer; /* * 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. */ fcopy_transaction.state = HVUTIL_DEVICE_INIT; sema_init(&fcopy_transaction.read_sema, 0); return fcopy_dev_init(); } void hv_fcopy_deinit(void) { fcopy_transaction.state = HVUTIL_DEVICE_DYING; cancel_delayed_work_sync(&fcopy_timeout_work); fcopy_dev_deinit(); }