[PATCH RFC 3/3] Drivers: hv: fcopy: make it consistent with vss/kvp

Vitaly Kuznetsov vkuznets at redhat.com
Fri Feb 27 16:14:25 UTC 2015


Re-implement fcopy in a consistent with "Drivers: hv: vss/kvp: convert
userspace/kernel communication to using char device" way.

In particular:
- Implement "state machine" for the driver instead of 3 separate syncronization
  variables ('fcopy_transaction.active', 'fcopy_transaction.read_sema', 'opened')
- Use ioctl for kernel/userspace version negotiation.
- Support poll() operation.
- Set .owner = THIS_MODULE to prevent kernel crash if the module if being
  unloaded while there is an active file descriptior in userspace.
- Use __u32 instead of int in userspace replies as it matches icmsg_hdr.status.
- Other minor changes to make drivers code look alike.

Signed-off-by: Vitaly Kuznetsov <vkuznets at redhat.com>
---
 drivers/hv/hv_fcopy.c       | 395 ++++++++++++++++++++++++++------------------
 include/uapi/linux/hyperv.h |   1 +
 tools/hv/hv_fcopy_daemon.c  |  48 ++++--
 3 files changed, 264 insertions(+), 180 deletions(-)

diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index cd453e4..05c3580 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -28,6 +28,7 @@
 #include <linux/sched.h>
 #include <linux/uaccess.h>
 #include <linux/miscdevice.h>
+#include <linux/poll.h>
 
 #include "hyperv_vmbus.h"
 
@@ -35,6 +36,8 @@
 #define WIN8_SRV_MINOR		1
 #define WIN8_SRV_VERSION	(WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
 
+#define MAX_FCOPY_CHSIZE (PAGE_SIZE * 2)
+
 /*
  * Global state maintained for transaction that is being processed.
  * For a class of integration services, including the "file copy service",
@@ -47,36 +50,37 @@
  * 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.
- *
- * The transaction "active" state is set when we receive a request from the
- * host and we cleanup this state when the transaction is completed - when we
- * respond to the host with our response. When the transaction active state is
- * set, we defer handling incoming packets.
  */
 
+enum fcopy_device_state {
+	FCOPY_DEVICE_INITIALIZING = 0, /* driver was loaded */
+	FCOPY_DEVICE_OPENED,           /* device was opened */
+	FCOPY_READY,                   /* userspace registered */
+	FCOPY_HOSTMSG_RECEIVED,        /* message from host was received */
+	FCOPY_USERMSG_READY,           /* message for userspace is ready */
+	FCOPY_USERSPACE_REQ,           /* request to userspace was sent */
+	FCOPY_USERSPACE_RECV,          /* reply from userspace was received */
+	FCOPY_DEVICE_DYING,            /* driver unload is in progress */
+};
+
 static struct {
-	bool active; /* transaction status - active or not */
+	int state; /* fcopy 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 bool opened; /* currently device opened */
+	int dm_reg_value; /* daemon version number */
+	struct mutex lock; /* syncronization */
+	u8 user_msg[MAX_FCOPY_CHSIZE]; /* message to userspace */
+	u8 host_msg[MAX_FCOPY_CHSIZE]; /* message to/from host */
+	wait_queue_head_t proc_list; /* waiting processes */
+} fcopy_device;
 
-/*
- * Before we can accept copy messages from the host, we need
- * to handshake with the user level daemon. This state tracks
- * if we are in the handshake phase.
- */
-static bool in_hand_shake = true;
-static void fcopy_send_data(void);
 static void fcopy_respond_to_host(int error);
 static void fcopy_work_func(struct work_struct *dummy);
+static void fcopy_send_op(struct work_struct *dummy);
 static DECLARE_DELAYED_WORK(fcopy_work, fcopy_work_func);
+static DECLARE_WORK(fcopy_send_op_work, fcopy_send_op);
 static u8 *recv_buffer;
 
 static void fcopy_work_func(struct work_struct *dummy)
