[PATCH RFC 1/3] Drivers: hv: kvp: convert userspace/kernel communication to using char device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Userspace/kernel communication via netlink has a number of issues:
- It is hard for userspace to figure out if the kernel part was loaded or not
  and this fact can change as there is a way to enable/disable the service from
  host side. Racy daemon startup is also a problem.
- When the userspace daemon restarts/dies kernel part doesn't receive a
  notification.
- Netlink communication is not stable under heavy load.
- ...

Re-implement the communication using misc char device. Use ioctl to do
kernel/userspace version negotiation (doesn't make much sense at this moment
as we're breaking backwards compatibility but can be used in future).

Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx>
---
 drivers/hv/hv_kvp.c         | 396 +++++++++++++++++++++++++++-----------------
 include/uapi/linux/hyperv.h |   8 +
 tools/hv/hv_kvp_daemon.c    | 187 ++++-----------------
 3 files changed, 287 insertions(+), 304 deletions(-)

diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index beb8105..8078b1a 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -22,12 +22,16 @@
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/net.h>
 #include <linux/nls.h>
-#include <linux/connector.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <linux/hyperv.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
 
+#include <linux/uaccess.h>
 
 /*
  * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
@@ -45,46 +49,41 @@
 #define WIN8_SRV_VERSION     (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
 
 /*
- * Global state maintained for transaction that is being processed.
- * Note that only one transaction can be active at any point in time.
- *
- * This state is set when we receive a request from the host; we
- * cleanup this state when the transaction is completed - when we respond
- * to the host with the key value.
+ * Global state maintained for the device. Note that only one transaction can
+ * be active at any point in time.
  */
 
+enum kvp_device_state {
+	KVP_DEVICE_INITIALIZING = 0, /* driver was loaded */
+	KVP_DEVICE_OPENED,           /* device was opened */
+	KVP_READY,                   /* userspace was registered */
+	KVP_HOSTMSG_RECEIVED,        /* message from host was received */
+	KVP_USERMSG_READY,           /* message for userspace is ready */
+	KVP_USERSPACE_REQ,           /* request to userspace was sent */
+	KVP_USERSPACE_RECV,          /* reply from userspace was received */
+	KVP_DEVICE_DYING,            /* driver unload is in progress */
+};
+
 static struct {
-	bool active; /* transaction status - active or not */
+	int state; /* kvp_device_state */
 	int recv_len; /* number of bytes received. */
-	struct hv_kvp_msg  *kvp_msg; /* current message */
 	struct vmbus_channel *recv_channel; /* chn we got the request */
 	u64 recv_req_id; /* request ID. */
 	void *kvp_context; /* for the channel callback */
-} kvp_transaction;
-
-/*
- * Before we can accept KVP 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;
-
-/*
- * This state maintains the version number registered by the daemon.
- */
-static int dm_reg_value;
+	int dm_reg_value; /* daemon version number */
+	struct mutex lock; /* syncronization */
+	struct hv_kvp_msg user_msg; /* message to/from userspace */
+	struct hv_kvp_msg host_msg; /* message to/from host */
+	wait_queue_head_t proc_list; /* waiting processes */
+} kvp_device;
 
 static void kvp_send_key(struct work_struct *dummy);
-
-
-static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
+static void kvp_respond_to_host(int error);
 static void kvp_work_func(struct work_struct *dummy);
-static void kvp_register(int);
 
 static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
 static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
 
-static struct cb_id kvp_id = { CN_KVP_IDX, CN_KVP_VAL };
 static const char kvp_name[] = "kvp_kernel_module";
 static u8 *recv_buffer;
 /*
@@ -92,31 +91,8 @@ static u8 *recv_buffer;
  * As part of this registration, pass the LIC version number.
  * This number has no meaning, it satisfies the registration protocol.
  */
