diff options
Diffstat (limited to 'net/9p/trans_virtio.c')
-rw-r--r-- | net/9p/trans_virtio.c | 355 |
1 files changed, 213 insertions, 142 deletions
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 42eea5fe2628..0117b9fb8480 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c | |||
@@ -1,17 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * The Guest 9p transport driver | 2 | * The Guest 9p transport driver |
3 | * | 3 | * |
4 | * This is a trivial pipe-based transport driver based on the lguest console | 4 | * This is a block based transport driver based on the lguest block driver |
5 | * code: we use lguest's DMA mechanism to send bytes out, and register a | 5 | * code. |
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 | * | 6 | * |
16 | */ | 7 | */ |
17 | /* | 8 | /* |
@@ -55,11 +46,25 @@ | |||
55 | #include <linux/virtio.h> | 46 | #include <linux/virtio.h> |
56 | #include <linux/virtio_9p.h> | 47 | #include <linux/virtio_9p.h> |
57 | 48 | ||
49 | #define VIRTQUEUE_NUM 128 | ||
50 | |||
58 | /* a single mutex to manage channel initialization and attachment */ | 51 | /* a single mutex to manage channel initialization and attachment */ |
59 | static DECLARE_MUTEX(virtio_9p_lock); | 52 | static DECLARE_MUTEX(virtio_9p_lock); |
60 | /* global which tracks highest initialized channel */ | 53 | /* global which tracks highest initialized channel */ |
61 | static int chan_index; | 54 | static int chan_index; |
62 | 55 | ||
56 | #define P9_INIT_MAXTAG 16 | ||
57 | |||
58 | #define REQ_STATUS_IDLE 0 | ||
59 | #define REQ_STATUS_SENT 1 | ||
60 | #define REQ_STATUS_RCVD 2 | ||
61 | #define REQ_STATUS_FLSH 3 | ||
62 | |||
63 | struct p9_req_t { | ||
64 | int status; | ||
65 | wait_queue_head_t *wq; | ||
66 | }; | ||
67 | |||
63 | /* We keep all per-channel information in a structure. | 68 | /* We keep all per-channel information in a structure. |
64 | * This structure is allocated within the devices dev->mem space. | 69 | * This structure is allocated within the devices dev->mem space. |
65 | * A pointer to the structure will get put in the transport private. | 70 | * A pointer to the structure will get put in the transport private. |
@@ -68,146 +73,198 @@ static struct virtio_chan { | |||
68 | bool initialized; /* channel is initialized */ | 73 | bool initialized; /* channel is initialized */ |
69 | bool inuse; /* channel is in use */ | 74 | bool inuse; /* channel is in use */ |
70 | 75 | ||
71 | struct virtqueue *in_vq, *out_vq; | 76 | spinlock_t lock; |
77 | |||
72 | struct virtio_device *vdev; | 78 | struct virtio_device *vdev; |
79 | struct virtqueue *vq; | ||
73 | 80 | ||
74 | /* This is our input buffer, and how much data is left in it. */ | 81 | struct p9_idpool *tagpool; |
75 | unsigned int in_len; | 82 | struct p9_req_t *reqs; |
76 | char *in, *inbuf; | 83 | int max_tag; |
77 | 84 | ||
78 | wait_queue_head_t wq; /* waitq for buffer */ | 85 | /* Scatterlist: can be too big for stack. */ |
86 | struct scatterlist sg[VIRTQUEUE_NUM]; | ||
79 | } channels[MAX_9P_CHAN]; | 87 | } channels[MAX_9P_CHAN]; |
80 | 88 | ||
89 | /* Lookup requests by tag */ | ||
90 | static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag) | ||
91 | { | ||
92 | /* This looks up the original request by tag so we know which | ||
93 | * buffer to read the data into */ | ||
94 | tag++; | ||
95 | |||
96 | while (tag >= c->max_tag) { | ||
97 | int old_max = c->max_tag; | ||
98 | int count; | ||
99 | |||
100 | if (c->max_tag) | ||
101 | c->max_tag *= 2; | ||
102 | else | ||
103 | c->max_tag = P9_INIT_MAXTAG; | ||
104 | |||
105 | c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag, | ||
106 | GFP_ATOMIC); | ||
107 | if (!c->reqs) { | ||
108 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
109 | BUG(); | ||
110 | } | ||
111 | for (count = old_max; count < c->max_tag; count++) { | ||
112 | c->reqs[count].status = REQ_STATUS_IDLE; | ||
113 | c->reqs[count].wq = kmalloc(sizeof(wait_queue_t), | ||
114 | GFP_ATOMIC); | ||
115 | if (!c->reqs[count].wq) { | ||
116 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
117 | BUG(); | ||
118 | } | ||
119 | init_waitqueue_head(c->reqs[count].wq); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | return &c->reqs[tag]; | ||
124 | } | ||
125 | |||
126 | |||
81 | /* How many bytes left in this page. */ | 127 | /* How many bytes left in this page. */ |
82 | static unsigned int rest_of_page(void *data) | 128 | static unsigned int rest_of_page(void *data) |
83 | { | 129 | { |
84 | return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE); | 130 | return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE); |
85 | } | 131 | } |
86 | 132 | ||
87 | static int p9_virtio_write(struct p9_trans *trans, void *buf, int count) | 133 | static void p9_virtio_close(struct p9_trans *trans) |
88 | { | 134 | { |
89 | struct virtio_chan *chan = (struct virtio_chan *) trans->priv; | 135 | struct virtio_chan *chan = trans->priv; |
90 | struct virtqueue *out_vq = chan->out_vq; | 136 | int count; |
91 | struct scatterlist sg[1]; | 137 | unsigned int flags; |
92 | unsigned int len; | ||
93 | 138 | ||
94 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio write (%d)\n", count); | 139 | spin_lock_irqsave(&chan->lock, flags); |
140 | p9_idpool_destroy(chan->tagpool); | ||
141 | for (count = 0; count < chan->max_tag; count++) | ||
142 | kfree(chan->reqs[count].wq); | ||
143 | kfree(chan->reqs); | ||
144 | chan->max_tag = 0; | ||
145 | spin_unlock_irqrestore(&chan->lock, flags); | ||
95 | 146 | ||
96 | /* keep it simple - make sure we don't overflow a page */ | 147 | down(&virtio_9p_lock); |
97 | if (rest_of_page(buf) < count) | 148 | chan->inuse = false; |
98 | count = rest_of_page(buf); | 149 | up(&virtio_9p_lock); |
99 | 150 | ||
100 | sg_init_one(sg, buf, count); | 151 | kfree(trans); |
152 | } | ||
101 | 153 | ||
102 | /* add_buf wants a token to identify this buffer: we hand it any | 154 | static void req_done(struct virtqueue *vq) |
103 | * non-NULL pointer, since there's only ever one buffer. */ | 155 | { |
104 | if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) { | 156 | struct virtio_chan *chan = vq->vdev->priv; |
105 | /* Tell Host to go! */ | 157 | struct p9_fcall *rc; |
106 | out_vq->vq_ops->kick(out_vq); | 158 | unsigned int len; |
107 | /* Chill out until it's done with the buffer. */ | 159 | unsigned long flags; |
108 | while (!out_vq->vq_ops->get_buf(out_vq, &len)) | 160 | struct p9_req_t *req; |
109 | cpu_relax(); | 161 | |
162 | spin_lock_irqsave(&chan->lock, flags); | ||
163 | while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) { | ||
164 | req = p9_lookup_tag(chan, rc->tag); | ||
165 | req->status = REQ_STATUS_RCVD; | ||
166 | wake_up(req->wq); | ||
110 | } | 167 | } |
111 | 168 | /* In case queue is stopped waiting for more buffers. */ | |
112 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio wrote (%d)\n", count); | 169 | spin_unlock_irqrestore(&chan->lock, flags); |
113 | |||
114 | /* We're expected to return the amount of data we wrote: all of it. */ | ||
115 | return count; | ||
116 | } | 170 | } |
117 | 171 | ||
118 | /* Create a scatter-gather list representing our input buffer and put it in the | 172 | static int |
119 | * queue. */ | 173 | pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, |
120 | static void add_inbuf(struct virtio_chan *chan) | 174 | int count) |
121 | { | 175 | { |
122 | struct scatterlist sg[1]; | 176 | int s; |
123 | 177 | int index = start; | |
124 | sg_init_one(sg, chan->inbuf, PAGE_SIZE); | 178 | |
179 | while (count) { | ||
180 | s = rest_of_page(data); | ||
181 | if (s > count) | ||
182 | s = count; | ||
183 | sg_set_buf(&sg[index++], data, s); | ||
184 | count -= s; | ||
185 | data += s; | ||
186 | if (index > limit) | ||
187 | BUG(); | ||
188 | } | ||
125 | 189 | ||
126 | /* We should always be able to add one buffer to an empty queue. */ | 190 | return index-start; |
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 | } | 191 | } |
131 | 192 | ||
132 | static int p9_virtio_read(struct p9_trans *trans, void *buf, int count) | 193 | static int |
194 | p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc) | ||
133 | { | 195 | { |
134 | struct virtio_chan *chan = (struct virtio_chan *) trans->priv; | 196 | int in, out; |
135 | struct virtqueue *in_vq = chan->in_vq; | 197 | int n, err, size; |
136 | 198 | struct virtio_chan *chan = t->priv; | |
137 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio read (%d)\n", count); | 199 | char *rdata; |
200 | struct p9_req_t *req; | ||
201 | unsigned long flags; | ||
202 | |||
203 | if (*rc == NULL) { | ||
204 | *rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL); | ||
205 | if (!*rc) | ||
206 | return -ENOMEM; | ||
207 | } | ||
138 | 208 | ||
139 | /* If we don't have an input queue yet, we can't get input. */ | 209 | rdata = (char *)*rc+sizeof(struct p9_fcall); |
140 | BUG_ON(!in_vq); | ||
141 | 210 | ||
142 | /* No buffer? Try to get one. */ | 211 | n = P9_NOTAG; |
143 | if (!chan->in_len) { | 212 | if (tc->id != P9_TVERSION) { |
144 | chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len); | 213 | n = p9_idpool_get(chan->tagpool); |
145 | if (!chan->in) | 214 | if (n < 0) |
146 | return 0; | 215 | return -ENOMEM; |
147 | } | 216 | } |
148 | 217 | ||
149 | /* You want more than we have to give? Well, try wanting less! */ | 218 | spin_lock_irqsave(&chan->lock, flags); |
150 | if (chan->in_len < count) | 219 | req = p9_lookup_tag(chan, n); |
151 | count = chan->in_len; | 220 | spin_unlock_irqrestore(&chan->lock, flags); |
152 | 221 | ||
153 | /* Copy across to their buffer and increment offset. */ | 222 | p9_set_tag(tc, n); |
154 | memcpy(buf, chan->in, count); | ||
155 | chan->in += count; | ||
156 | chan->in_len -= count; | ||
157 | 223 | ||
158 | /* Finished? Re-register buffer so Host will use it again. */ | 224 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n); |
159 | if (chan->in_len == 0) | ||
160 | add_inbuf(chan); | ||
161 | 225 | ||
162 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio finished read (%d)\n", | 226 | out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size); |
163 | count); | 227 | in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, t->msize); |
164 | 228 | ||
165 | return count; | 229 | req->status = REQ_STATUS_SENT; |
166 | } | ||
167 | 230 | ||
168 | /* The poll function is used by 9p transports to determine if there | 231 | if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) { |
169 | * is there is activity available on a particular channel. In our case | 232 | P9_DPRINTK(P9_DEBUG_TRANS, |
170 | * we use it to wait for a callback from the input routines. | 233 | "9p debug: virtio rpc add_buf returned failure"); |
171 | */ | 234 | return -EIO; |
172 | static unsigned int | 235 | } |
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 | 236 | ||
179 | poll_wait(NULL, &chan->wq, pt); | 237 | chan->vq->vq_ops->kick(chan->vq); |
180 | 238 | ||
181 | /* No buffer? Try to get one. */ | 239 | wait_event(*req->wq, req->status == REQ_STATUS_RCVD); |
182 | if (!chan->in_len) | ||
183 | chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len); | ||
184 | 240 | ||
185 | if (chan->in_len) | 241 | size = le32_to_cpu(*(__le32 *) rdata); |
186 | ret |= POLLIN; | ||
187 | 242 | ||
188 | return ret; | 243 | err = p9_deserialize_fcall(rdata, size, *rc, t->extended); |
189 | } | 244 | if (err < 0) { |
245 | P9_DPRINTK(P9_DEBUG_TRANS, | ||
246 | "9p debug: virtio rpc deserialize returned %d\n", err); | ||
247 | return err; | ||
248 | } | ||
190 | 249 | ||
191 | static void p9_virtio_close(struct p9_trans *trans) | 250 | #ifdef CONFIG_NET_9P_DEBUG |
192 | { | 251 | if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { |
193 | struct virtio_chan *chan = trans->priv; | 252 | char buf[150]; |
194 | 253 | ||
195 | down(&virtio_9p_lock); | 254 | p9_printfcall(buf, sizeof(buf), *rc, t->extended); |
196 | chan->inuse = false; | 255 | printk(KERN_NOTICE ">>> %p %s\n", t, buf); |
197 | up(&virtio_9p_lock); | 256 | } |
257 | #endif | ||
198 | 258 | ||
199 | kfree(trans); | 259 | if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool)) |
200 | } | 260 | p9_idpool_put(n, chan->tagpool); |
201 | 261 | ||
202 | static void p9_virtio_intr(struct virtqueue *q) | 262 | req->status = REQ_STATUS_IDLE; |
203 | { | ||
204 | struct virtio_chan *chan = q->vdev->priv; | ||
205 | 263 | ||
206 | P9_DPRINTK(P9_DEBUG_TRANS, "9p poll_wakeup: %p\n", &chan->wq); | 264 | return 0; |
207 | wake_up_interruptible(&chan->wq); | ||
208 | } | 265 | } |
209 | 266 | ||
210 | static int p9_virtio_probe(struct virtio_device *dev) | 267 | static int p9_virtio_probe(struct virtio_device *vdev) |
211 | { | 268 | { |
212 | int err; | 269 | int err; |
213 | struct virtio_chan *chan; | 270 | struct virtio_chan *chan; |
@@ -221,44 +278,29 @@ static int p9_virtio_probe(struct virtio_device *dev) | |||
221 | if (chan_index > MAX_9P_CHAN) { | 278 | if (chan_index > MAX_9P_CHAN) { |
222 | printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n"); | 279 | printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n"); |
223 | BUG(); | 280 | BUG(); |
224 | } | ||
225 | |||
226 | chan->vdev = dev; | ||
227 | |||
228 | /* This is the scratch page we use to receive console input */ | ||
229 | chan->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
230 | if (!chan->inbuf) { | ||
231 | err = -ENOMEM; | 281 | err = -ENOMEM; |
232 | goto fail; | 282 | goto fail; |
233 | } | 283 | } |
234 | 284 | ||
235 | /* Find the input queue. */ | 285 | chan->vdev = vdev; |
236 | dev->priv = chan; | ||
237 | chan->in_vq = dev->config->find_vq(dev, 0, p9_virtio_intr); | ||
238 | if (IS_ERR(chan->in_vq)) { | ||
239 | err = PTR_ERR(chan->in_vq); | ||
240 | goto free; | ||
241 | } | ||
242 | 286 | ||
243 | chan->out_vq = dev->config->find_vq(dev, 1, NULL); | 287 | /* We expect one virtqueue, for requests. */ |
244 | if (IS_ERR(chan->out_vq)) { | 288 | chan->vq = vdev->config->find_vq(vdev, 0, req_done); |
245 | err = PTR_ERR(chan->out_vq); | 289 | if (IS_ERR(chan->vq)) { |
246 | goto free_in_vq; | 290 | err = PTR_ERR(chan->vq); |
291 | goto out_free_vq; | ||
247 | } | 292 | } |
293 | chan->vq->vdev->priv = chan; | ||
294 | spin_lock_init(&chan->lock); | ||
248 | 295 | ||
249 | init_waitqueue_head(&chan->wq); | 296 | sg_init_table(chan->sg, VIRTQUEUE_NUM); |
250 | 297 | ||
251 | /* Register the input buffer the first time. */ | ||
252 | add_inbuf(chan); | ||
253 | chan->inuse = false; | 298 | chan->inuse = false; |
254 | chan->initialized = true; | 299 | chan->initialized = true; |
255 | |||
256 | return 0; | 300 | return 0; |
257 | 301 | ||
258 | free_in_vq: | 302 | out_free_vq: |
259 | dev->config->del_vq(chan->in_vq); | 303 | vdev->config->del_vq(chan->vq); |
260 | free: | ||
261 | kfree(chan->inbuf); | ||
262 | fail: | 304 | fail: |
263 | down(&virtio_9p_lock); | 305 | down(&virtio_9p_lock); |
264 | chan_index--; | 306 | chan_index--; |
@@ -271,11 +313,13 @@ fail: | |||
271 | * alternate channels by matching devname versus a virtio_config entry. | 313 | * alternate channels by matching devname versus a virtio_config entry. |
272 | * We use a simple reference count mechanism to ensure that only a single | 314 | * We use a simple reference count mechanism to ensure that only a single |
273 | * mount has a channel open at a time. */ | 315 | * mount has a channel open at a time. */ |
274 | static struct p9_trans *p9_virtio_create(const char *devname, char *args) | 316 | static struct p9_trans * |
317 | p9_virtio_create(const char *devname, char *args, int msize, | ||
318 | unsigned char extended) | ||
275 | { | 319 | { |
276 | struct p9_trans *trans; | 320 | struct p9_trans *trans; |
277 | int index = 0; | ||
278 | struct virtio_chan *chan = channels; | 321 | struct virtio_chan *chan = channels; |
322 | int index = 0; | ||
279 | 323 | ||
280 | down(&virtio_9p_lock); | 324 | down(&virtio_9p_lock); |
281 | while (index < MAX_9P_CHAN) { | 325 | while (index < MAX_9P_CHAN) { |
@@ -290,25 +334,45 @@ static struct p9_trans *p9_virtio_create(const char *devname, char *args) | |||
290 | up(&virtio_9p_lock); | 334 | up(&virtio_9p_lock); |
291 | 335 | ||
292 | if (index >= MAX_9P_CHAN) { | 336 | if (index >= MAX_9P_CHAN) { |
293 | printk(KERN_ERR "9p: virtio: couldn't find a free channel\n"); | 337 | printk(KERN_ERR "9p: no channels available\n"); |
294 | return NULL; | 338 | return ERR_PTR(-ENODEV); |
295 | } | 339 | } |
296 | 340 | ||
341 | chan->tagpool = p9_idpool_create(); | ||
342 | if (IS_ERR(chan->tagpool)) { | ||
343 | printk(KERN_ERR "9p: couldn't allocate tagpool\n"); | ||
344 | return ERR_PTR(-ENOMEM); | ||
345 | } | ||
346 | p9_idpool_get(chan->tagpool); /* reserve tag 0 */ | ||
347 | chan->max_tag = 0; | ||
348 | chan->reqs = NULL; | ||
349 | |||
297 | trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); | 350 | trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); |
298 | if (!trans) { | 351 | if (!trans) { |
299 | printk(KERN_ERR "9p: couldn't allocate transport\n"); | 352 | printk(KERN_ERR "9p: couldn't allocate transport\n"); |
300 | return ERR_PTR(-ENOMEM); | 353 | return ERR_PTR(-ENOMEM); |
301 | } | 354 | } |
302 | 355 | trans->extended = extended; | |
303 | trans->write = p9_virtio_write; | 356 | trans->msize = msize; |
304 | trans->read = p9_virtio_read; | ||
305 | trans->close = p9_virtio_close; | 357 | trans->close = p9_virtio_close; |
306 | trans->poll = p9_virtio_poll; | 358 | trans->rpc = p9_virtio_rpc; |
307 | trans->priv = chan; | 359 | trans->priv = chan; |
308 | 360 | ||
309 | return trans; | 361 | return trans; |
310 | } | 362 | } |
311 | 363 | ||
364 | static void p9_virtio_remove(struct virtio_device *vdev) | ||
365 | { | ||
366 | struct virtio_chan *chan = vdev->priv; | ||
367 | |||
368 | BUG_ON(chan->inuse); | ||
369 | |||
370 | if (chan->initialized) { | ||
371 | vdev->config->del_vq(chan->vq); | ||
372 | chan->initialized = false; | ||
373 | } | ||
374 | } | ||
375 | |||
312 | #define VIRTIO_ID_9P 9 | 376 | #define VIRTIO_ID_9P 9 |
313 | 377 | ||
314 | static struct virtio_device_id id_table[] = { | 378 | static struct virtio_device_id id_table[] = { |
@@ -322,12 +386,13 @@ static struct virtio_driver p9_virtio_drv = { | |||
322 | .driver.owner = THIS_MODULE, | 386 | .driver.owner = THIS_MODULE, |
323 | .id_table = id_table, | 387 | .id_table = id_table, |
324 | .probe = p9_virtio_probe, | 388 | .probe = p9_virtio_probe, |
389 | .remove = p9_virtio_remove, | ||
325 | }; | 390 | }; |
326 | 391 | ||
327 | static struct p9_trans_module p9_virtio_trans = { | 392 | static struct p9_trans_module p9_virtio_trans = { |
328 | .name = "virtio", | 393 | .name = "virtio", |
329 | .create = p9_virtio_create, | 394 | .create = p9_virtio_create, |
330 | .maxsize = PAGE_SIZE, | 395 | .maxsize = PAGE_SIZE*16, |
331 | .def = 0, | 396 | .def = 0, |
332 | }; | 397 | }; |
333 | 398 | ||
@@ -343,7 +408,13 @@ static int __init p9_virtio_init(void) | |||
343 | return register_virtio_driver(&p9_virtio_drv); | 408 | return register_virtio_driver(&p9_virtio_drv); |
344 | } | 409 | } |
345 | 410 | ||
411 | static void __exit p9_virtio_cleanup(void) | ||
412 | { | ||
413 | unregister_virtio_driver(&p9_virtio_drv); | ||
414 | } | ||
415 | |||
346 | module_init(p9_virtio_init); | 416 | module_init(p9_virtio_init); |
417 | module_exit(p9_virtio_cleanup); | ||
347 | 418 | ||
348 | MODULE_DEVICE_TABLE(virtio, id_table); | 419 | MODULE_DEVICE_TABLE(virtio, id_table); |
349 | MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); | 420 | MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); |