@@ -86,22 +90,22 @@ static void fcopy_work_func(struct work_struct *dummy)
 	 * 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))
-		;
-
+static void poll_channel(struct vmbus_channel *channel)
+{
+	if (channel->target_cpu != smp_processor_id())
+		smp_call_function_single(channel->target_cpu,
+					 hv_fcopy_onchannelcallback,
+					 channel, true);
+	else
+		hv_fcopy_onchannelcallback(channel);
 }
 
 static int fcopy_handle_handshake(u32 version)
 {
+	int ret = 0;
+
 	switch (version) {
 	case FCOPY_CURRENT_VERSION:
 		break;
@@ -112,23 +116,19 @@ static int fcopy_handle_handshake(u32 version)
 		 * deal with, we will be backward compatible.
 		 * We will add this code when needed.
 		 */
-		return -EINVAL;
+		ret = -EINVAL;
 	}
-	pr_info("FCP: user-mode registering done. Daemon version: %d\n",
-		version);
-	fcopy_transaction.active = false;
-	if (fcopy_transaction.fcopy_context)
-		hv_fcopy_onchannelcallback(fcopy_transaction.fcopy_context);
-	in_hand_shake = false;
 	return 0;
 }
 
-static void fcopy_send_data(void)
+static void fcopy_send_op(struct work_struct *dummy)
 {
-	struct hv_start_fcopy *smsg_out = &fcopy_transaction.message;
-	int operation = fcopy_transaction.fcopy_msg->operation;
+	struct hv_start_fcopy *smsg_out;
+	struct hv_do_fcopy *dmsg_out;
 	struct hv_start_fcopy *smsg_in;
 
+	mutex_lock(&fcopy_device.lock);
+
 	/*
 	 * The  strings sent from the host are encoded in
 	 * in utf16; convert it to utf8 strings.
@@ -140,11 +140,14 @@ static void fcopy_send_data(void)
 	 * that the strings can be properly terminated!
 	 */
 
-	switch (operation) {
+	switch (((struct hv_fcopy_hdr *)fcopy_device.host_msg)->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;
+		memset(&fcopy_device.user_msg, 0,
+		       sizeof(struct hv_start_fcopy));
+
+		smsg_out = (struct hv_start_fcopy *)fcopy_device.user_msg;
+		smsg_out->hdr.operation = START_FILE_COPY;
+		smsg_in = (struct hv_start_fcopy *)fcopy_device.host_msg;
 
 		utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
 				UTF16_LITTLE_ENDIAN,
@@ -159,9 +162,16 @@ static void fcopy_send_data(void)
 		break;
 
 	default:
+		dmsg_out = (struct hv_do_fcopy *)fcopy_device.user_msg;
+		memcpy(fcopy_device.user_msg, fcopy_device.host_msg,
+		       sizeof(struct hv_do_fcopy));
 		break;
 	}
-	up(&fcopy_transaction.read_sema);
+
+	fcopy_device.state = FCOPY_USERMSG_READY;
+	wake_up_interruptible(&fcopy_device.proc_list);
+
+	mutex_unlock(&fcopy_device.lock);
 	return;
 }
 
@@ -169,8 +179,7 @@ static void fcopy_send_data(void)
  * Send a response back to the host.
  */
 
-static void
-fcopy_respond_to_host(int error)
+static void fcopy_respond_to_host(int error)
 {
 	struct icmsg_hdr *icmsghdr;
 	u32 buf_len;
@@ -185,11 +194,9 @@ fcopy_respond_to_host(int error)
 	 * 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;
-
-	fcopy_transaction.active = false;
+	buf_len = fcopy_device.recv_len;
+	channel = fcopy_device.recv_channel;
+	req_id = fcopy_device.recv_req_id;
 
 	icmsghdr = (struct icmsg_hdr *)
 			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
@@ -205,6 +212,20 @@ fcopy_respond_to_host(int error)
 	icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
 	vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
 				VM_PKT_DATA_INBAND, 0);
+
+
+	/* We're ready to process next request, reset the device state */
+	if (fcopy_device.state == FCOPY_USERSPACE_RECV ||
+	    fcopy_device.state == FCOPY_USERSPACE_REQ)
+		fcopy_device.state = FCOPY_READY;
+
+	/*
+	 * Make sure device state was set before polling the channel as
+	 * processing can happen on a different CPU.
+	 */
+	smp_mb();
+
+	poll_channel(channel);
 }
 
 void hv_fcopy_onchannelcallback(void *context)