-#define HV_DRV_VERSION           "3.1"
-
-static void
-kvp_register(int reg_value)
-{
-
-	struct cn_msg *msg;
-	struct hv_kvp_msg *kvp_msg;
-	char *version;
-
-	msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC);
+#define HV_DRV_VERSION           31
 
-	if (msg) {
-		kvp_msg = (struct hv_kvp_msg *)msg->data;
-		version = kvp_msg->body.kvp_register.version;
-		msg->id.idx =  CN_KVP_IDX;
-		msg->id.val = CN_KVP_VAL;
-
-		kvp_msg->kvp_hdr.operation = reg_value;
-		strcpy(version, HV_DRV_VERSION);
-		msg->len = sizeof(struct hv_kvp_msg);
-		cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
-		kfree(msg);
-	}
-}
 static void
 kvp_work_func(struct work_struct *dummy)
 {
@@ -124,7 +100,7 @@ kvp_work_func(struct work_struct *dummy)
 	 * If the timer fires, the user-mode component has not responded;
 	 * process the pending transaction.
 	 */
-	kvp_respond_to_host(NULL, HV_E_FAIL);
+	kvp_respond_to_host(HV_E_FAIL);
 }
 
 static void poll_channel(struct vmbus_channel *channel)
@@ -138,36 +114,26 @@ static void poll_channel(struct vmbus_channel *channel)
 }
 
 
-static int kvp_handle_handshake(struct hv_kvp_msg *msg)
+static int kvp_handle_handshake(u32 op)
 {
-	int ret = 1;
+	int ret = 0;
 
-	switch (msg->kvp_hdr.operation) {
+	switch (op) {
 	case KVP_OP_REGISTER:
-		dm_reg_value = KVP_OP_REGISTER;
+		kvp_device.dm_reg_value = KVP_OP_REGISTER;
 		pr_info("KVP: IP injection functionality not available\n");
 		pr_info("KVP: Upgrade the KVP daemon\n");
 		break;
 	case KVP_OP_REGISTER1:
-		dm_reg_value = KVP_OP_REGISTER1;
+		kvp_device.dm_reg_value = KVP_OP_REGISTER1;
 		break;
 	default:
 		pr_info("KVP: incompatible daemon\n");
 		pr_info("KVP: KVP version: %d, Daemon version: %d\n",
-			KVP_OP_REGISTER1, msg->kvp_hdr.operation);
-		ret = 0;
+			KVP_OP_REGISTER1, op);
+		ret = 1;
 	}
 
-	if (ret) {
-		/*
-		 * We have a compatible daemon; complete the handshake.
-		 */
-		pr_info("KVP: user-mode registering done.\n");
-		kvp_register(dm_reg_value);
-		kvp_transaction.active = false;
-		if (kvp_transaction.kvp_context)
-			poll_channel(kvp_transaction.kvp_context);
-	}
 	return ret;
 }
 
@@ -176,25 +142,11 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
  * Callback when data is received from user mode.
  */
 
-static void
-kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+static void kvp_userwrite_callback(void)
 {
-	struct hv_kvp_msg *message;
+	struct hv_kvp_msg *message = &kvp_device.user_msg;
 	struct hv_kvp_msg_enumerate *data;
-	int	error = 0;
-
-	message = (struct hv_kvp_msg *)msg->data;
-
-	/*
-	 * If we are negotiating the version information
-	 * with the daemon; handle that first.
-	 */
-
-	if (in_hand_shake) {
-		if (kvp_handle_handshake(message))
-			in_hand_shake = false;
-		return;
-	}
+	int error = 0;
 
 	/*
 	 * Based on the version of the daemon, we propagate errors from the
@@ -203,7 +155,7 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
 
 	data = &message->body.kvp_enum_data;
 
-	switch (dm_reg_value) {
+	switch (kvp_device.dm_reg_value) {
 	case KVP_OP_REGISTER:
 		/*
 		 * Null string is used to pass back error condition.
@@ -226,10 +178,9 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
 	 * to the host. But first, cancel the timeout.
 	 */
 	if (cancel_delayed_work_sync(&kvp_work))
-		kvp_respond_to_host(message, error);
+		kvp_respond_to_host(error);
 }
 
