LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: Vitaly Kuznetsov <vkuznets@redhat.com>
To: "K. Y. Srinivasan" <kys@microsoft.com>, devel@linuxdriverproject.org
Cc: Haiyang Zhang <haiyangz@microsoft.com>,
	linux-kernel@vger.kernel.org, Dexuan Cui <decui@microsoft.com>,
	Radim Krcmar <rkrcmar@redhat.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	linux-api@vger.kernel.org
Subject: [PATCH RFCv2 13/21] Drivers: hv: util: introduce hv_utils_transport abstraction
Date: Wed, 11 Mar 2015 14:29:26 +0100	[thread overview]
Message-ID: <1426080574-9011-14-git-send-email-vkuznets@redhat.com> (raw)
In-Reply-To: <1426080574-9011-1-git-send-email-vkuznets@redhat.com>

The intention is to make KVP/VSS drivers work through misc char devices.
Introduce an abstraction for kernel/userspace communication to make the
migration smoother. Transport operational mode (netlink or char device)
is determined by the first received message. To support driver upgrades
the switch from netlink to chardev operational mode is supported.

Every hv_util daemon is supposed to register 2 callbacks:
1) on_msg() to get notified when the userspace daemon sent a message;
2) on_reset() to get notified when the userspace daemon drops the connection.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
---
 drivers/hv/Makefile             |   2 +-
 drivers/hv/hv_utils_transport.c | 276 ++++++++++++++++++++++++++++++++++++++++
 drivers/hv/hv_utils_transport.h |  51 ++++++++
 3 files changed, 328 insertions(+), 1 deletion(-)
 create mode 100644 drivers/hv/hv_utils_transport.c
 create mode 100644 drivers/hv/hv_utils_transport.h

diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 5e4dfa4..39c9b2c 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON)	+= hv_balloon.o
 hv_vmbus-y := vmbus_drv.o \
 		 hv.o connection.o channel.o \
 		 channel_mgmt.o ring_buffer.o
-hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o
+hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c
new file mode 100644
index 0000000..ea7ba5e
--- /dev/null
+++ b/drivers/hv/hv_utils_transport.c
@@ -0,0 +1,276 @@
+/*
+ * Kernel/userspace transport abstraction for Hyper-V util driver.
+ *
+ * Copyright (C) 2015, Vitaly Kuznetsov <vkuznets@redhat.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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+
+#include "hyperv_vmbus.h"
+#include "hv_utils_transport.h"
+
+static DEFINE_SPINLOCK(hvt_list_lock);
+static struct list_head hvt_list = LIST_HEAD_INIT(hvt_list);
+
+static void hvt_reset(struct hvutil_transport *hvt)
+{
+	mutex_lock(&hvt->outmsg_lock);
+	kfree(hvt->outmsg);
+	hvt->outmsg = NULL;
+	hvt->outmsg_len = 0;
+	mutex_unlock(&hvt->outmsg_lock);
+	if (hvt->on_reset)
+		hvt->on_reset();
+}
+
+static ssize_t hvt_op_read(struct file *file, char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct hvutil_transport *hvt;
+	int ret;
+
+	hvt = container_of(file->f_op, struct hvutil_transport, fops);
+
+	if (wait_event_interruptible(hvt->outmsg_q, hvt->outmsg_len > 0))
+		return -EINTR;
+
+	mutex_lock(&hvt->outmsg_lock);
+	if (!hvt->outmsg) {
+		ret = -EAGAIN;
+		goto out_unlock;
+	}
+
+	if (count < hvt->outmsg_len) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (!copy_to_user(buf, hvt->outmsg, hvt->outmsg_len))
+		ret = hvt->outmsg_len;
+	else
+		ret = -EFAULT;
+
+	kfree(hvt->outmsg);
+	hvt->outmsg = NULL;
+	hvt->outmsg_len = 0;
+
+out_unlock:
+	mutex_unlock(&hvt->outmsg_lock);
+	return ret;
+}
+
+static ssize_t hvt_op_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	struct hvutil_transport *hvt;
+	u8 *inmsg;
+
+	hvt = container_of(file->f_op, struct hvutil_transport, fops);
+
+	inmsg = kzalloc(count, GFP_KERNEL);
+	if (copy_from_user(inmsg, buf, count)) {
+		kfree(inmsg);
+		return -EFAULT;
+	}
+	if (hvt->on_msg(inmsg, count))
+		return -EFAULT;
+	kfree(inmsg);
+
+	return count;
+}
+
+static unsigned int hvt_op_poll(struct file *file, poll_table *wait)
+{
+	struct hvutil_transport *hvt;
+
+	hvt = container_of(file->f_op, struct hvutil_transport, fops);
+
+	poll_wait(file, &hvt->outmsg_q, wait);
+	if (hvt->outmsg_len > 0)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static int hvt_op_open(struct inode *inode, struct file *file)
+{
+	struct hvutil_transport *hvt;
+
+	hvt = container_of(file->f_op, struct hvutil_transport, fops);
+
+	/*
+	 * Switching to CHARDEV mode. We switch bach to INIT when device
+	 * gets released.
+	 */
+	if (hvt->mode == HVUTIL_TRANSPORT_INIT)
+		hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
+	else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
+		/*
+		 * We're switching from netlink communication to using char
+		 * device. Issue the reset first.
+		 */
+		hvt_reset(hvt);
+		hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
+	} else
+		return -EBUSY;
+
+	return 0;
+}
+
+static int hvt_op_release(struct inode *inode, struct file *file)
+{
+	struct hvutil_transport *hvt;
+
+	hvt = container_of(file->f_op, struct hvutil_transport, fops);
+
+	hvt->mode = HVUTIL_TRANSPORT_INIT;
+	/*
+	 * Cleanup message buffers to avoid spurious messages when the daemon
+	 * connects back.
+	 */
+	hvt_reset(hvt);
+
+	return 0;
+}
+
+static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+{
+	struct hvutil_transport *hvt, *hvt_found = NULL;
+
+	spin_lock(&hvt_list_lock);
+	list_for_each_entry(hvt, &hvt_list, list) {
+		if (hvt->cn_id.idx == msg->id.idx &&
+		    hvt->cn_id.val == msg->id.val) {
+			hvt_found = hvt;
+			break;
+		}
+	}
+	spin_unlock(&hvt_list_lock);
+	if (!hvt_found) {
+		pr_warn("hvt_cn_callback: spurious message received!\n");
+		return;
+	}
+
+	/*
+	 * Switching to NETLINK mode. Switching to CHARDEV happens when someone
+	 * opens the device.
+	 */
+	if (hvt->mode == HVUTIL_TRANSPORT_INIT)
+		hvt->mode = HVUTIL_TRANSPORT_NETLINK;
+
+	if (hvt->mode == HVUTIL_TRANSPORT_NETLINK)
+		hvt_found->on_msg(msg->data, msg->len);
+	else
+		pr_warn("hvt_cn_callback: unexpected netlink message!\n");
+}
+
+int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
+{
+	struct cn_msg *cn_msg;
+	int ret = 0;
+
+	if (hvt->mode == HVUTIL_TRANSPORT_INIT) {
+		return -EINVAL;
+	} else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
+		cn_msg = kzalloc(sizeof(*cn_msg) + len, GFP_ATOMIC);
+		if (!msg)
+			return -ENOMEM;
+		cn_msg->id.idx = hvt->cn_id.idx;
+		cn_msg->id.val = hvt->cn_id.val;
+		cn_msg->len = len;
+		memcpy(cn_msg->data, msg, len);
+		ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC);
+		kfree(cn_msg);
+		return ret;
+	}
+	/* HVUTIL_TRANSPORT_CHARDEV */
+	mutex_lock(&hvt->outmsg_lock);
+	if (hvt->outmsg) {
+		/* Previous message wasn't received */
+		ret = -EFAULT;
+		goto out_unlock;
+	}
+	hvt->outmsg = kzalloc(len, GFP_KERNEL);
+	memcpy(hvt->outmsg, msg, len);
+	hvt->outmsg_len = len;
+	wake_up_interruptible(&hvt->outmsg_q);
+out_unlock:
+	mutex_unlock(&hvt->outmsg_lock);
+	return ret;
+}
+
+struct hvutil_transport *hvutil_transport_init(const char *name,
+					       u32 cn_idx, u32 cn_val,
+					       int (*on_msg)(void *, int),
+					       void (*on_reset)(void))
+{
+	struct hvutil_transport *hvt;
+
+	hvt = kzalloc(sizeof(*hvt), GFP_KERNEL);
+	if (!hvt)
+		return NULL;
+
+	hvt->cn_id.idx = cn_idx;
+	hvt->cn_id.val = cn_val;
+
+	hvt->mdev.minor = MISC_DYNAMIC_MINOR;
+	hvt->mdev.name = name;
+
+	hvt->fops.owner = THIS_MODULE;
+	hvt->fops.read = hvt_op_read;
+	hvt->fops.write = hvt_op_write;
+	hvt->fops.poll = hvt_op_poll;
+	hvt->fops.open = hvt_op_open;
+	hvt->fops.release = hvt_op_release;
+
+	hvt->mdev.fops = &hvt->fops;
+
+	init_waitqueue_head(&hvt->outmsg_q);
+	mutex_init(&hvt->outmsg_lock);
+
+	spin_lock(&hvt_list_lock);
+	list_add(&hvt->list, &hvt_list);
+	spin_unlock(&hvt_list_lock);
+
+	hvt->on_msg = on_msg;
+	hvt->on_reset = on_reset;
+
+	if (misc_register(&hvt->mdev))
+		goto err_free_hvt;
+
+	/* Use cn_id.idx/cn_id.val to determine if we need to setup netlink */
+	if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0 &&
+	    cn_add_callback(&hvt->cn_id, name, hvt_cn_callback))
+		goto err_free_hvt;
+
+	return hvt;
+
+err_free_hvt:
+	kfree(hvt);
+	return NULL;
+}
+
+void hvutil_transport_destroy(struct hvutil_transport *hvt)
+{
+	spin_lock(&hvt_list_lock);
+	list_del(&hvt->list);
+	spin_unlock(&hvt_list_lock);
+	if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0)
+		cn_del_callback(&hvt->cn_id);
+	misc_deregister(&hvt->mdev);
+	kfree(hvt->outmsg);
+	kfree(hvt);
+}
diff --git a/drivers/hv/hv_utils_transport.h b/drivers/hv/hv_utils_transport.h
new file mode 100644
index 0000000..314c76c
--- /dev/null
+++ b/drivers/hv/hv_utils_transport.h
@@ -0,0 +1,51 @@
+/*
+ * Kernel/userspace transport abstraction for Hyper-V util driver.
+ *
+ * Copyright (C) 2015, Vitaly Kuznetsov <vkuznets@redhat.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.
+ *
+ */
+
+#ifndef _HV_UTILS_TRANSPORT_H
+#define _HV_UTILS_TRANSPORT_H
+
+#include <linux/connector.h>
+#include <linux/miscdevice.h>
+
+enum hvutil_transport_mode {
+	HVUTIL_TRANSPORT_INIT = 0,
+	HVUTIL_TRANSPORT_NETLINK,
+	HVUTIL_TRANSPORT_CHARDEV,
+};
+
+struct hvutil_transport {
+	int mode;                           /* hvutil_transport_mode */
+	struct file_operations fops;        /* file operations */
+	struct miscdevice mdev;             /* misc device */
+	struct cb_id cn_id;                 /* CN_*_IDX/CN_*_VAL */
+	struct list_head list;              /* hvt_list */
+	int (*on_msg)(void *, int);         /* callback on new user message */
+	void (*on_reset)(void);             /* callback when userspace drops */
+	u8 *outmsg;                         /* message to the userspace */
+	int outmsg_len;                     /* its length */
+	wait_queue_head_t outmsg_q;         /* poll/read wait queue */
+	struct mutex outmsg_lock;           /* protects outmsg */
+};
+
+struct hvutil_transport *hvutil_transport_init(const char *name,
+					       u32 cn_idx, u32 cn_val,
+					       int (*on_msg)(void *, int),
+					       void (*on_reset)(void));
+int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len);
+void hvutil_transport_destroy(struct hvutil_transport *hvt);
+
+#endif /* _HV_UTILS_TRANSPORT_H */
-- 
1.9.3


  parent reply	other threads:[~2015-03-11 13:30 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-11 13:29 [PATCH RFCv2 00/21] Drivers: hv: utils: re-implement the kernel/userspace communication layer Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 01/21] Drivers: hv: util: move kvp/vss function declarations to hyperv_vmbus.h Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 02/21] Drivers: hv: kvp: reset kvp_context Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 03/21] Drivers: hv: kvp: move poll_channel() to hyperv_vmbus.h Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 04/21] Drivers: hv: fcopy: process deferred messages when we complete the transaction Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 05/21] Drivers: hv: vss: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 06/21] Drivers: hv: kvp: rename kvp_work -> kvp_timeout_work Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 07/21] Drivers: hv: fcopy: rename fcopy_work -> fcopy_timeout_work Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 08/21] Drivers: hv: util: introduce state machine for util drivers Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 09/21] Drivers: hv: kvp: switch to using the hvutil_device_state state machine Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 10/21] Drivers: hv: vss: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 11/21] Drivers: hv: fcopy: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 12/21] Drivers: hv: fcopy: set .owner reference for file operations Vitaly Kuznetsov
2015-03-11 13:29 ` Vitaly Kuznetsov [this message]
2015-03-11 13:29 ` [PATCH RFCv2 14/21] Drivers: hv: vss: convert to hv_utils_transport Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 15/21] Drivers: hv: fcopy: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 16/21] Drivers: hv: kvp: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 17/21] Tools: hv: kvp: use misc char device to communicate with kernel Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 18/21] Tools: hv: vss: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 19/21] Drivers: hv: vss: full handshake support Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 20/21] Drivers: hv: fcopy: " Vitaly Kuznetsov
2015-03-11 13:29 ` [PATCH RFCv2 21/21] Drivers: hv: utils: unify driver registration reporting Vitaly Kuznetsov
2015-03-16 18:15 ` [PATCH RFCv2 00/21] Drivers: hv: utils: re-implement the kernel/userspace communication layer KY Srinivasan
2015-04-08 16:00   ` Vitaly Kuznetsov
2015-04-08 16:15     ` KY Srinivasan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1426080574-9011-14-git-send-email-vkuznets@redhat.com \
    --to=vkuznets@redhat.com \
    --cc=decui@microsoft.com \
    --cc=devel@linuxdriverproject.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=haiyangz@microsoft.com \
    --cc=kys@microsoft.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rkrcmar@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).