]>
Commit | Line | Data |
---|---|---|
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"); |