-
 static int process_ob_ipinfo(void *in_msg, void *out_msg, int op)
 {
 	struct hv_kvp_msg *in = in_msg;
@@ -337,32 +288,21 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
 	}
 }
 
-
-
-
 static void
 kvp_send_key(struct work_struct *dummy)
 {
-	struct cn_msg *msg;
-	struct hv_kvp_msg *message;
-	struct hv_kvp_msg *in_msg;
-	__u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation;
-	__u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool;
+	struct hv_kvp_msg *message = &kvp_device.user_msg;
+	struct hv_kvp_msg *in_msg = &kvp_device.host_msg;
+	__u8 operation = in_msg->kvp_hdr.operation;
+	__u8 pool = in_msg->kvp_hdr.pool;
 	__u32 val32;
 	__u64 val64;
-	int rc;
 
-	msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC);
-	if (!msg)
-		return;
-
-	msg->id.idx =  CN_KVP_IDX;
-	msg->id.val = CN_KVP_VAL;
+	mutex_lock(&kvp_device.lock);
 
-	message = (struct hv_kvp_msg *)msg->data;
+	memset(message, 0, sizeof(struct hv_kvp_msg));
 	message->kvp_hdr.operation = operation;
 	message->kvp_hdr.pool = pool;
-	in_msg = kvp_transaction.kvp_msg;
 
 	/*
 	 * The key/value strings sent from the host are encoded in
@@ -446,15 +386,10 @@ kvp_send_key(struct work_struct *dummy)
 			break;
 	}
 
-	msg->len = sizeof(struct hv_kvp_msg);
-	rc = cn_netlink_send(msg, 0, 0, GFP_ATOMIC);
-	if (rc) {
-		pr_debug("KVP: failed to communicate to the daemon: %d\n", rc);
-		if (cancel_delayed_work_sync(&kvp_work))
-			kvp_respond_to_host(message, HV_E_FAIL);
-	}
+	kvp_device.state = KVP_USERMSG_READY;
+	wake_up_interruptible(&kvp_device.proc_list);
 
-	kfree(msg);
+	mutex_unlock(&kvp_device.lock);
 
 	return;
 }
@@ -463,10 +398,10 @@ kvp_send_key(struct work_struct *dummy)
  * Send a response back to the host.
  */
 