@@ -218,16 +239,17 @@ void hv_fcopy_onchannelcallback(void *context)
 	int util_fw_version;
 	int fcopy_srv_version;
 
-	if (fcopy_transaction.active) {
+	if (fcopy_device.state > FCOPY_READY) {
 		/*
 		 * We will defer processing this callback once
 		 * the current transaction is complete.
 		 */
-		fcopy_transaction.fcopy_context = context;
+		fcopy_device.fcopy_context = channel;
 		return;
 	}
+	fcopy_device.fcopy_context = NULL;
 
-	vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
+	vmbus_recvpacket(channel, recv_buffer, MAX_FCOPY_CHSIZE, &recvlen,
 			 &requestid);
 	if (recvlen <= 0)
 		return;
@@ -235,6 +257,10 @@ void hv_fcopy_onchannelcallback(void *context)
 	icmsghdr = (struct icmsg_hdr *)&recv_buffer[
 			sizeof(struct vmbuspipe_hdr)];
 	if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
+		/*
+		 * Process negotiation even before usersace daemon is connected
+		 * as we can timeout othervise.
+		 */
 		util_fw_version = UTIL_FW_VERSION;
 		fcopy_srv_version = WIN8_SRV_VERSION;
 		vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer,
@@ -249,17 +275,26 @@ void hv_fcopy_onchannelcallback(void *context)
 		 * transaction; note transactions are serialized.
 		 */
 
-		fcopy_transaction.active = true;
-		fcopy_transaction.recv_len = recvlen;
-		fcopy_transaction.recv_channel = channel;
-		fcopy_transaction.recv_req_id = requestid;
-		fcopy_transaction.fcopy_msg = fcopy_msg;
+		fcopy_device.recv_len = recvlen;
+		fcopy_device.recv_channel = channel;
+		fcopy_device.recv_req_id = requestid;
+
+		if (fcopy_device.state != FCOPY_READY) {
+			/* Userspace daemon is not connected, just fail. */
+			fcopy_respond_to_host(HV_E_FAIL);
+			return;
+		}
+
+		memcpy(fcopy_device.host_msg, fcopy_msg, recvlen -
+		       (sizeof(struct vmbuspipe_hdr) +
+			sizeof(struct icmsg_hdr)));
+		fcopy_device.state = FCOPY_HOSTMSG_RECEIVED;
 
 		/*
 		 * Send the information to the user-level daemon.
 		 */
+		schedule_work(&fcopy_send_op_work);
 		schedule_delayed_work(&fcopy_work, 5*HZ);
-		fcopy_send_data();
 		return;
 	}
 	icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
@@ -272,121 +307,170 @@ void hv_fcopy_onchannelcallback(void *context)
  * the payload.
  */
 
-static ssize_t fcopy_read(struct file *file, char __user *buf,
-		size_t count, loff_t *ppos)
+static ssize_t fcopy_op_read(struct file *file, char __user *buf,
+			     size_t count, loff_t *ppos)
 {
-	void *src;
-	size_t copy_size;
-	int operation;
+	ssize_t ret = 0;
+	int copy_size;
+	struct hv_fcopy_hdr *hdr;
+
+	if (wait_event_interruptible(fcopy_device.proc_list,
+				     fcopy_device.state == FCOPY_USERMSG_READY
+				     ||
+				     fcopy_device.state == FCOPY_DEVICE_DYING))
+		return -EFAULT;
 
-	/*
-	 * Wait until there is something to be read.
-	 */
-	if (down_interruptible(&fcopy_transaction.read_sema))
-		return -EINTR;
+	if (fcopy_device.state != FCOPY_USERMSG_READY)
+		return -EFAULT;
 
-	/*
-	 * The channel may be rescinded and in this case, we will wakeup the
-	 * the thread blocked on the semaphore and we will use the opened
-	 * state to correctly handle this case.
-	 */
-	if (!opened)
-		return -ENODEV;
+	mutex_lock(&fcopy_device.lock);
 
-	operation = fcopy_transaction.fcopy_msg->operation;
+	hdr = (struct hv_fcopy_hdr *)fcopy_device.user_msg;
 
-	if (operation == START_FILE_COPY) {
-		src = &fcopy_transaction.message;
+	if (hdr->operation == START_FILE_COPY)
 		copy_size = sizeof(struct hv_start_fcopy);
-		if (count < copy_size)
-			return 0;
-	} else {
-		src = fcopy_transaction.fcopy_msg;
+	else
 		copy_size = sizeof(struct hv_do_fcopy);
-		if (count < copy_size)
-			return 0;
+
+	if (count < copy_size) {
+		pr_warn("fcopy_op_read: invalid read len: %d (expected: %d)\n",
+			(int)count, copy_size);
+		mutex_unlock(&fcopy_device.lock);
+		return -EINVAL;
 	}
-	if (copy_to_user(buf, src, copy_size))
-		return -EFAULT;
 
-	return copy_size;
+	if (!copy_to_user(buf, fcopy_device.user_msg, copy_size)) {
+		fcopy_device.state = FCOPY_USERSPACE_REQ;
+		ret = copy_size;
+	} else
+		ret = -EFAULT;
+
+	mutex_unlock(&fcopy_device.lock);
+	return ret;
 }
 
-static ssize_t fcopy_write(struct file *file, const char __user *buf,
-			size_t count, loff_t *ppos)
+static ssize_t fcopy_op_write(struct file *file, const char __user *buf,
+			      size_t count, loff_t *ppos)
 {
-	int response = 0;
+	int ret = 0;
+	u32 val32;
 
-	if (count != sizeof(int))
-		return -EINVAL;
-
-	if (copy_from_user(&response, buf, sizeof(int)))
+	if (fcopy_device.state == FCOPY_DEVICE_DYING)
 		return -EFAULT;
 
-	if (in_hand_shake) {
-		if (fcopy_handle_handshake(response))
-			return -EINVAL;
-		return sizeof(int);
+	if (count != sizeof(u32)) {
+		pr_warn("fcopy_op_write: invalid write len: %d (expected: %d)\n",
+			(int)count, (int)sizeof(u32));
+		return -EINVAL;
 	}
 
-	/*
-	 * 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);
+	mutex_lock(&fcopy_device.lock);
 
-	return sizeof(int);
+	if (fcopy_device.state == FCOPY_USERSPACE_REQ) {
+		if (!copy_from_user(&val32, buf, sizeof(u32))) {
+			fcopy_device.state = FCOPY_USERSPACE_RECV;
+			if (cancel_delayed_work_sync(&fcopy_work))
+				fcopy_respond_to_host(val32);
+			ret = sizeof(u32);
+		} else
+			ret = -EFAULT;
+	} else {
+		pr_warn("fcopy_op_write: invalid transaction state: %d\n",
+			fcopy_device.state);
+		ret = -EINVAL;
+	}
+
+	mutex_unlock(&fcopy_device.lock);
+	return ret;
 }
 
-static int fcopy_open(struct inode *inode, struct file *f)
+static int fcopy_op_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 (opened)
+	if (fcopy_device.state != FCOPY_DEVICE_INITIALIZING)
 		return -EBUSY;
-
-	/*
-	 * The daemon is alive; setup the state.
-	 */
-	opened = true;
+	fcopy_device.state = FCOPY_DEVICE_OPENED;
 	return 0;
 }
 
-/* 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)
+static int fcopy_op_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);
-	}
+	fcopy_device.state = FCOPY_DEVICE_INITIALIZING;
+	return 0;
+}
+
+static unsigned int fcopy_op_poll(struct file *file, poll_table *wait)
+{
+	if (fcopy_device.state == FCOPY_DEVICE_DYING)
+		return -EFAULT;
+
+	poll_wait(file, &fcopy_device.proc_list, wait);
+	if (fcopy_device.state == FCOPY_USERMSG_READY)
+		return POLLIN | POLLRDNORM;
 	return 0;
 }
 
+static long fcopy_op_ioctl(struct file *fp,
+			 unsigned int cmd, unsigned long arg)
+{
+	long ret = 0;
+	void __user *argp = (void __user *)arg;
+	u32 val32;
+
+	if (fcopy_device.state == FCOPY_DEVICE_DYING)
+		return -EFAULT;
+
+	/* The only ioctl we have is registation */
+	if (fcopy_device.state != FCOPY_DEVICE_OPENED)
+		return -EINVAL;
+
+	mutex_lock(&fcopy_device.lock);
+
+	switch (cmd) {
+	case HYPERV_FCOPY_REGISTER:
+		if (copy_from_user(&val32, argp, sizeof(val32))) {
+			ret = -EFAULT;
+			break;
+		}
+		if (!fcopy_handle_handshake(val32)) {
+			/* No special meaning for userspace part for now. */
+			val32 = (u32)WIN8_SRV_VERSION;
+			if (copy_to_user(argp, &val32, sizeof(val32))) {
+				ret = -EFAULT;
+				break;
+			}
+			fcopy_device.state = FCOPY_READY;
+			pr_info("FCOPY: user-mode registering done.\n");
+			if (fcopy_device.fcopy_context)
+				poll_channel(fcopy_device.fcopy_context);
+		} else
+			ret = -EINVAL;
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&fcopy_device.lock);
+	return ret;
+}
 
 static const struct file_operations fcopy_fops = {
-	.read           = fcopy_read,
-	.write          = fcopy_write,
-	.release	= fcopy_release,
-	.open		= fcopy_open,
+	.owner		= THIS_MODULE,
+	.read           = fcopy_op_read,
+	.write          = fcopy_op_write,
+	.release	= fcopy_op_release,
+	.open		= fcopy_op_open,
+	.poll		= fcopy_op_poll,
+	.unlocked_ioctl = fcopy_op_ioctl
 };
 
 static struct miscdevice fcopy_misc = {
@@ -395,29 +479,6 @@ static struct miscdevice fcopy_misc = {
 	.fops           = &fcopy_fops,
 };
 
-static int fcopy_dev_init(void)
-{
-	return misc_register(&fcopy_misc);
-}
-
-static void fcopy_dev_deinit(void)
-{
-
-	/*
-	 * The device is going away - perhaps because the
-	 * host has rescinded the channel. Setup state so that
-	 * user level daemon can gracefully exit if it is blocked
-	 * on the read semaphore.
-	 */
-	opened = false;
-	/*
-	 * 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;
@@ -428,14 +489,20 @@ int hv_fcopy_init(struct hv_util_service *srv)
 	 * Defer processing channel callbacks until the daemon
 	 * has registered.
 	 */
-	fcopy_transaction.active = true;
-	sema_init(&fcopy_transaction.read_sema, 0);
+	fcopy_device.state = FCOPY_DEVICE_INITIALIZING;
+	init_waitqueue_head(&fcopy_device.proc_list);
+	mutex_init(&fcopy_device.lock);
 
-	return fcopy_dev_init();
+	return misc_register(&fcopy_misc);
 }
 
 void hv_fcopy_deinit(void)
 {
+	fcopy_device.state = FCOPY_DEVICE_DYING;
+	/* Make sure nobody sees the old state */
+	smp_mb();
+	wake_up_interruptible(&fcopy_device.proc_list);
 	cancel_delayed_work_sync(&fcopy_work);
-	fcopy_dev_deinit();
+	cancel_work_sync(&fcopy_send_op_work);
+	misc_deregister(&fcopy_misc);
 }
diff --git a/include/uapi/linux/hyperv.h b/include/uapi/linux/hyperv.h
index 1939826..590a2f4 100644
--- a/include/uapi/linux/hyperv.h
+++ b/include/uapi/linux/hyperv.h
@@ -397,5 +397,6 @@ struct hv_kvp_ip_msg {
  */
 #define HYPERV_KVP_REGISTER        _IOWR('v', 0, __u32)
 #define HYPERV_VSS_REGISTER        _IOWR('v', 0, __u32)
+#define HYPERV_FCOPY_REGISTER      _IOWR('v', 0, __u32)
 
 #endif /* _UAPI_HYPERV_H */
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c
index 9445d8f..2ae8196 100644
--- a/tools/hv/hv_fcopy_daemon.c
+++ b/tools/hv/hv_fcopy_daemon.c
@@ -18,19 +18,16 @@
 
 
 #include <sys/types.h>
-#include <sys/socket.h>
 #include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <linux/types.h>
-#include <linux/kdev_t.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <string.h>
-#include <ctype.h>
 #include <errno.h>
 #include <linux/hyperv.h>
 #include <syslog.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 #include <dirent.h>
 #include <getopt.h>
@@ -132,7 +129,8 @@ void print_usage(char *argv[])
 int main(int argc, char *argv[])
 {
 	int fcopy_fd, len;
-	int error;
+	__u32 error;
+	struct pollfd pfd;
 	int daemonize = 1, long_index = 0, opt;
 	int version = FCOPY_CURRENT_VERSION;
 	char *buffer[4096 * 2];
@@ -176,19 +174,33 @@ int main(int argc, char *argv[])
 	/*
 	 * Register with the kernel.
 	 */
-	if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
-		syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
+	if (ioctl(fcopy_fd, HYPERV_FCOPY_REGISTER, &version)) {
+		syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
+		       errno, strerror(errno));
+		close(fcopy_fd);
 		exit(EXIT_FAILURE);
 	}
 
+	pfd.fd = fcopy_fd;
+
 	while (1) {
-		/*
-		 * In this loop we process fcopy messages after the
-		 * handshake is complete.
-		 */
-		len = pread(fcopy_fd, buffer, (4096 * 2), 0);
+		pfd.events = POLLIN;
+		pfd.revents = 0;
+
+		if (poll(&pfd, 1, -1) < 0) {
+			syslog(LOG_ERR, "poll failed; error:%d %s", errno,
+			       strerror(errno));
+			if (errno == EINVAL) {
+				close(fcopy_fd);
+				exit(EXIT_FAILURE);
+			} else
+				continue;
+		}
+
+		len = read(fcopy_fd, buffer, (4096 * 2));
 		if (len < 0) {
-			syslog(LOG_ERR, "pread failed: %s", strerror(errno));
+			syslog(LOG_ERR, "read failed: %d %s", errno,
+			       strerror(errno));
 			exit(EXIT_FAILURE);
 		}
 		in_msg = (struct hv_fcopy_hdr *)buffer;
@@ -213,9 +225,13 @@ int main(int argc, char *argv[])
 
 		}
 
-		if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
-			syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
+		if (write(fcopy_fd, &error, sizeof(__u32)) != sizeof(__u32)) {
+			syslog(LOG_ERR, "write failed: %d %s", errno,
+			       strerror(errno));
 			exit(EXIT_FAILURE);
 		}
 	}
+
+	close(fcopy_fd);
+	exit(0);
 }
-- 
1.9.3



More information about the devel mailing list