aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hemminger <sthemmin@microsoft.com>2016-12-03 15:34:40 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-12-06 05:52:49 -0500
commit95096f2fbd10186d3e78a328b327afc71428f65f (patch)
tree5f6478dea3a9ca8d99652736c99691b3de219c4f
parentfc76936d3ea5720a6e0948a08381b803a68deb28 (diff)
uio-hv-generic: new userspace i/o driver for VMBus
This is a new driver to enable userspace networking on VMBus. It is based largely on the similar driver that already exists for PCI, and earlier work done by Brocade to support DPDK. Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/hv/connection.c1
-rw-r--r--drivers/uio/Kconfig9
-rw-r--r--drivers/uio/Makefile1
-rw-r--r--drivers/uio/uio_hv_generic.c218
5 files changed, 230 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index b0fee8c45135..d19faf724ac6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5852,6 +5852,7 @@ F: drivers/input/serio/hyperv-keyboard.c
5852F: drivers/pci/host/pci-hyperv.c 5852F: drivers/pci/host/pci-hyperv.c
5853F: drivers/net/hyperv/ 5853F: drivers/net/hyperv/
5854F: drivers/scsi/storvsc_drv.c 5854F: drivers/scsi/storvsc_drv.c
5855F: drivers/uio/uio_hv_generic.c
5855F: drivers/video/fbdev/hyperv_fb.c 5856F: drivers/video/fbdev/hyperv_fb.c
5856F: include/linux/hyperv.h 5857F: include/linux/hyperv.h
5857F: tools/hv/ 5858F: tools/hv/
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 78e6368a4423..6ce8b874e833 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -39,6 +39,7 @@ struct vmbus_connection vmbus_connection = {
39 .conn_state = DISCONNECTED, 39 .conn_state = DISCONNECTED,
40 .next_gpadl_handle = ATOMIC_INIT(0xE1E10), 40 .next_gpadl_handle = ATOMIC_INIT(0xE1E10),
41}; 41};
42EXPORT_SYMBOL_GPL(vmbus_connection);
42 43
43/* 44/*
44 * Negotiated protocol version with the host. 45 * Negotiated protocol version with the host.
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index 52c98ce1b6fe..7e8dc78a9796 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -155,4 +155,13 @@ config UIO_MF624
155 155
156 If you compile this as a module, it will be called uio_mf624. 156 If you compile this as a module, it will be called uio_mf624.
157 157
158config UIO_HV_GENERIC
159 tristate "Generic driver for Hyper-V VMBus"
160 depends on HYPERV
161 help
162 Generic driver that you can bind, dynamically, to any
163 Hyper-V VMBus device. It is useful to provide direct access
164 to network and storage devices from userspace.
165
166 If you compile this as a module, it will be called uio_hv_generic.
158endif 167endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index 8560dad52d0f..e9663bb8a4c7 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_UIO_NETX) += uio_netx.o
9obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o 9obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
10obj-$(CONFIG_UIO_MF624) += uio_mf624.o 10obj-$(CONFIG_UIO_MF624) += uio_mf624.o
11obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o 11obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o
12obj-$(CONFIG_UIO_HV_GENERIC) += uio_hv_generic.o
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
new file mode 100644
index 000000000000..ad3ab5805ad8
--- /dev/null
+++ b/drivers/uio/uio_hv_generic.c
@@ -0,0 +1,218 @@
1/*
2 * uio_hv_generic - generic UIO driver for VMBus
3 *
4 * Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
5 * Copyright (c) 2016, Microsoft Corporation.
6 *
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2.
9 *
10 * Since the driver does not declare any device ids, you must allocate
11 * id and bind the device to the driver yourself. For example:
12 *
13 * # echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" \
14 * > /sys/bus/vmbus/drivers/uio_hv_generic
15 * # echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 \
16 * > /sys/bus/vmbus/drivers/hv_netvsc/unbind
17 * # echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 \
18 * > /sys/bus/vmbus/drivers/uio_hv_generic/bind
19 */
20
21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
23#include <linux/device.h>
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/uio_driver.h>
27#include <linux/netdevice.h>
28#include <linux/if_ether.h>
29#include <linux/skbuff.h>
30#include <linux/hyperv.h>
31#include <linux/vmalloc.h>
32#include <linux/slab.h>
33
34#include "../hv/hyperv_vmbus.h"
35
36#define DRIVER_VERSION "0.02.0"
37#define DRIVER_AUTHOR "Stephen Hemminger <sthemmin at microsoft.com>"
38#define DRIVER_DESC "Generic UIO driver for VMBus devices"
39
40/*
41 * List of resources to be mapped to user space
42 * can be extended up to MAX_UIO_MAPS(5) items
43 */
44enum hv_uio_map {
45 TXRX_RING_MAP = 0,
46 INT_PAGE_MAP,
47 MON_PAGE_MAP,
48};
49
50#define HV_RING_SIZE 512
51
52struct hv_uio_private_data {
53 struct uio_info info;
54 struct hv_device *device;
55};
56
57static int
58hv_uio_mmap(struct uio_info *info, struct vm_area_struct *vma)
59{
60 int mi;
61
62 if (vma->vm_pgoff >= MAX_UIO_MAPS)
63 return -EINVAL;
64
65 if (info->mem[vma->vm_pgoff].size == 0)
66 return -EINVAL;
67
68 mi = (int)vma->vm_pgoff;
69
70 return remap_pfn_range(vma, vma->vm_start,
71 virt_to_phys((void *)info->mem[mi].addr) >> PAGE_SHIFT,
72 vma->vm_end - vma->vm_start, vma->vm_page_prot);
73}
74
75/*
76 * This is the irqcontrol callback to be registered to uio_info.
77 * It can be used to disable/enable interrupt from user space processes.
78 *
79 * @param info
80 * pointer to uio_info.
81 * @param irq_state
82 * state value. 1 to enable interrupt, 0 to disable interrupt.
83 */
84static int
85hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
86{
87 struct hv_uio_private_data *pdata = info->priv;
88 struct hv_device *dev = pdata->device;
89
90 dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state;
91 virt_mb();
92
93 return 0;
94}
95
96/*
97 * Callback from vmbus_event when something is in inbound ring.
98 */
99static void hv_uio_channel_cb(void *context)
100{
101 struct hv_uio_private_data *pdata = context;
102 struct hv_device *dev = pdata->device;
103
104 dev->channel->inbound.ring_buffer->interrupt_mask = 1;
105 virt_mb();
106
107 uio_event_notify(&pdata->info);
108}
109
110static int
111hv_uio_probe(struct hv_device *dev,
112 const struct hv_vmbus_device_id *dev_id)
113{
114 struct hv_uio_private_data *pdata;
115 int ret;
116
117 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
118 if (!pdata)
119 return -ENOMEM;
120
121 ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE,
122 HV_RING_SIZE * PAGE_SIZE, NULL, 0,
123 hv_uio_channel_cb, pdata);
124 if (ret)
125 goto fail;
126
127 dev->channel->inbound.ring_buffer->interrupt_mask = 1;
128 dev->channel->batched_reading = false;
129
130 /* Fill general uio info */
131 pdata->info.name = "uio_hv_generic";
132 pdata->info.version = DRIVER_VERSION;
133 pdata->info.irqcontrol = hv_uio_irqcontrol;
134 pdata->info.mmap = hv_uio_mmap;
135 pdata->info.irq = UIO_IRQ_CUSTOM;
136
137 /* mem resources */
138 pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings";
139 pdata->info.mem[TXRX_RING_MAP].addr
140 = (phys_addr_t)dev->channel->ringbuffer_pages;
141 pdata->info.mem[TXRX_RING_MAP].size
142 = dev->channel->ringbuffer_pagecount * PAGE_SIZE;
143 pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL;
144
145 pdata->info.mem[INT_PAGE_MAP].name = "int_page";
146 pdata->info.mem[INT_PAGE_MAP].addr =
147 (phys_addr_t)vmbus_connection.int_page;
148 pdata->info.mem[INT_PAGE_MAP].size = PAGE_SIZE;
149 pdata->info.mem[INT_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
150
151 pdata->info.mem[MON_PAGE_MAP].name = "monitor_pages";
152 pdata->info.mem[MON_PAGE_MAP].addr =
153 (phys_addr_t)vmbus_connection.monitor_pages[1];
154 pdata->info.mem[MON_PAGE_MAP].size = PAGE_SIZE;
155 pdata->info.mem[MON_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
156
157 pdata->info.priv = pdata;
158 pdata->device = dev;
159
160 ret = uio_register_device(&dev->device, &pdata->info);
161 if (ret) {
162 dev_err(&dev->device, "hv_uio register failed\n");
163 goto fail_close;
164 }
165
166 hv_set_drvdata(dev, pdata);
167
168 return 0;
169
170fail_close:
171 vmbus_close(dev->channel);
172fail:
173 kfree(pdata);
174
175 return ret;
176}
177
178static int
179hv_uio_remove(struct hv_device *dev)
180{
181 struct hv_uio_private_data *pdata = hv_get_drvdata(dev);
182
183 if (!pdata)
184 return 0;
185
186 uio_unregister_device(&pdata->info);
187 hv_set_drvdata(dev, NULL);
188 vmbus_close(dev->channel);
189 kfree(pdata);
190 return 0;
191}
192
193static struct hv_driver hv_uio_drv = {
194 .name = "uio_hv_generic",
195 .id_table = NULL, /* only dynamic id's */
196 .probe = hv_uio_probe,
197 .remove = hv_uio_remove,
198};
199
200static int __init
201hyperv_module_init(void)
202{
203 return vmbus_driver_register(&hv_uio_drv);
204}
205
206static void __exit
207hyperv_module_exit(void)
208{
209 vmbus_driver_unregister(&hv_uio_drv);
210}
211
212module_init(hyperv_module_init);
213module_exit(hyperv_module_exit);
214
215MODULE_VERSION(DRIVER_VERSION);
216MODULE_LICENSE("GPL v2");
217MODULE_AUTHOR(DRIVER_AUTHOR);
218MODULE_DESCRIPTION(DRIVER_DESC);