-static void
-kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
+static void kvp_respond_to_host(int error)
 {
 	struct hv_kvp_msg  *kvp_msg;
+	struct hv_kvp_msg  *msg_to_host = &kvp_device.user_msg;
 	struct hv_kvp_exchg_msg_value  *kvp_data;
 	char	*key_name;
 	char	*value;
@@ -479,26 +414,13 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
 	int ret;
 
 	/*
-	 * If a transaction is not active; log and return.
-	 */
-
-	if (!kvp_transaction.active) {
-		/*
-		 * This is a spurious call!
-		 */
-		pr_warn("KVP: Transaction not active\n");
-		return;
-	}
-	/*
 	 * Copy the global state for completing the transaction. Note that
 	 * only one transaction can be active at a time.
 	 */
 
-	buf_len = kvp_transaction.recv_len;
-	channel = kvp_transaction.recv_channel;
-	req_id = kvp_transaction.recv_req_id;
-
-	kvp_transaction.active = false;
+	buf_len = kvp_device.recv_len;
+	channel = kvp_device.recv_channel;
+	req_id = kvp_device.recv_req_id;
 
 	icmsghdrp = (struct icmsg_hdr *)
 			&recv_buffer[sizeof(struct vmbuspipe_hdr)];
@@ -528,7 +450,8 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
 			&recv_buffer[sizeof(struct vmbuspipe_hdr) +
 			sizeof(struct icmsg_hdr)];
 
-	switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
+
+	switch (kvp_device.host_msg.kvp_hdr.operation) {
 	case KVP_OP_GET_IP_INFO:
 		ret = process_ob_ipinfo(msg_to_host,
 				 (struct hv_kvp_ip_msg *)kvp_msg,
@@ -586,6 +509,17 @@ response_done:
 
 	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 (kvp_device.state == KVP_USERSPACE_RECV ||
+	    kvp_device.state == KVP_USERSPACE_REQ)
+		kvp_device.state = KVP_READY;
+	/*
+	 * Make sure device state was set before polling the channel as
+	 * processing can happen on a different CPU.
+	 */
+	smp_mb();
+
 	poll_channel(channel);
 }
 
@@ -612,14 +546,15 @@ void hv_kvp_onchannelcallback(void *context)
 	int util_fw_version;
 	int kvp_srv_version;
 
-	if (kvp_transaction.active) {
+	if (kvp_device.state > KVP_READY) {
 		/*
 		 * We will defer processing this callback once
 		 * the current transaction is complete.
 		 */
-		kvp_transaction.kvp_context = context;
+		kvp_device.kvp_context = channel;
 		return;
 	}
+	kvp_device.kvp_context = NULL;
 
 	vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen,
 			 &requestid);
@@ -661,11 +596,19 @@ void hv_kvp_onchannelcallback(void *context)
 			 * transaction; note transactions are serialized.
 			 */
 
-			kvp_transaction.recv_len = recvlen;
-			kvp_transaction.recv_channel = channel;
-			kvp_transaction.recv_req_id = requestid;
-			kvp_transaction.active = true;
-			kvp_transaction.kvp_msg = kvp_msg;
+			kvp_device.recv_len = recvlen;
+			kvp_device.recv_channel = channel;
+			kvp_device.recv_req_id = requestid;
+
+			if (kvp_device.state != KVP_READY) {
+				/* Userspace daemon is not connected, fail. */
+				kvp_respond_to_host(HV_E_FAIL);
+				return;
+			}
+
+			kvp_device.state = KVP_HOSTMSG_RECEIVED;
+			memcpy(&kvp_device.host_msg, kvp_msg,
+			       sizeof(struct hv_kvp_msg));
 
 			/*
 			 * Get the information from the
@@ -690,17 +633,166 @@ void hv_kvp_onchannelcallback(void *context)
 				       recvlen, requestid,
 				       VM_PKT_DATA_INBAND, 0);
 	}
+}
+
+static int kvp_op_open(struct inode *inode, struct file *f)
+{
+	if (kvp_device.state != KVP_DEVICE_INITIALIZING)
+		return -EBUSY;
+	kvp_device.state = KVP_DEVICE_OPENED;
+	return 0;
+}
+
+static int kvp_op_release(struct inode *inode, struct file *f)
+{
+	kvp_device.state = KVP_DEVICE_INITIALIZING;
+	return 0;
+}
+
+static ssize_t kvp_op_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	int ret = 0;
+
+	if (kvp_device.state == KVP_DEVICE_DYING)
+		return -EFAULT;
+
+	if (count != sizeof(struct hv_kvp_msg)) {
+		pr_warn("kvp_op_write: invalid write len: %d (expected: %d)\n",
+			(int)count, (int)sizeof(struct hv_kvp_msg));
+		return -EINVAL;
+	}
 
+	mutex_lock(&kvp_device.lock);
+
+	if (kvp_device.state == KVP_USERSPACE_REQ) {
+		if (!copy_from_user(&kvp_device.user_msg, buf,
+				    sizeof(struct hv_kvp_msg))) {
+			kvp_device.state = KVP_USERSPACE_RECV;
+			kvp_userwrite_callback();
+			ret = sizeof(struct hv_kvp_msg);
+		} else
+			ret = -EFAULT;
+	} else {
+		pr_warn("kvp_op_write: invalid transaction state: %d\n",
+			kvp_device.state);
+		ret = -EINVAL;
+	}
+
+	mutex_unlock(&kvp_device.lock);
+	return ret;
 }
 
+static ssize_t kvp_op_read(struct file *file, char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	ssize_t ret = 0;
+
+	if (kvp_device.state == KVP_DEVICE_DYING)
+		return -EFAULT;
+
+	if (count != sizeof(struct hv_kvp_msg)) {
+		pr_warn("kvp_op_read: invalid read len: %d (expected: %d)\n",
+			(int)count, (int)sizeof(struct hv_kvp_msg));
+		return -EINVAL;
+	}
+
+	if (wait_event_interruptible(kvp_device.proc_list,
+				     kvp_device.state == KVP_USERMSG_READY ||
+				     kvp_device.state == KVP_DEVICE_DYING))
+		return -EFAULT;
+
+	if (kvp_device.state != KVP_USERMSG_READY)
+		return -EFAULT;
+
+	mutex_lock(&kvp_device.lock);
+
+	if (!copy_to_user(buf, &kvp_device.user_msg,
+			  sizeof(struct hv_kvp_msg))) {
+		kvp_device.state = KVP_USERSPACE_REQ;
+		ret = sizeof(struct hv_kvp_msg);
+	} else
+		ret = -EFAULT;
+
+	mutex_unlock(&kvp_device.lock);
+	return ret;
+}
+
+static unsigned int kvp_op_poll(struct file *file, poll_table *wait)
+{
+	if (kvp_device.state == KVP_DEVICE_DYING)
+		return -EFAULT;
+
+	poll_wait(file, &kvp_device.proc_list, wait);
+	if (kvp_device.state == KVP_USERMSG_READY)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static long kvp_op_ioctl(struct file *fp,
+			 unsigned int cmd, unsigned long arg)
+{
+	long ret = 0;
+	void __user *argp = (void __user *)arg;
+	u32 val32;
+
+	if (kvp_device.state == KVP_DEVICE_DYING)
+		return -EFAULT;
+
+	/* The only ioctl we have is registation */
+	if (kvp_device.state != KVP_DEVICE_OPENED)
+		return -EINVAL;
+
+	mutex_lock(&kvp_device.lock);
+
+	switch (cmd) {
+	case HYPERV_KVP_REGISTER:
+		if (copy_from_user(&val32, argp, sizeof(val32))) {
+			ret = -EFAULT;
+			break;
+		}
+		if (!kvp_handle_handshake(val32)) {
+			val32 = (u32)HV_DRV_VERSION;
+			if (copy_to_user(argp, &val32, sizeof(val32))) {
+				ret = -EFAULT;
+				break;
+			}
+			kvp_device.state = KVP_READY;
+			pr_info("KVP: user-mode registering done.\n");
+			if (kvp_device.kvp_context)
+				poll_channel(kvp_device.kvp_context);
+		} else
+			ret = -EINVAL;
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	mutex_unlock(&kvp_device.lock);
+	return ret;
+}
+
+static const struct file_operations kvp_fops = {
+	.owner		= THIS_MODULE,
+	.read           = kvp_op_read,
+	.write          = kvp_op_write,
+	.release	= kvp_op_release,
+	.open		= kvp_op_open,
+	.poll		= kvp_op_poll,
+	.unlocked_ioctl = kvp_op_ioctl
+};
+
+static struct miscdevice kvp_misc = {
+	.minor          = MISC_DYNAMIC_MINOR,
+	.name           = "vmbus/hv_kvp",
+	.fops           = &kvp_fops,
+};
+
 int
 hv_kvp_init(struct hv_util_service *srv)
 {
-	int err;
-
-	err = cn_add_callback(&kvp_id, kvp_name, kvp_cn_callback);
-	if (err)
-		return err;
 	recv_buffer = srv->recv_buffer;
 
 	/*
@@ -709,14 +801,20 @@ hv_kvp_init(struct hv_util_service *srv)
 	 * Defer processing channel callbacks until the daemon
 	 * has registered.
 	 */
-	kvp_transaction.active = true;
+	kvp_device.state = KVP_DEVICE_INITIALIZING;
+	init_waitqueue_head(&kvp_device.proc_list);
+	mutex_init(&kvp_device.lock);
 
-	return 0;
+	return misc_register(&kvp_misc);
 }
 
 void hv_kvp_deinit(void)
 {
-	cn_del_callback(&kvp_id);
+	kvp_device.state = KVP_DEVICE_DYING;
+	/* Make sure nobody sees the old state */
+	smp_mb();
+	wake_up_interruptible(&kvp_device.proc_list);
 	cancel_delayed_work_sync(&kvp_work);
 	cancel_work_sync(&kvp_sendkey_work);
+	misc_deregister(&kvp_misc);
 }
diff --git a/include/uapi/linux/hyperv.h b/include/uapi/linux/hyperv.h
index bb1cb73..80713a3 100644
--- a/include/uapi/linux/hyperv.h
+++ b/include/uapi/linux/hyperv.h
@@ -26,6 +26,7 @@
 #define _UAPI_HYPERV_H
 
 #include <linux/uuid.h>
+#include <linux/types.h>
 
 /*
  * Framework version for util services.
@@ -389,4 +390,11 @@ struct hv_kvp_ip_msg {
 	struct hv_kvp_ipaddr_value      kvp_ip_val;
 } __attribute__((packed));
 
+/*
+ * Userspace registration ioctls. Userspace daemons are supposed to pass their
+ * version as a parameter and get driver version back. KVP daemon supplies
+ * either KVP_OP_REGISTER or KVP_OP_REGISTER1.
+ */
+#define HYPERV_KVP_REGISTER        _IOWR('v', 0, __u32)
+
 #endif /* _UAPI_HYPERV_H */
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index 408bb07..0c3cac7 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -33,7 +33,6 @@
 #include <ctype.h>
 #include <errno.h>
 #include <arpa/inet.h>
-#include <linux/connector.h>
 #include <linux/hyperv.h>
 #include <linux/netlink.h>
 #include <ifaddrs.h>
@@ -44,6 +43,7 @@
 #include <dirent.h>
 #include <net/if.h>
 #include <getopt.h>
+#include <sys/ioctl.h>
 
 /*
  * KVP protocol: The user mode component first registers with the
@@ -79,9 +79,6 @@ enum {
 	DNS
 };
 
-static struct sockaddr_nl addr;
-static int in_hand_shake = 1;
-
 static char *os_name = "";
 static char *os_major = "";
 static char *os_minor = "";
@@ -1387,34 +1384,6 @@ kvp_get_domain_name(char *buffer, int length)
 	freeaddrinfo(info);
 }
 
-static int
-netlink_send(int fd, struct cn_msg *msg)
-{
-	struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
-	unsigned int size;
-	struct msghdr message;
-	struct iovec iov[2];
-
-	size = sizeof(struct cn_msg) + msg->len;
-
-	nlh.nlmsg_pid = getpid();
-	nlh.nlmsg_len = NLMSG_LENGTH(size);
-
-	iov[0].iov_base = &nlh;
-	iov[0].iov_len = sizeof(nlh);
-
-	iov[1].iov_base = msg;
-	iov[1].iov_len = size;
-
-	memset(&message, 0, sizeof(message));
-	message.msg_name = &addr;
-	message.msg_namelen = sizeof(addr);
-	message.msg_iov = iov;
-	message.msg_iovlen = 2;
-
-	return sendmsg(fd, &message, 0);
-}
-
 void print_usage(char *argv[])
 {
 	fprintf(stderr, "Usage: %s [options]\n"
@@ -1425,23 +1394,18 @@ void print_usage(char *argv[])
 
 int main(int argc, char *argv[])
 {
-	int fd, len, nl_group;
+	int kvp_fd, len;
 	int error;
-	struct cn_msg *message;
 	struct pollfd pfd;
-	struct nlmsghdr *incoming_msg;
-	struct cn_msg	*incoming_cn_msg;
-	struct hv_kvp_msg *hv_msg;
-	char	*p;
+	struct hv_kvp_msg hv_msg[1];
 	char	*key_value;
 	char	*key_name;
 	int	op;
 	int	pool;
 	char	*if_name;
 	struct hv_kvp_ipaddr_value *kvp_ip_val;
-	char *kvp_recv_buffer;
-	size_t kvp_recv_buffer_len;
 	int daemonize = 1, long_index = 0, opt;
+	__u32 daemon_ver = (__u32)KVP_OP_REGISTER1;
 
 	static struct option long_options[] = {
 		{"help",	no_argument,	   0,  'h' },
@@ -1468,12 +1432,14 @@ int main(int argc, char *argv[])
 	openlog("KVP", 0, LOG_USER);
 	syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
 
-	kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
-	kvp_recv_buffer = calloc(1, kvp_recv_buffer_len);
-	if (!kvp_recv_buffer) {
-		syslog(LOG_ERR, "Failed to allocate netlink buffer");
+	kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR);
+
+	if (kvp_fd < 0) {
+		syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s",
+			errno, strerror(errno));
 		exit(EXIT_FAILURE);
 	}
+
 	/*
 	 * Retrieve OS release information.
 	 */
@@ -1489,100 +1455,44 @@ int main(int argc, char *argv[])
 		exit(EXIT_FAILURE);
 	}
 
-	fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
-	if (fd < 0) {
-		syslog(LOG_ERR, "netlink socket creation failed; error: %d %s", errno,
-				strerror(errno));
-		exit(EXIT_FAILURE);
-	}
-	addr.nl_family = AF_NETLINK;
-	addr.nl_pad = 0;
-	addr.nl_pid = 0;
-	addr.nl_groups = 0;
-
-
-	error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
-	if (error < 0) {
-		syslog(LOG_ERR, "bind failed; error: %d %s", errno, strerror(errno));
-		close(fd);
-		exit(EXIT_FAILURE);
-	}
-	nl_group = CN_KVP_IDX;
-
-	if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) {
-		syslog(LOG_ERR, "setsockopt failed; error: %d %s", errno, strerror(errno));
-		close(fd);
-		exit(EXIT_FAILURE);
-	}
-
 	/*
 	 * Register ourselves with the kernel.
 	 */
