aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Van Hensbergen <ericvh@gmail.com>2008-10-13 19:45:23 -0400
committerEric Van Hensbergen <ericvh@gmail.com>2008-10-17 12:04:42 -0400
commitfea511a644fb0fb938309c6ab286725ac31b87e2 (patch)
treefc9cdf7af5ad05435ea85fd52070a487930f824c
parent044c7768841f1ef39f951972d3c1e6537a535030 (diff)
9p: move request management to client code
The virtio transport uses a simplified request management system that I want to use for all transports. This patch adapts and moves the exisiting code for managing requests to the client common code. Later patches will apply these mechanisms to the other transports. Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
-rw-r--r--include/net/9p/client.h77
-rw-r--r--net/9p/client.c152
-rw-r--r--net/9p/trans_virtio.c136
3 files changed, 238 insertions, 127 deletions
diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index c35fb548e7cf..140cf1d58452 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -26,6 +26,9 @@
26#ifndef NET_9P_CLIENT_H 26#ifndef NET_9P_CLIENT_H
27#define NET_9P_CLIENT_H 27#define NET_9P_CLIENT_H
28 28
29/* Number of requests per row */
30#define P9_ROW_MAXTAG 255
31
29/** 32/**
30 * enum p9_trans_status - different states of underlying transports 33 * enum p9_trans_status - different states of underlying transports
31 * @Connected: transport is connected and healthy 34 * @Connected: transport is connected and healthy
@@ -43,6 +46,62 @@ enum p9_trans_status {
43}; 46};
44 47
45/** 48/**
49 * enum p9_req_status_t - virtio request status
50 * @REQ_STATUS_IDLE: request slot unused
51 * @REQ_STATUS_ALLOC: request has been allocated but not sent
52 * @REQ_STATUS_SENT: request sent to server
53 * @REQ_STATUS_FLSH: a flush has been sent for this request
54 * @REQ_STATUS_RCVD: response received from server
55 * @REQ_STATUS_FLSHD: request has been flushed
56 * @REQ_STATUS_ERR: request encountered an error on the client side
57 *
58 * The @REQ_STATUS_IDLE state is used to mark a request slot as unused
59 * but use is actually tracked by the idpool structure which handles tag
60 * id allocation.
61 *
62 */
63
64enum p9_req_status_t {
65 REQ_STATUS_IDLE,
66 REQ_STATUS_ALLOC,
67 REQ_STATUS_SENT,
68 REQ_STATUS_FLSH,
69 REQ_STATUS_RCVD,
70 REQ_STATUS_FLSHD,
71 REQ_STATUS_ERROR,
72};
73
74/**
75 * struct p9_req_t - request slots
76 * @status: status of this request slot
77 * @t_err: transport error
78 * @wq: wait_queue for the client to block on for this request
79 * @tc: the request fcall structure
80 * @rc: the response fcall structure
81 * @aux: transport specific data (provided for trans_fd migration)
82 *
83 * Transport use an array to track outstanding requests
84 * instead of a list. While this may incurr overhead during initial
85 * allocation or expansion, it makes request lookup much easier as the
86 * tag id is a index into an array. (We use tag+1 so that we can accomodate
87 * the -1 tag for the T_VERSION request).
88 * This also has the nice effect of only having to allocate wait_queues
89 * once, instead of constantly allocating and freeing them. Its possible
90 * other resources could benefit from this scheme as well.
91 *
92 */
93
94struct p9_req_t {
95 int status;
96 int t_err;
97 wait_queue_head_t *wq;
98 struct p9_fcall *tc;
99 struct p9_fcall *rc;
100 u16 flush_tag;
101 void *aux;
102};
103
104/**
46 * struct p9_client - per client instance state 105 * struct p9_client - per client instance state
47 * @lock: protect @fidlist 106 * @lock: protect @fidlist
48 * @msize: maximum data size negotiated by protocol 107 * @msize: maximum data size negotiated by protocol
@@ -52,9 +111,20 @@ enum p9_trans_status {
52 * @conn: connection state information used by trans_fd 111 * @conn: connection state information used by trans_fd
53 * @fidpool: fid handle accounting for session 112 * @fidpool: fid handle accounting for session
54 * @fidlist: List of active fid handles 113 * @fidlist: List of active fid handles
114 * @tagpool - transaction id accounting for session
115 * @reqs - 2D array of requests
116 * @max_tag - current maximum tag id allocated
55 * 117 *
56 * The client structure is used to keep track of various per-client 118 * The client structure is used to keep track of various per-client
57 * state that has been instantiated. 119 * state that has been instantiated.
120 * In order to minimize per-transaction overhead we use a
121 * simple array to lookup requests instead of a hash table
122 * or linked list. In order to support larger number of
123 * transactions, we make this a 2D array, allocating new rows
124 * when we need to grow the total number of the transactions.
125 *
126 * Each row is 256 requests and we'll support up to 256 rows for
127 * a total of 64k concurrent requests per session.
58 * 128 *
59 * Bugs: duplicated data and potentially unnecessary elements. 129 * Bugs: duplicated data and potentially unnecessary elements.
60 */ 130 */
@@ -70,6 +140,10 @@ struct p9_client {
70 140
71 struct p9_idpool *fidpool; 141 struct p9_idpool *fidpool;
72 struct list_head fidlist; 142 struct list_head fidlist;
143
144 struct p9_idpool *tagpool;
145 struct p9_req_t *reqs[P9_ROW_MAXTAG];
146 int max_tag;
73}; 147};
74 148
75/** 149/**
@@ -131,4 +205,7 @@ struct p9_stat *p9_client_stat(struct p9_fid *fid);
131int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); 205int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst);
132struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset); 206struct p9_stat *p9_client_dirread(struct p9_fid *fid, u64 offset);
133 207
208struct p9_req_t *p9_tag_alloc(struct p9_client *, u16);
209struct p9_req_t *p9_tag_lookup(struct p9_client *, u16);
210
134#endif /* NET_9P_CLIENT_H */ 211#endif /* NET_9P_CLIENT_H */
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
138struct 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}
183EXPORT_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
192struct 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}
207EXPORT_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
217static 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;
231error:
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 */
242static 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
123static struct p9_fid *p9_fid_create(struct p9_client *clnt) 271static 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}
290EXPORT_SYMBOL(p9_client_destroy); 442EXPORT_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 */
55static int chan_index; 53static 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
72enum 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
95struct 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
148static 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. */
186static unsigned int rest_of_page(void *data) 88static unsigned int rest_of_page(void *data)
187{ 89{
@@ -200,22 +102,10 @@ static unsigned int rest_of_page(void *data)
200static void p9_virtio_close(struct p9_client *client) 102static 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}