aboutsummaryrefslogtreecommitdiffstats
path: root/net/9p
diff options
context:
space:
mode:
authorEric Van Hensbergen <ericvh@gmail.com>2007-10-23 14:47:31 -0400
committerEric Van Hensbergen <ericvh@gmail.com>2007-10-23 14:47:31 -0400
commitb530cc794024be227876a089e66fb17b7b512763 (patch)
tree70b2c555ea9cfe0337a7e25b8cf57c0236efe242 /net/9p
parent0b776eb5426752d4e53354ac89e3710d857e09a7 (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/Kconfig7
-rw-r--r--net/9p/Makefile4
-rw-r--r--net/9p/trans_virtio.c353
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
26config 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
26config NET_9P_DEBUG 33config 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 @@
1obj-$(CONFIG_NET_9P) := 9pnet.o 1obj-$(CONFIG_NET_9P) := 9pnet.o
2obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o 2obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o
3obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o
3 4
49pnet-objs := \ 59pnet-objs := \
5 mod.o \ 6 mod.o \
@@ -12,3 +13,6 @@ obj-$(CONFIG_NET_9P_FD) += 9pnet_fd.o
12 13
139pnet_fd-objs := \ 149pnet_fd-objs := \
14 trans_fd.o \ 15 trans_fd.o \
16
179pnet_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 */
59static DECLARE_MUTEX(virtio_9p_lock);
60/* global which tracks highest initialized channel */
61static 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 */
67static 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. */
82static unsigned int rest_of_page(void *data)
83{
84 return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
85}
86
87static 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. */
120static 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
132static 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 */
172static unsigned int
173p9_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
191static 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
202static 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
212static 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
260free_in_vq:
261 dev->config->del_vq(chan->in_vq);
262free:
263 kfree(chan->inbuf);
264fail:
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. */
276static 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
316static struct virtio_device_id id_table[] = {
317 { VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID },
318 { 0 },
319};
320
321/* The standard "struct lguest_driver": */
322static 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
329static 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 */
337static 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
348module_init(p9_virtio_init);
349
350MODULE_DEVICE_TABLE(virtio, id_table);
351MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
352MODULE_DESCRIPTION("Virtio 9p Transport");
353MODULE_LICENSE("GPL");