-	message = (struct cn_msg *)kvp_recv_buffer;
-	message->id.idx = CN_KVP_IDX;
-	message->id.val = CN_KVP_VAL;
-
-	hv_msg = (struct hv_kvp_msg *)message->data;
-	hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
-	message->ack = 0;
-	message->len = sizeof(struct hv_kvp_msg);
-
-	len = netlink_send(fd, message);
-	if (len < 0) {
-		syslog(LOG_ERR, "netlink_send failed; error: %d %s", errno, strerror(errno));
-		close(fd);
+	if (ioctl(kvp_fd, HYPERV_KVP_REGISTER, &daemon_ver)) {
+		syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
+		       errno, strerror(errno));
+		close(kvp_fd);
 		exit(EXIT_FAILURE);
 	}
 
-	pfd.fd = fd;
+	syslog(LOG_INFO, "KVP LIC Version: %d", daemon_ver);
+
+	pfd.fd = kvp_fd;
 
 	while (1) {
-		struct sockaddr *addr_p = (struct sockaddr *) &addr;
-		socklen_t addr_l = sizeof(addr);
 		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(fd);
+				close(kvp_fd);
 				exit(EXIT_FAILURE);
 			}
 			else
 				continue;
 		}
 
-		len = recvfrom(fd, kvp_recv_buffer, kvp_recv_buffer_len, 0,
-				addr_p, &addr_l);
-
-		if (len < 0) {
-			int saved_errno = errno;
-			syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
-					addr.nl_pid, errno, strerror(errno));
-
-			if (saved_errno == ENOBUFS) {
-				syslog(LOG_ERR, "receive error: ignored");
-				continue;
-			}
+		len = read(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
 
-			close(fd);
-			return -1;
-		}
+		if (len != sizeof(struct hv_kvp_msg)) {
+			syslog(LOG_ERR, "read failed; error:%d %s",
+			       errno, strerror(errno));
 
-		if (addr.nl_pid) {
-			syslog(LOG_WARNING, "Received packet from untrusted pid:%u",
-					addr.nl_pid);
-			continue;
+			close(kvp_fd);
+			return EXIT_FAILURE;
 		}
 
-		incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
-
-		if (incoming_msg->nlmsg_type != NLMSG_DONE)
-			continue;
-
-		incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
-		hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
-
 		/*
 		 * We will use the KVP header information to pass back
 		 * the error from this daemon. So, first copy the state
@@ -1592,24 +1502,6 @@ int main(int argc, char *argv[])
 		pool = hv_msg->kvp_hdr.pool;
 		hv_msg->error = HV_S_OK;
 
-		if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
-			/*
-			 * Driver is registering with us; stash away the version
-			 * information.
-			 */
-			in_hand_shake = 0;
-			p = (char *)hv_msg->body.kvp_register.version;
-			lic_version = malloc(strlen(p) + 1);
-			if (lic_version) {
-				strcpy(lic_version, p);
-				syslog(LOG_INFO, "KVP LIC Version: %s",
-					lic_version);
-			} else {
-				syslog(LOG_ERR, "malloc failed");
-			}
-			continue;
-		}
-
 		switch (op) {
 		case KVP_OP_GET_IP_INFO:
 			kvp_ip_val = &hv_msg->body.kvp_ip_val;
@@ -1702,7 +1594,6 @@ int main(int argc, char *argv[])
 			goto kvp_done;
 		}
 
