From: Stephen Hemminger <sthemmin@xxxxxxxxxxxxx> 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 <sthemmin@xxxxxxxxxxxxx> --- 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 devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel