diff options
Diffstat (limited to 'net/9p/trans_virtio.c')
-rw-r--r-- | net/9p/trans_virtio.c | 136 |
1 files changed, 9 insertions, 127 deletions
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 72493f04a76d..36bce45e4e44 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 |
@@ -54,49 +52,6 @@ static DEFINE_MUTEX(virtio_9p_lock); | |||
54 | /* global which tracks highest initialized channel */ | 52 | /* global which tracks highest initialized channel */ |
55 | static int chan_index; | 53 | static int chan_index; |
56 | 54 | ||
57 | #define P9_INIT_MAXTAG 16 | ||
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 | { |
@@ -200,22 +102,10 @@ static unsigned int rest_of_page(void *data) | |||
200 | static void p9_virtio_close(struct p9_client *client) | 102 | static void p9_virtio_close(struct p9_client *client) |
201 | { | 103 | { |
202 | struct virtio_chan *chan = client->trans; | 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 | client->trans = NULL; | ||
219 | } | 109 | } |
220 | 110 | ||
221 | /** | 111 | /** |
@@ -241,7 +131,7 @@ static void req_done(struct virtqueue *vq) | |||
241 | 131 | ||
242 | spin_lock_irqsave(&chan->lock, flags); | 132 | spin_lock_irqsave(&chan->lock, flags); |
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 | req = p9_tag_lookup(chan->client, rc->tag); |
245 | req->status = REQ_STATUS_RCVD; | 135 | req->status = REQ_STATUS_RCVD; |
246 | wake_up(req->wq); | 136 | wake_up(req->wq); |
247 | } | 137 | } |
@@ -311,13 +201,13 @@ p9_virtio_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc) | |||
311 | 201 | ||
312 | n = P9_NOTAG; | 202 | n = P9_NOTAG; |
313 | if (tc->id != P9_TVERSION) { | 203 | if (tc->id != P9_TVERSION) { |
314 | n = p9_idpool_get(chan->tagpool); | 204 | n = p9_idpool_get(c->tagpool); |
315 | if (n < 0) | 205 | if (n < 0) |
316 | return -ENOMEM; | 206 | return -ENOMEM; |
317 | } | 207 | } |
318 | 208 | ||
319 | spin_lock_irqsave(&chan->lock, flags); | 209 | spin_lock_irqsave(&chan->lock, flags); |
320 | req = p9_lookup_tag(chan, n); | 210 | req = p9_tag_alloc(c, n); |
321 | spin_unlock_irqrestore(&chan->lock, flags); | 211 | spin_unlock_irqrestore(&chan->lock, flags); |
322 | 212 | ||
323 | p9_set_tag(tc, n); | 213 | p9_set_tag(tc, n); |
@@ -357,8 +247,8 @@ p9_virtio_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc) | |||
357 | } | 247 | } |
358 | #endif | 248 | #endif |
359 | 249 | ||
360 | if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool)) | 250 | if (n != P9_NOTAG && p9_idpool_check(n, c->tagpool)) |
361 | p9_idpool_put(n, chan->tagpool); | 251 | p9_idpool_put(n, c->tagpool); |
362 | 252 | ||
363 | req->status = REQ_STATUS_IDLE; | 253 | req->status = REQ_STATUS_IDLE; |
364 | 254 | ||
@@ -463,16 +353,8 @@ p9_virtio_create(struct p9_client *client, const char *devname, char *args) | |||
463 | return -ENODEV; | 353 | return -ENODEV; |
464 | } | 354 | } |
465 | 355 | ||
466 | chan->tagpool = p9_idpool_create(); | ||
467 | if (IS_ERR(chan->tagpool)) { | ||
468 | printk(KERN_ERR "9p: couldn't allocate tagpool\n"); | ||
469 | return -ENOMEM; | ||
470 | } | ||
471 | p9_idpool_get(chan->tagpool); /* reserve tag 0 */ | ||
472 | chan->max_tag = 0; | ||
473 | chan->reqs = NULL; | ||
474 | |||
475 | client->trans = (void *)chan; | 356 | client->trans = (void *)chan; |
357 | chan->client = client; | ||
476 | 358 | ||
477 | return 0; | 359 | return 0; |
478 | } | 360 | } |