-		hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
 		key_name = (char *)hv_msg->body.kvp_enum_data.data.key;
 		key_value = (char *)hv_msg->body.kvp_enum_data.data.value;
 
@@ -1753,31 +1644,17 @@ int main(int argc, char *argv[])
 			hv_msg->error = HV_S_CONT;
 			break;
 		}
-		/*
-		 * Send the value back to the kernel. The response is
-		 * already in the receive buffer. Update the cn_msg header to
-		 * reflect the key value that has been added to the message
-		 */
-kvp_done:
-
-		incoming_cn_msg->id.idx = CN_KVP_IDX;
-		incoming_cn_msg->id.val = CN_KVP_VAL;
-		incoming_cn_msg->ack = 0;
-		incoming_cn_msg->len = sizeof(struct hv_kvp_msg);
-
-		len = netlink_send(fd, incoming_cn_msg);
-		if (len < 0) {
-			int saved_errno = errno;
-			syslog(LOG_ERR, "net_link send failed; error: %d %s", errno,
-					strerror(errno));
-
-			if (saved_errno == ENOMEM || saved_errno == ENOBUFS) {
-				syslog(LOG_ERR, "send error: ignored");
-				continue;
-			}
 
+		/* Send the value back to the kernel. */
+kvp_done:
+		len = write(kvp_fd, hv_msg, sizeof(struct hv_kvp_msg));
+		if (len != sizeof(struct hv_kvp_msg)) {
+			syslog(LOG_ERR, "write failed; error: %d %s", errno,
+			       strerror(errno));
 			exit(EXIT_FAILURE);
 		}
 	}
 
+	close(kvp_fd);
+	exit(0);
 }
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux