Skip to content

Commit 95096f2

Browse files
shemmingergregkh
authored andcommitted
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>
1 parent fc76936 commit 95096f2

File tree

5 files changed

+230
-0
lines changed

5 files changed

+230
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5852,6 +5852,7 @@ F: drivers/input/serio/hyperv-keyboard.c
58525852
F: drivers/pci/host/pci-hyperv.c
58535853
F: drivers/net/hyperv/
58545854
F: drivers/scsi/storvsc_drv.c
5855+
F: drivers/uio/uio_hv_generic.c
58555856
F: drivers/video/fbdev/hyperv_fb.c
58565857
F: include/linux/hyperv.h
58575858
F: tools/hv/

drivers/hv/connection.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct vmbus_connection vmbus_connection = {
3939
.conn_state = DISCONNECTED,
4040
.next_gpadl_handle = ATOMIC_INIT(0xE1E10),
4141
};
42+
EXPORT_SYMBOL_GPL(vmbus_connection);
4243

4344
/*
4445
* Negotiated protocol version with the host.

drivers/uio/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,13 @@ config UIO_MF624
155155

156156
If you compile this as a module, it will be called uio_mf624.
157157

158+
config 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.
158167
endif

drivers/uio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ obj-$(CONFIG_UIO_NETX) += uio_netx.o
99
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
1010
obj-$(CONFIG_UIO_MF624) += uio_mf624.o
1111
obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o
12+
obj-$(CONFIG_UIO_HV_GENERIC) += uio_hv_generic.o

drivers/uio/uio_hv_generic.c

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
*/
44+
enum 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+
52+
struct hv_uio_private_data {
53+
struct uio_info info;
54+
struct hv_device *device;
55+
};
56+
57+
static int
58+
hv_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+
*/
84+
static int
85+
hv_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+
*/
99+
static 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+
110+
static int
111+
hv_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+
170+
fail_close:
171+
vmbus_close(dev->channel);
172+
fail:
173+
kfree(pdata);
174+
175+
return ret;
176+
}
177+
178+
static int
179+
hv_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+
193+
static 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+
200+
static int __init
201+
hyperv_module_init(void)
202+
{
203+
return vmbus_driver_register(&hv_uio_drv);
204+
}
205+
206+
static void __exit
207+
hyperv_module_exit(void)
208+
{
209+
vmbus_driver_unregister(&hv_uio_drv);
210+
}
211+
212+
module_init(hyperv_module_init);
213+
module_exit(hyperv_module_exit);
214+
215+
MODULE_VERSION(DRIVER_VERSION);
216+
MODULE_LICENSE("GPL v2");
217+
MODULE_AUTHOR(DRIVER_AUTHOR);
218+
MODULE_DESCRIPTION(DRIVER_DESC);

0 commit comments

Comments
 (0)