diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2008-10-22 14:34:09 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-10-22 14:34:09 -0400 |
commit | f20e3b5fe7ead0615309433260b9784d8da0bbbd (patch) | |
tree | eabb2e47a0355ac4e8024b7087b4e7cb9f324358 /net/9p/trans_virtio.c | |
parent | bcbfe664e7af019e698cef2feb85ac2b4f1ac11d (diff) | |
parent | f030d7b65e4e6399f23de2a41a58d1b607b6bd89 (diff) |
Merge branch 'for-rmk' of git://git.android.com/kernel into devel
Diffstat (limited to 'net/9p/trans_virtio.c')
-rw-r--r-- | net/9p/trans_virtio.c | 246 |
1 files changed, 39 insertions, 207 deletions
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 94912e077a55..2d7781ec663b 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c | |||
@@ -1,12 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | * The Guest 9p transport driver | 2 | * The Virtio 9p transport driver |
3 | * | 3 | * |
4 | * This is a block based transport driver based on the lguest block driver | 4 | * This is a block based transport driver based on the lguest block driver |
5 | * code. | 5 | * code. |
6 | * | 6 | * |
7 | */ | 7 | * Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation |
8 | /* | ||
9 | * Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation | ||
10 | * | 8 | * |
11 | * Based on virtio console driver | 9 | * Based on virtio console driver |
12 | * Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation | 10 | * Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation |
@@ -41,6 +39,7 @@ | |||
41 | #include <linux/file.h> | 39 | #include <linux/file.h> |
42 | #include <net/9p/9p.h> | 40 | #include <net/9p/9p.h> |
43 | #include <linux/parser.h> | 41 | #include <linux/parser.h> |
42 | #include <net/9p/client.h> | ||
44 | #include <net/9p/transport.h> | 43 | #include <net/9p/transport.h> |
45 | #include <linux/scatterlist.h> | 44 | #include <linux/scatterlist.h> |
46 | #include <linux/virtio.h> | 45 | #include <linux/virtio.h> |
@@ -53,50 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock); | |||
53 | /* global which tracks highest initialized channel */ | 52 | /* global which tracks highest initialized channel */ |
54 | static int chan_index; | 53 | static int chan_index; |
55 | 54 | ||
56 | #define P9_INIT_MAXTAG 16 | ||
57 | |||
58 | |||
59 | /** | ||
60 | * enum p9_req_status_t - virtio request status | ||
61 | * @REQ_STATUS_IDLE: request slot unused | ||
62 | * @REQ_STATUS_SENT: request sent to server | ||
63 | * @REQ_STATUS_RCVD: response received from server | ||
64 | * @REQ_STATUS_FLSH: request has been flushed | ||
65 | * | ||
66 | * The @REQ_STATUS_IDLE state is used to mark a request slot as unused | ||
67 | * but use is actually tracked by the idpool structure which handles tag | ||
68 | * id allocation. | ||
69 | * | ||
70 | */ | ||
71 | |||
72 | enum p9_req_status_t { | ||
73 | REQ_STATUS_IDLE, | ||
74 | REQ_STATUS_SENT, | ||
75 | REQ_STATUS_RCVD, | ||
76 | REQ_STATUS_FLSH, | ||
77 | }; | ||
78 | |||
79 | /** | ||
80 | * struct p9_req_t - virtio request slots | ||
81 | * @status: status of this request slot | ||
82 | * @wq: wait_queue for the client to block on for this request | ||
83 | * | ||
84 | * The virtio transport uses an array to track outstanding requests | ||
85 | * instead of a list. While this may incurr overhead during initial | ||
86 | * allocation or expansion, it makes request lookup much easier as the | ||
87 | * tag id is a index into an array. (We use tag+1 so that we can accomodate | ||
88 | * the -1 tag for the T_VERSION request). | ||
89 | * This also has the nice effect of only having to allocate wait_queues | ||
90 | * once, instead of constantly allocating and freeing them. Its possible | ||
91 | * other resources could benefit from this scheme as well. | ||
92 | * | ||
93 | */ | ||
94 | |||
95 | struct p9_req_t { | ||
96 | int status; | ||
97 | wait_queue_head_t *wq; | ||
98 | }; | ||
99 | |||
100 | /** | 55 | /** |
101 | * struct virtio_chan - per-instance transport information | 56 | * struct virtio_chan - per-instance transport information |
102 | * @initialized: whether the channel is initialized | 57 | * @initialized: whether the channel is initialized |
@@ -121,67 +76,14 @@ static struct virtio_chan { | |||
121 | 76 | ||
122 | spinlock_t lock; | 77 | spinlock_t lock; |
123 | 78 | ||
79 | struct p9_client *client; | ||
124 | struct virtio_device *vdev; | 80 | struct virtio_device *vdev; |
125 | struct virtqueue *vq; | 81 | struct virtqueue *vq; |
126 | 82 | ||
127 | struct p9_idpool *tagpool; | ||
128 | struct p9_req_t *reqs; | ||
129 | int max_tag; | ||
130 | |||
131 | /* Scatterlist: can be too big for stack. */ | 83 | /* Scatterlist: can be too big for stack. */ |
132 | struct scatterlist sg[VIRTQUEUE_NUM]; | 84 | struct scatterlist sg[VIRTQUEUE_NUM]; |
133 | } channels[MAX_9P_CHAN]; | 85 | } channels[MAX_9P_CHAN]; |
134 | 86 | ||
135 | /** | ||
136 | * p9_lookup_tag - Lookup requests by tag | ||
137 | * @c: virtio channel to lookup tag within | ||
138 | * @tag: numeric id for transaction | ||
139 | * | ||
140 | * this is a simple array lookup, but will grow the | ||
141 | * request_slots as necessary to accomodate transaction | ||
142 | * ids which did not previously have a slot. | ||
143 | * | ||
144 | * Bugs: there is currently no upper limit on request slots set | ||
145 | * here, but that should be constrained by the id accounting. | ||
146 | */ | ||
147 | |||
148 | static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag) | ||
149 | { | ||
150 | /* This looks up the original request by tag so we know which | ||
151 | * buffer to read the data into */ | ||
152 | tag++; | ||
153 | |||
154 | while (tag >= c->max_tag) { | ||
155 | int old_max = c->max_tag; | ||
156 | int count; | ||
157 | |||
158 | if (c->max_tag) | ||
159 | c->max_tag *= 2; | ||
160 | else | ||
161 | c->max_tag = P9_INIT_MAXTAG; | ||
162 | |||
163 | c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag, | ||
164 | GFP_ATOMIC); | ||
165 | if (!c->reqs) { | ||
166 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
167 | BUG(); | ||
168 | } | ||
169 | for (count = old_max; count < c->max_tag; count++) { | ||
170 | c->reqs[count].status = REQ_STATUS_IDLE; | ||
171 | c->reqs[count].wq = kmalloc(sizeof(wait_queue_head_t), | ||
172 | GFP_ATOMIC); | ||
173 | if (!c->reqs[count].wq) { | ||
174 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
175 | BUG(); | ||
176 | } | ||
177 | init_waitqueue_head(c->reqs[count].wq); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | return &c->reqs[tag]; | ||
182 | } | ||
183 | |||
184 | |||
185 | /* How many bytes left in this page. */ | 87 | /* How many bytes left in this page. */ |
186 | static unsigned int rest_of_page(void *data) | 88 | static unsigned int rest_of_page(void *data) |
187 | { | 89 | { |
@@ -197,25 +99,13 @@ static unsigned int rest_of_page(void *data) | |||
197 | * | 99 | * |
198 | */ | 100 | */ |
199 | 101 | ||
200 | static void p9_virtio_close(struct p9_trans *trans) | 102 | static void p9_virtio_close(struct p9_client *client) |
201 | { | 103 | { |
202 | struct virtio_chan *chan = trans->priv; | 104 | struct virtio_chan *chan = client->trans; |
203 | int count; | ||
204 | unsigned long flags; | ||
205 | |||
206 | spin_lock_irqsave(&chan->lock, flags); | ||
207 | p9_idpool_destroy(chan->tagpool); | ||
208 | for (count = 0; count < chan->max_tag; count++) | ||
209 | kfree(chan->reqs[count].wq); | ||
210 | kfree(chan->reqs); | ||
211 | chan->max_tag = 0; | ||
212 | spin_unlock_irqrestore(&chan->lock, flags); | ||
213 | 105 | ||
214 | mutex_lock(&virtio_9p_lock); | 106 | mutex_lock(&virtio_9p_lock); |
215 | chan->inuse = false; | 107 | chan->inuse = false; |
216 | mutex_unlock(&virtio_9p_lock); | 108 | mutex_unlock(&virtio_9p_lock); |
217 | |||
218 | kfree(trans); | ||
219 | } | 109 | } |
220 | 110 | ||
221 | /** | 111 | /** |
@@ -236,17 +126,16 @@ static void req_done(struct virtqueue *vq) | |||
236 | struct virtio_chan *chan = vq->vdev->priv; | 126 | struct virtio_chan *chan = vq->vdev->priv; |
237 | struct p9_fcall *rc; | 127 | struct p9_fcall *rc; |
238 | unsigned int len; | 128 | unsigned int len; |
239 | unsigned long flags; | ||
240 | struct p9_req_t *req; | 129 | struct p9_req_t *req; |
241 | 130 | ||
242 | spin_lock_irqsave(&chan->lock, flags); | 131 | P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n"); |
132 | |||
243 | while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) { | 133 | while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) { |
244 | req = p9_lookup_tag(chan, rc->tag); | 134 | P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc); |
245 | req->status = REQ_STATUS_RCVD; | 135 | P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag); |
246 | wake_up(req->wq); | 136 | req = p9_tag_lookup(chan->client, rc->tag); |
137 | p9_client_cb(chan->client, req); | ||
247 | } | 138 | } |
248 | /* In case queue is stopped waiting for more buffers. */ | ||
249 | spin_unlock_irqrestore(&chan->lock, flags); | ||
250 | } | 139 | } |
251 | 140 | ||
252 | /** | 141 | /** |
@@ -283,8 +172,14 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, | |||
283 | return index-start; | 172 | return index-start; |
284 | } | 173 | } |
285 | 174 | ||
175 | /* We don't currently allow canceling of virtio requests */ | ||
176 | static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) | ||
177 | { | ||
178 | return 1; | ||
179 | } | ||
180 | |||
286 | /** | 181 | /** |
287 | * p9_virtio_rpc - issue a request and wait for a response | 182 | * p9_virtio_request - issue a request |
288 | * @t: transport state | 183 | * @t: transport state |
289 | * @tc: &p9_fcall request to transmit | 184 | * @tc: &p9_fcall request to transmit |
290 | * @rc: &p9_fcall to put reponse into | 185 | * @rc: &p9_fcall to put reponse into |
@@ -292,44 +187,22 @@ pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, | |||
292 | */ | 187 | */ |
293 | 188 | ||
294 | static int | 189 | static int |
295 | p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc) | 190 | p9_virtio_request(struct p9_client *client, struct p9_req_t *req) |
296 | { | 191 | { |
297 | int in, out; | 192 | int in, out; |
298 | int n, err, size; | 193 | struct virtio_chan *chan = client->trans; |
299 | struct virtio_chan *chan = t->priv; | 194 | char *rdata = (char *)req->rc+sizeof(struct p9_fcall); |
300 | char *rdata; | ||
301 | struct p9_req_t *req; | ||
302 | unsigned long flags; | ||
303 | |||
304 | if (*rc == NULL) { | ||
305 | *rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL); | ||
306 | if (!*rc) | ||
307 | return -ENOMEM; | ||
308 | } | ||
309 | |||
310 | rdata = (char *)*rc+sizeof(struct p9_fcall); | ||
311 | |||
312 | n = P9_NOTAG; | ||
313 | if (tc->id != P9_TVERSION) { | ||
314 | n = p9_idpool_get(chan->tagpool); | ||
315 | if (n < 0) | ||
316 | return -ENOMEM; | ||
317 | } | ||
318 | |||
319 | spin_lock_irqsave(&chan->lock, flags); | ||
320 | req = p9_lookup_tag(chan, n); | ||
321 | spin_unlock_irqrestore(&chan->lock, flags); | ||
322 | 195 | ||
323 | p9_set_tag(tc, n); | 196 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n"); |
324 | 197 | ||
325 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n); | 198 | out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata, |
326 | 199 | req->tc->size); | |
327 | out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size); | 200 | in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, |
328 | in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, t->msize); | 201 | client->msize); |
329 | 202 | ||
330 | req->status = REQ_STATUS_SENT; | 203 | req->status = REQ_STATUS_SENT; |
331 | 204 | ||
332 | if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) { | 205 | if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc)) { |
333 | P9_DPRINTK(P9_DEBUG_TRANS, | 206 | P9_DPRINTK(P9_DEBUG_TRANS, |
334 | "9p debug: virtio rpc add_buf returned failure"); | 207 | "9p debug: virtio rpc add_buf returned failure"); |
335 | return -EIO; | 208 | return -EIO; |
@@ -337,31 +210,7 @@ p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc) | |||
337 | 210 | ||
338 | chan->vq->vq_ops->kick(chan->vq); | 211 | chan->vq->vq_ops->kick(chan->vq); |
339 | 212 | ||
340 | wait_event(*req->wq, req->status == REQ_STATUS_RCVD); | 213 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n"); |
341 | |||
342 | size = le32_to_cpu(*(__le32 *) rdata); | ||
343 | |||
344 | err = p9_deserialize_fcall(rdata, size, *rc, t->extended); | ||
345 | if (err < 0) { | ||
346 | P9_DPRINTK(P9_DEBUG_TRANS, | ||
347 | "9p debug: virtio rpc deserialize returned %d\n", err); | ||
348 | return err; | ||
349 | } | ||
350 | |||
351 | #ifdef CONFIG_NET_9P_DEBUG | ||
352 | if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { | ||
353 | char buf[150]; | ||
354 | |||
355 | p9_printfcall(buf, sizeof(buf), *rc, t->extended); | ||
356 | printk(KERN_NOTICE ">>> %p %s\n", t, buf); | ||
357 | } | ||
358 | #endif | ||
359 | |||
360 | if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool)) | ||
361 | p9_idpool_put(n, chan->tagpool); | ||
362 | |||
363 | req->status = REQ_STATUS_IDLE; | ||
364 | |||
365 | return 0; | 214 | return 0; |
366 | } | 215 | } |
367 | 216 | ||
@@ -422,10 +271,9 @@ fail: | |||
422 | 271 | ||
423 | /** | 272 | /** |
424 | * p9_virtio_create - allocate a new virtio channel | 273 | * p9_virtio_create - allocate a new virtio channel |
274 | * @client: client instance invoking this transport | ||
425 | * @devname: string identifying the channel to connect to (unused) | 275 | * @devname: string identifying the channel to connect to (unused) |
426 | * @args: args passed from sys_mount() for per-transport options (unused) | 276 | * @args: args passed from sys_mount() for per-transport options (unused) |
427 | * @msize: requested maximum packet size | ||
428 | * @extended: 9p2000.u enabled flag | ||
429 | * | 277 | * |
430 | * This sets up a transport channel for 9p communication. Right now | 278 | * This sets up a transport channel for 9p communication. Right now |
431 | * we only match the first available channel, but eventually we couldlook up | 279 | * we only match the first available channel, but eventually we couldlook up |
@@ -441,11 +289,9 @@ fail: | |||
441 | * | 289 | * |
442 | */ | 290 | */ |
443 | 291 | ||
444 | static struct p9_trans * | 292 | static int |
445 | p9_virtio_create(const char *devname, char *args, int msize, | 293 | p9_virtio_create(struct p9_client *client, const char *devname, char *args) |
446 | unsigned char extended) | ||
447 | { | 294 | { |
448 | struct p9_trans *trans; | ||
449 | struct virtio_chan *chan = channels; | 295 | struct virtio_chan *chan = channels; |
450 | int index = 0; | 296 | int index = 0; |
451 | 297 | ||
@@ -463,30 +309,13 @@ p9_virtio_create(const char *devname, char *args, int msize, | |||
463 | 309 | ||
464 | if (index >= MAX_9P_CHAN) { | 310 | if (index >= MAX_9P_CHAN) { |
465 | printk(KERN_ERR "9p: no channels available\n"); | 311 | printk(KERN_ERR "9p: no channels available\n"); |
466 | return ERR_PTR(-ENODEV); | 312 | return -ENODEV; |
467 | } | 313 | } |
468 | 314 | ||
469 | chan->tagpool = p9_idpool_create(); | 315 | client->trans = (void *)chan; |
470 | if (IS_ERR(chan->tagpool)) { | 316 | chan->client = client; |
471 | printk(KERN_ERR "9p: couldn't allocate tagpool\n"); | ||
472 | return ERR_PTR(-ENOMEM); | ||
473 | } | ||
474 | p9_idpool_get(chan->tagpool); /* reserve tag 0 */ | ||
475 | chan->max_tag = 0; | ||
476 | chan->reqs = NULL; | ||
477 | |||
478 | trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); | ||
479 | if (!trans) { | ||
480 | printk(KERN_ERR "9p: couldn't allocate transport\n"); | ||
481 | return ERR_PTR(-ENOMEM); | ||
482 | } | ||
483 | trans->extended = extended; | ||
484 | trans->msize = msize; | ||
485 | trans->close = p9_virtio_close; | ||
486 | trans->rpc = p9_virtio_rpc; | ||
487 | trans->priv = chan; | ||
488 | 317 | ||
489 | return trans; | 318 | return 0; |
490 | } | 319 | } |
491 | 320 | ||
492 | /** | 321 | /** |
@@ -526,6 +355,9 @@ static struct virtio_driver p9_virtio_drv = { | |||
526 | static struct p9_trans_module p9_virtio_trans = { | 355 | static struct p9_trans_module p9_virtio_trans = { |
527 | .name = "virtio", | 356 | .name = "virtio", |
528 | .create = p9_virtio_create, | 357 | .create = p9_virtio_create, |
358 | .close = p9_virtio_close, | ||
359 | .request = p9_virtio_request, | ||
360 | .cancel = p9_virtio_cancel, | ||
529 | .maxsize = PAGE_SIZE*16, | 361 | .maxsize = PAGE_SIZE*16, |
530 | .def = 0, | 362 | .def = 0, |
531 | .owner = THIS_MODULE, | 363 | .owner = THIS_MODULE, |