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 (in_hand_shake) { if (fcopy_handle_handshake(response)) return -EINVAL; return sizeof(int); } /* * Complete the transaction by forwarding the result * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&fcopy_work)) fcopy_respond_to_host(response); return sizeof(int); }
/* Callback when data is received from userspace */ static int fcopy_on_msg(void *msg, int len) { int *val = (int *)msg; if (len != sizeof(int)) return -EINVAL; if (fcopy_transaction.state == HVUTIL_DEVICE_INIT) return fcopy_handle_handshake(*val); 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(*val); hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); } return 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) return; 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_work(&fcopy_send_work); schedule_delayed_work(&fcopy_timeout_work, HV_UTIL_TIMEOUT * HZ); return; } icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, VM_PKT_DATA_INBAND, 0); }
static void fcopy_send_data(struct work_struct *dummy) { struct hv_start_fcopy *smsg_out = NULL; int operation = fcopy_transaction.fcopy_msg->operation; struct hv_start_fcopy *smsg_in; void *out_src; int rc, out_len; /* * 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: out_len = sizeof(struct hv_start_fcopy); smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL); if (!smsg_out) return; 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; out_src = smsg_out; break; default: out_src = fcopy_transaction.fcopy_msg; out_len = fcopy_transaction.recv_len; break; } fcopy_transaction.state = HVUTIL_USERSPACE_REQ; rc = hvutil_transport_send(hvt, out_src, out_len, NULL); if (rc) { pr_debug("FCP: failed to communicate to the daemon: %d\n", rc); if (cancel_delayed_work_sync(&fcopy_timeout_work)) { fcopy_respond_to_host(HV_E_FAIL); fcopy_transaction.state = HVUTIL_READY; } } kfree(smsg_out); }
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); hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper); }
static void fcopy_on_reset(void) { /* * The daemon has exited; reset the state. */ fcopy_transaction.state = HVUTIL_DEVICE_INIT; if (cancel_delayed_work_sync(&fcopy_timeout_work)) fcopy_respond_to_host(HV_E_FAIL); }
/* XXX: there are still some tricky corner cases, e.g., * 1) In a 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. * * 2) When the fcopy daemon is running, if we unload the driver, * we'll notice a kernel oops when we kill the daemon later. */ static int fcopy_release(struct inode *inode, struct file *f) { /* * The daemon has exited; reset the state. */ in_hand_shake = true; opened = false; if (cancel_delayed_work_sync(&fcopy_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 void fcopy_work_func(void *dummy) { /* * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ fcopy_respond_to_host(HV_E_FAIL); /* 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)) ; }