From: Stephen Hemminger <sthem...@microsoft.com>

This is a staging driver to enable userspace networking on VMBus.
Hyper-V does not support guest IOMMU, so this is an alternative
to allow for applications using DPDK.

This is based on earlier GPL driver developed for DPDK by Brocade.

Signed-off-by: Stephen Hemminger <sthem...@microsoft.com>
---
 drivers/hv/connection.c         |   1 +
 drivers/staging/Kconfig         |   2 +
 drivers/staging/Makefile        |   1 +
 drivers/staging/hv_uio/Kconfig  |   6 ++
 drivers/staging/hv_uio/Makefile |   4 +
 drivers/staging/hv_uio/hv_uio.c | 222 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 236 insertions(+)
 create mode 100644 drivers/staging/hv_uio/Kconfig
 create mode 100644 drivers/staging/hv_uio/Makefile
 create mode 100644 drivers/staging/hv_uio/hv_uio.c

diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 78e6368..6ce8b87 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -39,6 +39,7 @@ struct vmbus_connection vmbus_connection = {
        .conn_state             = DISCONNECTED,
        .next_gpadl_handle      = ATOMIC_INIT(0xE1E10),
 };
+EXPORT_SYMBOL_GPL(vmbus_connection);
 
 /*
  * Negotiated protocol version with the host.
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 58a7b35..fd31191 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -106,4 +106,6 @@ source "drivers/staging/greybus/Kconfig"
 
 source "drivers/staging/vc04_services/Kconfig"
 
+source "drivers/staging/hv_uio/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 2fa9745..ce6bd66 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_ISDN_I4L)                += i4l/
 obj-$(CONFIG_KS7010)           += ks7010/
 obj-$(CONFIG_GREYBUS)          += greybus/
 obj-$(CONFIG_BCM2708_VCHIQ)    += vc04_services/
+obj-$(CONFIG_HV_UIO)           += hv_uio/
diff --git a/drivers/staging/hv_uio/Kconfig b/drivers/staging/hv_uio/Kconfig
new file mode 100644
index 0000000..a2b1eb2
--- /dev/null
+++ b/drivers/staging/hv_uio/Kconfig
@@ -0,0 +1,6 @@
+config HV_UIO
+       tristate "Microsoft Hyper-V UIO driver"
+       depends on HYPERV_NET && UIO
+       help
+         Userspace interface for Hyper-V VMBus.
+         Can be used with DPDK to provide access to network device.
diff --git a/drivers/staging/hv_uio/Makefile b/drivers/staging/hv_uio/Makefile
new file mode 100644
index 0000000..65483b3
--- /dev/null
+++ b/drivers/staging/hv_uio/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for Hyper UIO driver
+#
+obj-$(CONFIG_HV_UIO) += hv_uio.o
diff --git a/drivers/staging/hv_uio/hv_uio.c b/drivers/staging/hv_uio/hv_uio.c
new file mode 100644
index 0000000..4996454
--- /dev/null
+++ b/drivers/staging/hv_uio/hv_uio.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
+ * Copyright (c) 2016, Microsoft Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uio_driver.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/hyperv.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include "../../hv/hyperv_vmbus.h"
+
+/*
+ * List of resources to be mapped to user space
+ * can be extended up to MAX_UIO_MAPS(5) items
+ */
+enum hv_uio_map {
+       TXRX_RING_MAP = 0,
+       INT_PAGE_MAP,
+       MON_PAGE_MAP,
+};
+
+#define HV_RING_SIZE   512
+
+struct hv_uio_private_data {
+       struct uio_info info;
+       struct hv_device *device;
+};
+
+static int
+hv_uio_mmap(struct uio_info *info, struct vm_area_struct *vma)
+{
+       int mi;
+
+       if (vma->vm_pgoff >= MAX_UIO_MAPS)
+               return -EINVAL;
+
+       if (info->mem[vma->vm_pgoff].size == 0)
+               return -EINVAL;
+
+       mi = (int)vma->vm_pgoff;
+
+       return remap_pfn_range(vma, vma->vm_start,
+                              virt_to_phys((void *)info->mem[mi].addr) >> 
PAGE_SHIFT,
+                              vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+/*
+ * This is the irqcontrol callback to be registered to uio_info.
+ * It can be used to disable/enable interrupt from user space processes.
+ *
+ * @param info
+ *  pointer to uio_info.
+ * @param irq_state
+ *  state value. 1 to enable interrupt, 0 to disable interrupt.
+ */
+static int
+hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
+{
+       struct hv_uio_private_data *pdata = info->priv;
+       struct hv_device *dev = pdata->device;
+
+       dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state;
+       virt_mb();
+
+       return 0;
+}
+
+/*
+ * Callback from vmbus_event when something is in inbound ring.
+ */
+static void hv_uio_channel_cb(void *context)
+{
+       struct hv_uio_private_data *pdata = context;
+       struct hv_device *dev = pdata->device;
+
+       dev->channel->inbound.ring_buffer->interrupt_mask = 1;
+       virt_mb();
+
+       uio_event_notify(&pdata->info);
+}
+
+static int
+hv_uio_probe(struct hv_device *dev,
+            const struct hv_vmbus_device_id *dev_id)
+{
+       struct hv_uio_private_data *pdata;
+       int ret;
+
+       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       dev->channel->inbound.ring_buffer->interrupt_mask = 1;
+       dev->channel->batched_reading = false;
+
+       ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE,
+                        HV_RING_SIZE * PAGE_SIZE, NULL, 0,
+                        hv_uio_channel_cb, pdata);
+       if (ret)
+               goto fail;
+
+       /* Fill general uio info */
+       pdata->info.name = "hv_uio";
+       pdata->info.version = "0.1";
+       pdata->info.irqcontrol = hv_uio_irqcontrol;
+       pdata->info.mmap = hv_uio_mmap;
+       pdata->info.irq = UIO_IRQ_CUSTOM;
+
+       /* mem resources */
+       pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings";
+       pdata->info.mem[TXRX_RING_MAP].addr
+               = (phys_addr_t)dev->channel->ringbuffer_pages;
+       pdata->info.mem[TXRX_RING_MAP].size
+               = dev->channel->ringbuffer_pagecount * PAGE_SIZE;
+       pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL;
+
+       pdata->info.mem[INT_PAGE_MAP].name = "int_page";
+       pdata->info.mem[INT_PAGE_MAP].addr = 
(phys_addr_t)vmbus_connection.int_page;
+       pdata->info.mem[INT_PAGE_MAP].size = PAGE_SIZE;
+       pdata->info.mem[INT_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
+
+       pdata->info.mem[MON_PAGE_MAP].name = "monitor_pages";
+       pdata->info.mem[MON_PAGE_MAP].addr = 
(phys_addr_t)vmbus_connection.monitor_pages[1];
+       pdata->info.mem[MON_PAGE_MAP].size = PAGE_SIZE;
+       pdata->info.mem[MON_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
+
+       pdata->info.priv = pdata;
+       pdata->device = dev;
+
+       ret = uio_register_device(&dev->device, &pdata->info);
+       if (ret) {
+               dev_err(&dev->device, "hv_uio register failed\n");
+               goto fail_close;
+       }
+
+       hv_set_drvdata(dev, pdata);
+
+       dev_info(&dev->device, "hv_uio device registered\n");
+
+       return 0;
+
+fail_close:
+       vmbus_close(dev->channel);
+fail:
+       kfree(pdata);
+
+       return ret;
+}
+
+static int
+hv_uio_remove(struct hv_device *dev)
+{
+       struct hv_uio_private_data *pdata = hv_get_drvdata(dev);
+
+       if (!pdata)
+               return 0;
+
+       pr_devel("unregister hyperv driver for hv_device {%pUl}\n",
+                dev->dev_instance.b);
+
+       uio_unregister_device(&pdata->info);
+       hv_set_drvdata(dev, NULL);
+       vmbus_close(dev->channel);
+       kfree(pdata);
+       return 0;
+}
+
+/*
+ * The device table is intentionally left blank so that
+ * this device driver is not automatically bound to any device.
+ */
+static const struct hv_vmbus_device_id hyperv_id_table[] = {
+       { },
+};
+
+MODULE_DEVICE_TABLE(vmbus, hyperv_id_table);
+
+static struct hv_driver hv_uio_drv = {
+       .name = KBUILD_MODNAME,
+       .id_table = hyperv_id_table,
+       .probe = hv_uio_probe,
+       .remove = hv_uio_remove,
+};
+
+static int __init
+hyperv_module_init(void)
+{
+       return vmbus_driver_register(&hv_uio_drv);
+}
+
+static void __exit
+hyperv_module_exit(void)
+{
+       vmbus_driver_unregister(&hv_uio_drv);
+}
+
+module_init(hyperv_module_init);
+module_exit(hyperv_module_exit);
+
+MODULE_DESCRIPTION("UIO driver for Hyper-V");
+MODULE_LICENSE("GPL");
-- 
2.9.3

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to