diff options
author | Eric Van Hensbergen <ericvh@gmail.com> | 2007-10-23 14:47:31 -0400 |
---|---|---|
committer | Eric Van Hensbergen <ericvh@gmail.com> | 2007-10-23 14:47:31 -0400 |
commit | b530cc794024be227876a089e66fb17b7b512763 (patch) | |
tree | 70b2c555ea9cfe0337a7e25b8cf57c0236efe242 /net/9p | |
parent | 0b776eb5426752d4e53354ac89e3710d857e09a7 (diff) |
9p: add virtio transport
This adds a transport to 9p for communicating between guests and a host
using a virtio based transport.
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Diffstat (limited to 'net/9p')
-rw-r--r-- | net/9p/Kconfig | 7 | ||||
-rw-r--r-- | net/9p/Makefile | 4 | ||||
-rw-r--r-- | net/9p/trans_virtio.c | 353 |
3 files changed, 364 insertions, 0 deletions
diff --git a/net/9p/Kconfig b/net/9p/Kconfig index 71bc110aebf8..bafc50c9e6ff 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig | |||
@@ -23,6 +23,13 @@ config NET_9P_FD | |||
23 | file descriptors. TCP/IP is the default transport for 9p, | 23 | file descriptors. TCP/IP is the default transport for 9p, |
24 | so if you are going to use 9p, you'll likely want this. | 24 | so if you are going to use 9p, you'll likely want this. |
25 | 25 | ||
26 | config NET_9P_VIRTIO | ||
27 | depends on NET_9P && EXPERIMENTAL && VIRTIO | ||
28 | tristate "9P Virtio Transport (Experimental)" | ||
29 | help | ||
30 | This builds support for a transports between | ||
31 | guest partitions and a host partition. | ||
32 | |||
26 | config NET_9P_DEBUG | 33 | config NET_9P_DEBUG |
27 | bool "Debug information" | 34 | bool "Debug information" |
28 | depends on NET_9P | 35 | depends on NET_9P |
diff --git a/net/9p/Makefile b/net/9p/Makefile index 5059bc06f8f3..d3abb246ccab 100644 --- a/net/9p/Makefile +++ b/net/9p/Makefile | |||
@@ -1,5 +1,6 @@ | |||
1 | obj-$(CONFIG_NET_9P) := 9pnet.o | 1 | obj-$(CONFIG_NET_9P) := 9pnet.o |
2 | obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o | 2 | obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o |
3 | obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o | ||
3 | 4 | ||
4 | 9pnet-objs := \ | 5 | 9pnet-objs := \ |
5 | mod.o \ | 6 | mod.o \ |
@@ -12,3 +13,6 @@ obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o | |||
12 | 13 | ||
13 | 9pnet_fd-objs := \ | 14 | 9pnet_fd-objs := \ |
14 | trans_fd.o \ | 15 | trans_fd.o \ |
16 | |||
17 | 9pnet_virtio-objs := \ | ||
18 | trans_virtio.o \ | ||
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c new file mode 100644 index 000000000000..40b71a29fc3f --- /dev/null +++ b/net/9p/trans_virtio.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * The Guest 9p transport driver | ||
3 | * | ||
4 | * This is a trivial pipe-based transport driver based on the lguest console | ||
5 | * code: we use lguest's DMA mechanism to send bytes out, and register a | ||
6 | * DMA buffer to receive bytes in. It is assumed to be present and available | ||
7 | * from the very beginning of boot. | ||
8 | * | ||
9 | * This may be have been done by just instaniating another HVC console, | ||
10 | * but HVC's blocksize of 16 bytes is annoying and painful to performance. | ||
11 | * | ||
12 | * A more efficient transport could be built based on the virtio block driver | ||
13 | * but it requires some changes in the 9p transport model (which are in | ||
14 | * progress) | ||
15 | * | ||
16 | */ | ||
17 | /* | ||
18 | * Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation | ||
19 | * | ||
20 | * Based on virtio console driver | ||
21 | * Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation | ||
22 | * | ||
23 | * This program is free software; you can redistribute it and/or modify | ||
24 | * it under the terms of the GNU General Public License version 2 | ||
25 | * as published by the Free Software Foundation. | ||
26 | * | ||
27 | * This program is distributed in the hope that it will be useful, | ||
28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
30 | * GNU General Public License for more details. | ||
31 | * | ||
32 | * You should have received a copy of the GNU General Public License | ||
33 | * along with this program; if not, write to: | ||
34 | * Free Software Foundation | ||
35 | * 51 Franklin Street, Fifth Floor | ||
36 | * Boston, MA 02111-1301 USA | ||
37 | * | ||
38 | */ | ||
39 | |||
40 | #include <linux/in.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <linux/net.h> | ||
43 | #include <linux/ipv6.h> | ||
44 | #include <linux/errno.h> | ||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/un.h> | ||
47 | #include <linux/uaccess.h> | ||
48 | #include <linux/inet.h> | ||
49 | #include <linux/idr.h> | ||
50 | #include <linux/file.h> | ||
51 | #include <net/9p/9p.h> | ||
52 | #include <linux/parser.h> | ||
53 | #include <net/9p/transport.h> | ||
54 | #include <linux/scatterlist.h> | ||
55 | #include <linux/virtio.h> | ||
56 | #include <linux/virtio_9p.h> | ||
57 | |||
58 | /* a single mutex to manage channel initialization and attachment */ | ||
59 | static DECLARE_MUTEX(virtio_9p_lock); | ||
60 | /* global which tracks highest initialized channel */ | ||
61 | static int chan_index; | ||
62 | |||
63 | /* We keep all per-channel information in a structure. | ||
64 | * This structure is allocated within the devices dev->mem space. | ||
65 | * A pointer to the structure will get put in the transport private. | ||
66 | */ | ||
67 | static struct virtio_chan { | ||
68 | bool initialized; /* channel is initialized */ | ||
69 | bool inuse; /* channel is in use */ | ||
70 | |||
71 | struct virtqueue *in_vq, *out_vq; | ||
72 | struct virtio_device *vdev; | ||
73 | |||
74 | /* This is our input buffer, and how much data is left in it. */ | ||
75 | unsigned int in_len; | ||
76 | char *in, *inbuf; | ||
77 | |||
78 | wait_queue_head_t wq; /* waitq for buffer */ | ||
79 | } channels[MAX_9P_CHAN]; | ||
80 | |||
81 | /* How many bytes left in this page. */ | ||
82 | static unsigned int rest_of_page(void *data) | ||
83 | { | ||
84 | return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE); | ||
85 | } | ||
86 | |||
87 | static int p9_virtio_write(struct p9_trans *trans, void *buf, int count) | ||
88 | { | ||
89 | struct virtio_chan *chan = (struct virtio_chan *) trans->priv; | ||
90 | struct virtqueue *out_vq = chan->out_vq; | ||
91 | struct scatterlist sg[1]; | ||
92 | unsigned int len; | ||
93 | |||
94 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio write (%d)\n", count); | ||
95 | |||
96 | /* keep it simple - make sure we don't overflow a page */ | ||
97 | if (rest_of_page(buf) < count) | ||
98 | count = rest_of_page(buf); | ||
99 | |||
100 | sg_init_one(sg, buf, count); | ||
101 | |||
102 | /* add_buf wants a token to identify this buffer: we hand it any | ||
103 | * non-NULL pointer, since there's only ever one buffer. */ | ||
104 | if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) { | ||
105 | /* Tell Host to go! */ | ||
106 | out_vq->vq_ops->kick(out_vq); | ||
107 | /* Chill out until it's done with the buffer. */ | ||
108 | while (!out_vq->vq_ops->get_buf(out_vq, &len)) | ||
109 | cpu_relax(); | ||
110 | } | ||
111 | |||
112 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio wrote (%d)\n", count); | ||
113 | |||
114 | /* We're expected to return the amount of data we wrote: all of it. */ | ||
115 | return count; | ||
116 | } | ||
117 | |||
118 | /* Create a scatter-gather list representing our input buffer and put it in the | ||
119 | * queue. */ | ||
120 | static void add_inbuf(struct virtio_chan *chan) | ||
121 | { | ||
122 | struct scatterlist sg[1]; | ||
123 | |||
124 | sg_init_one(sg, chan->inbuf, PAGE_SIZE); | ||
125 | |||
126 | /* We should always be able to add one buffer to an empty queue. */ | ||
127 | if (chan->in_vq->vq_ops->add_buf(chan->in_vq, sg, 0, 1, chan->inbuf)) | ||
128 | BUG(); | ||
129 | chan->in_vq->vq_ops->kick(chan->in_vq); | ||
130 | } | ||
131 | |||
132 | static int p9_virtio_read(struct p9_trans *trans, void *buf, int count) | ||
133 | { | ||
134 | struct virtio_chan *chan = (struct virtio_chan *) trans->priv; | ||
135 | struct virtqueue *in_vq = chan->in_vq; | ||
136 | |||
137 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio read (%d)\n", count); | ||
138 | |||
139 | /* If we don't have an input queue yet, we can't get input. */ | ||
140 | BUG_ON(!in_vq); | ||
141 | |||
142 | /* No buffer? Try to get one. */ | ||
143 | if (!chan->in_len) { | ||
144 | chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len); | ||
145 | if (!chan->in) | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* You want more than we have to give? Well, try wanting less! */ | ||
150 | if (chan->in_len < count) | ||
151 | count = chan->in_len; | ||
152 | |||
153 | /* Copy across to their buffer and increment offset. */ | ||
154 | memcpy(buf, chan->in, count); | ||
155 | chan->in += count; | ||
156 | chan->in_len -= count; | ||
157 | |||
158 | /* Finished? Re-register buffer so Host will use it again. */ | ||
159 | if (chan->in_len == 0) | ||
160 | add_inbuf(chan); | ||
161 | |||
162 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio finished read (%d)\n", | ||
163 | count); | ||
164 | |||
165 | return count; | ||
166 | } | ||
167 | |||
168 | /* The poll function is used by 9p transports to determine if there | ||
169 | * is there is activity available on a particular channel. In our case | ||
170 | * we use it to wait for a callback from the input routines. | ||
171 | */ | ||
172 | static unsigned int | ||
173 | p9_virtio_poll(struct p9_trans *trans, struct poll_table_struct *pt) | ||
174 | { | ||
175 | struct virtio_chan *chan = (struct virtio_chan *)trans->priv; | ||
176 | struct virtqueue *in_vq = chan->in_vq; | ||
177 | int ret = POLLOUT; /* we can always handle more output */ | ||
178 | |||
179 | poll_wait(NULL, &chan->wq, pt); | ||
180 | |||
181 | /* No buffer? Try to get one. */ | ||
182 | if (!chan->in_len) | ||
183 | chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len); | ||
184 | |||
185 | if (chan->in_len) | ||
186 | ret |= POLLIN; | ||
187 | |||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | static void p9_virtio_close(struct p9_trans *trans) | ||
192 | { | ||
193 | struct virtio_chan *chan = trans->priv; | ||
194 | |||
195 | down(&virtio_9p_lock); | ||
196 | chan->inuse = false; | ||
197 | up(&virtio_9p_lock); | ||
198 | |||
199 | kfree(trans); | ||
200 | } | ||
201 | |||
202 | static bool p9_virtio_intr(struct virtqueue *q) | ||
203 | { | ||
204 | struct virtio_chan *chan = q->vdev->priv; | ||
205 | |||
206 | P9_DPRINTK(P9_DEBUG_TRANS, "9p poll_wakeup: %p\n", &chan->wq); | ||
207 | wake_up_interruptible(&chan->wq); | ||
208 | |||
209 | return true; | ||
210 | } | ||
211 | |||
212 | static int p9_virtio_probe(struct virtio_device *dev) | ||
213 | { | ||
214 | int err; | ||
215 | struct virtio_chan *chan; | ||
216 | int index; | ||
217 | |||
218 | down(&virtio_9p_lock); | ||
219 | index = chan_index++; | ||
220 | chan = &channels[index]; | ||
221 | up(&virtio_9p_lock); | ||
222 | |||
223 | if (chan_index > MAX_9P_CHAN) { | ||
224 | printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n"); | ||
225 | BUG(); | ||
226 | } | ||
227 | |||
228 | chan->vdev = dev; | ||
229 | |||
230 | /* This is the scratch page we use to receive console input */ | ||
231 | chan->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
232 | if (!chan->inbuf) { | ||
233 | err = -ENOMEM; | ||
234 | goto fail; | ||
235 | } | ||
236 | |||
237 | /* Find the input queue. */ | ||
238 | dev->priv = chan; | ||
239 | chan->in_vq = dev->config->find_vq(dev, p9_virtio_intr); | ||
240 | if (IS_ERR(chan->in_vq)) { | ||
241 | err = PTR_ERR(chan->in_vq); | ||
242 | goto free; | ||
243 | } | ||
244 | |||
245 | chan->out_vq = dev->config->find_vq(dev, NULL); | ||
246 | if (IS_ERR(chan->out_vq)) { | ||
247 | err = PTR_ERR(chan->out_vq); | ||
248 | goto free_in_vq; | ||
249 | } | ||
250 | |||
251 | init_waitqueue_head(&chan->wq); | ||
252 | |||
253 | /* Register the input buffer the first time. */ | ||
254 | add_inbuf(chan); | ||
255 | chan->inuse = false; | ||
256 | chan->initialized = true; | ||
257 | |||
258 | return 0; | ||
259 | |||
260 | free_in_vq: | ||
261 | dev->config->del_vq(chan->in_vq); | ||
262 | free: | ||
263 | kfree(chan->inbuf); | ||
264 | fail: | ||
265 | down(&virtio_9p_lock); | ||
266 | chan_index--; | ||
267 | up(&virtio_9p_lock); | ||
268 | return err; | ||
269 | } | ||
270 | |||
271 | /* This sets up a transport channel for 9p communication. Right now | ||
272 | * we only match the first available channel, but eventually we couldlook up | ||
273 | * alternate channels by matching devname versus a virtio_config entry. | ||
274 | * We use a simple reference count mechanism to ensure that only a single | ||
275 | * mount has a channel open at a time. */ | ||
276 | static struct p9_trans *p9_virtio_create(const char *devname, char *args) | ||
277 | { | ||
278 | struct p9_trans *trans; | ||
279 | int index = 0; | ||
280 | struct virtio_chan *chan = channels; | ||
281 | |||
282 | down(&virtio_9p_lock); | ||
283 | while (index < MAX_9P_CHAN) { | ||
284 | if (chan->initialized && !chan->inuse) { | ||
285 | chan->inuse = true; | ||
286 | break; | ||
287 | } else { | ||
288 | index++; | ||
289 | chan = &channels[index]; | ||
290 | } | ||
291 | } | ||
292 | up(&virtio_9p_lock); | ||
293 | |||
294 | if (index >= MAX_9P_CHAN) { | ||
295 | printk(KERN_ERR "9p: virtio: couldn't find a free channel\n"); | ||
296 | return NULL; | ||
297 | } | ||
298 | |||
299 | trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); | ||
300 | if (!trans) { | ||
301 | printk(KERN_ERR "9p: couldn't allocate transport\n"); | ||
302 | return ERR_PTR(-ENOMEM); | ||
303 | } | ||
304 | |||
305 | trans->write = p9_virtio_write; | ||
306 | trans->read = p9_virtio_read; | ||
307 | trans->close = p9_virtio_close; | ||
308 | trans->poll = p9_virtio_poll; | ||
309 | trans->priv = chan; | ||
310 | |||
311 | return trans; | ||
312 | } | ||
313 | |||
314 | #define VIRTIO_ID_9P 9 | ||
315 | |||
316 | static struct virtio_device_id id_table[] = { | ||
317 | { VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID }, | ||
318 | { 0 }, | ||
319 | }; | ||
320 | |||
321 | /* The standard "struct lguest_driver": */ | ||
322 | static struct virtio_driver p9_virtio_drv = { | ||
323 | .driver.name = KBUILD_MODNAME, | ||
324 | .driver.owner = THIS_MODULE, | ||
325 | .id_table = id_table, | ||
326 | .probe = p9_virtio_probe, | ||
327 | }; | ||
328 | |||
329 | static struct p9_trans_module p9_virtio_trans = { | ||
330 | .name = "virtio", | ||
331 | .create = p9_virtio_create, | ||
332 | .maxsize = PAGE_SIZE, | ||
333 | .def = 0, | ||
334 | }; | ||
335 | |||
336 | /* The standard init function */ | ||
337 | static int __init p9_virtio_init(void) | ||
338 | { | ||
339 | int count; | ||
340 | |||
341 | for (count = 0; count < MAX_9P_CHAN; count++) | ||
342 | channels[count].initialized = false; | ||
343 | |||
344 | v9fs_register_trans(&p9_virtio_trans); | ||
345 | return register_virtio_driver(&p9_virtio_drv); | ||
346 | } | ||
347 | |||
348 | module_init(p9_virtio_init); | ||
349 | |||
350 | MODULE_DEVICE_TABLE(virtio, id_table); | ||
351 | MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); | ||
352 | MODULE_DESCRIPTION("Virtio 9p Transport"); | ||
353 | MODULE_LICENSE("GPL"); | ||