diff options
Diffstat (limited to 'net/9p')
-rw-r--r-- | net/9p/client.c | 152 | ||||
-rw-r--r-- | net/9p/trans_virtio.c | 136 |
2 files changed, 161 insertions, 127 deletions
diff --git a/net/9p/client.c b/net/9p/client.c index 712d4f336adc..867031934f75 100644 --- a/net/9p/client.c +++ b/net/9p/client.c | |||
@@ -120,6 +120,154 @@ static int parse_opts(char *opts, struct p9_client *clnt) | |||
120 | return ret; | 120 | return ret; |
121 | } | 121 | } |
122 | 122 | ||
123 | /** | ||
124 | * p9_tag_alloc - lookup/allocate a request by tag | ||
125 | * @c: client session to lookup tag within | ||
126 | * @tag: numeric id for transaction | ||
127 | * | ||
128 | * this is a simple array lookup, but will grow the | ||
129 | * request_slots as necessary to accomodate transaction | ||
130 | * ids which did not previously have a slot. | ||
131 | * | ||
132 | * this code relies on the client spinlock to manage locks, its | ||
133 | * possible we should switch to something else, but I'd rather | ||
134 | * stick with something low-overhead for the common case. | ||
135 | * | ||
136 | */ | ||
137 | |||
138 | struct p9_req_t *p9_tag_alloc(struct p9_client *c, u16 tag) | ||
139 | { | ||
140 | unsigned long flags; | ||
141 | int row, col; | ||
142 | |||
143 | /* This looks up the original request by tag so we know which | ||
144 | * buffer to read the data into */ | ||
145 | tag++; | ||
146 | |||
147 | if (tag >= c->max_tag) { | ||
148 | spin_lock_irqsave(&c->lock, flags); | ||
149 | /* check again since original check was outside of lock */ | ||
150 | while (tag >= c->max_tag) { | ||
151 | row = (tag / P9_ROW_MAXTAG); | ||
152 | c->reqs[row] = kcalloc(P9_ROW_MAXTAG, | ||
153 | sizeof(struct p9_req_t), GFP_ATOMIC); | ||
154 | |||
155 | if (!c->reqs[row]) { | ||
156 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
157 | BUG(); | ||
158 | } | ||
159 | for (col = 0; col < P9_ROW_MAXTAG; col++) { | ||
160 | c->reqs[row][col].status = REQ_STATUS_IDLE; | ||
161 | c->reqs[row][col].flush_tag = P9_NOTAG; | ||
162 | c->reqs[row][col].wq = kmalloc( | ||
163 | sizeof(wait_queue_head_t), GFP_ATOMIC); | ||
164 | if (!c->reqs[row][col].wq) { | ||
165 | printk(KERN_ERR | ||
166 | "Couldn't grow tag array\n"); | ||
167 | BUG(); | ||
168 | } | ||
169 | init_waitqueue_head(c->reqs[row][col].wq); | ||
170 | } | ||
171 | c->max_tag += P9_ROW_MAXTAG; | ||
172 | } | ||
173 | spin_unlock_irqrestore(&c->lock, flags); | ||
174 | } | ||
175 | row = tag / P9_ROW_MAXTAG; | ||
176 | col = tag % P9_ROW_MAXTAG; | ||
177 | |||
178 | c->reqs[row][col].status = REQ_STATUS_ALLOC; | ||
179 | c->reqs[row][col].flush_tag = P9_NOTAG; | ||
180 | |||
181 | return &c->reqs[row][col]; | ||
182 | } | ||
183 | EXPORT_SYMBOL(p9_tag_alloc); | ||
184 | |||
185 | /** | ||
186 | * p9_tag_lookup - lookup a request by tag | ||
187 | * @c: client session to lookup tag within | ||
188 | * @tag: numeric id for transaction | ||
189 | * | ||
190 | */ | ||
191 | |||
192 | struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag) | ||
193 | { | ||
194 | int row, col; | ||
195 | |||
196 | /* This looks up the original request by tag so we know which | ||
197 | * buffer to read the data into */ | ||
198 | tag++; | ||
199 | |||
200 | BUG_ON(tag >= c->max_tag); | ||
201 | |||
202 | row = tag / P9_ROW_MAXTAG; | ||
203 | col = tag % P9_ROW_MAXTAG; | ||
204 | |||
205 | return &c->reqs[row][col]; | ||
206 | } | ||
207 | EXPORT_SYMBOL(p9_tag_lookup); | ||
208 | |||
209 | /** | ||
210 | * p9_tag_init - setup tags structure and contents | ||
211 | * @tags: tags structure from the client struct | ||
212 | * | ||
213 | * This initializes the tags structure for each client instance. | ||
214 | * | ||
215 | */ | ||
216 | |||
217 | static int p9_tag_init(struct p9_client *c) | ||
218 | { | ||
219 | int err = 0; | ||
220 | |||
221 | c->tagpool = p9_idpool_create(); | ||
222 | if (IS_ERR(c->tagpool)) { | ||
223 | err = PTR_ERR(c->tagpool); | ||
224 | c->tagpool = NULL; | ||
225 | goto error; | ||
226 | } | ||
227 | |||
228 | p9_idpool_get(c->tagpool); /* reserve tag 0 */ | ||
229 | |||
230 | c->max_tag = 0; | ||
231 | error: | ||
232 | return err; | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * p9_tag_cleanup - cleans up tags structure and reclaims resources | ||
237 | * @tags: tags structure from the client struct | ||
238 | * | ||
239 | * This frees resources associated with the tags structure | ||
240 | * | ||
241 | */ | ||
242 | static void p9_tag_cleanup(struct p9_client *c) | ||
243 | { | ||
244 | int row, col; | ||
245 | |||
246 | /* check to insure all requests are idle */ | ||
247 | for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) { | ||
248 | for (col = 0; col < P9_ROW_MAXTAG; col++) { | ||
249 | if (c->reqs[row][col].status != REQ_STATUS_IDLE) { | ||
250 | P9_DPRINTK(P9_DEBUG_MUX, | ||
251 | "Attempting to cleanup non-free tag %d,%d\n", | ||
252 | row, col); | ||
253 | /* TODO: delay execution of cleanup */ | ||
254 | return; | ||
255 | } | ||
256 | } | ||
257 | } | ||
258 | |||
259 | if (c->tagpool) | ||
260 | p9_idpool_destroy(c->tagpool); | ||
261 | |||
262 | /* free requests associated with tags */ | ||
263 | for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) { | ||
264 | for (col = 0; col < P9_ROW_MAXTAG; col++) | ||
265 | kfree(c->reqs[row][col].wq); | ||
266 | kfree(c->reqs[row]); | ||
267 | } | ||
268 | c->max_tag = 0; | ||
269 | } | ||
270 | |||
123 | static struct p9_fid *p9_fid_create(struct p9_client *clnt) | 271 | static struct p9_fid *p9_fid_create(struct p9_client *clnt) |
124 | { | 272 | { |
125 | int err; | 273 | int err; |
@@ -209,6 +357,8 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) | |||
209 | goto error; | 357 | goto error; |
210 | } | 358 | } |
211 | 359 | ||
360 | p9_tag_init(clnt); | ||
361 | |||
212 | err = parse_opts(options, clnt); | 362 | err = parse_opts(options, clnt); |
213 | if (err < 0) | 363 | if (err < 0) |
214 | goto error; | 364 | goto error; |
@@ -285,6 +435,8 @@ void p9_client_destroy(struct p9_client *clnt) | |||
285 | if (clnt->fidpool) | 435 | if (clnt->fidpool) |
286 | p9_idpool_destroy(clnt->fidpool); | 436 | p9_idpool_destroy(clnt->fidpool); |
287 | 437 | ||
438 | p9_tag_cleanup(clnt); | ||
439 | |||
288 | kfree(clnt); | 440 | kfree(clnt); |
289 | } | 441 | } |
290 | EXPORT_SYMBOL(p9_client_destroy); | 442 | EXPORT_SYMBOL(p9_client_destroy); |
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 | } |