| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 1 | /* | 
 | 2 |  * The Guest 9p transport driver | 
 | 3 |  * | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 4 |  * This is a block based transport driver based on the lguest block driver | 
 | 5 |  * code. | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 6 |  * | 
 | 7 |  */ | 
 | 8 | /* | 
 | 9 |  *  Copyright (C) 2007 Eric Van Hensbergen, IBM Corporation | 
 | 10 |  * | 
 | 11 |  *  Based on virtio console driver | 
 | 12 |  *  Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation | 
 | 13 |  * | 
 | 14 |  *  This program is free software; you can redistribute it and/or modify | 
 | 15 |  *  it under the terms of the GNU General Public License version 2 | 
 | 16 |  *  as published by the Free Software Foundation. | 
 | 17 |  * | 
 | 18 |  *  This program is distributed in the hope that it will be useful, | 
 | 19 |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 20 |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 21 |  *  GNU General Public License for more details. | 
 | 22 |  * | 
 | 23 |  *  You should have received a copy of the GNU General Public License | 
 | 24 |  *  along with this program; if not, write to: | 
 | 25 |  *  Free Software Foundation | 
 | 26 |  *  51 Franklin Street, Fifth Floor | 
 | 27 |  *  Boston, MA  02111-1301  USA | 
 | 28 |  * | 
 | 29 |  */ | 
 | 30 |  | 
 | 31 | #include <linux/in.h> | 
 | 32 | #include <linux/module.h> | 
 | 33 | #include <linux/net.h> | 
 | 34 | #include <linux/ipv6.h> | 
 | 35 | #include <linux/errno.h> | 
 | 36 | #include <linux/kernel.h> | 
 | 37 | #include <linux/un.h> | 
 | 38 | #include <linux/uaccess.h> | 
 | 39 | #include <linux/inet.h> | 
 | 40 | #include <linux/idr.h> | 
 | 41 | #include <linux/file.h> | 
 | 42 | #include <net/9p/9p.h> | 
 | 43 | #include <linux/parser.h> | 
 | 44 | #include <net/9p/transport.h> | 
 | 45 | #include <linux/scatterlist.h> | 
 | 46 | #include <linux/virtio.h> | 
 | 47 | #include <linux/virtio_9p.h> | 
 | 48 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 49 | #define VIRTQUEUE_NUM	128 | 
 | 50 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 51 | /* a single mutex to manage channel initialization and attachment */ | 
 | 52 | static DECLARE_MUTEX(virtio_9p_lock); | 
 | 53 | /* global which tracks highest initialized channel */ | 
 | 54 | static int chan_index; | 
 | 55 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 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 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 68 | /* We keep all per-channel information in a structure. | 
 | 69 |  * This structure is allocated within the devices dev->mem space. | 
 | 70 |  * A pointer to the structure will get put in the transport private. | 
 | 71 |  */ | 
 | 72 | static struct virtio_chan { | 
 | 73 | 	bool initialized;		/* channel is initialized */ | 
 | 74 | 	bool inuse;			/* channel is in use */ | 
 | 75 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 76 | 	spinlock_t lock; | 
 | 77 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 78 | 	struct virtio_device *vdev; | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 79 | 	struct virtqueue *vq; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 80 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 81 | 	struct p9_idpool *tagpool; | 
 | 82 | 	struct p9_req_t *reqs; | 
 | 83 | 	int max_tag; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 84 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 85 | 	/* Scatterlist: can be too big for stack. */ | 
 | 86 | 	struct scatterlist sg[VIRTQUEUE_NUM]; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 87 | } channels[MAX_9P_CHAN]; | 
 | 88 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 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; | 
| Adrian Bunk | 15e29b8 | 2008-02-19 16:25:30 -0800 | [diff] [blame] | 113 | 			c->reqs[count].wq = kmalloc(sizeof(wait_queue_head_t), | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 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 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 127 | /* How many bytes left in this page. */ | 
 | 128 | static unsigned int rest_of_page(void *data) | 
 | 129 | { | 
 | 130 | 	return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE); | 
 | 131 | } | 
 | 132 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 133 | static void p9_virtio_close(struct p9_trans *trans) | 
 | 134 | { | 
 | 135 | 	struct virtio_chan *chan = trans->priv; | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 136 | 	int count; | 
 | 137 | 	unsigned int flags; | 
 | 138 |  | 
 | 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); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 146 |  | 
 | 147 | 	down(&virtio_9p_lock); | 
 | 148 | 	chan->inuse = false; | 
 | 149 | 	up(&virtio_9p_lock); | 
 | 150 |  | 
 | 151 | 	kfree(trans); | 
 | 152 | } | 
 | 153 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 154 | static void req_done(struct virtqueue *vq) | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 155 | { | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 156 | 	struct virtio_chan *chan = vq->vdev->priv; | 
 | 157 | 	struct p9_fcall *rc; | 
 | 158 | 	unsigned int len; | 
 | 159 | 	unsigned long flags; | 
 | 160 | 	struct p9_req_t *req; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 161 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 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); | 
 | 167 | 	} | 
 | 168 | 	/* In case queue is stopped waiting for more buffers. */ | 
 | 169 | 	spin_unlock_irqrestore(&chan->lock, flags); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 170 | } | 
 | 171 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 172 | static int | 
 | 173 | pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, | 
 | 174 | 								int count) | 
 | 175 | { | 
 | 176 | 	int s; | 
 | 177 | 	int index = start; | 
 | 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; | 
| Julia Lawall | d6584f3 | 2008-02-17 18:42:53 -0800 | [diff] [blame] | 186 | 		BUG_ON(index > limit); | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 187 | 	} | 
 | 188 |  | 
 | 189 | 	return index-start; | 
 | 190 | } | 
 | 191 |  | 
 | 192 | static int | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 193 | p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc) | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 194 | { | 
 | 195 | 	int in, out; | 
 | 196 | 	int n, err, size; | 
 | 197 | 	struct virtio_chan *chan = t->priv; | 
 | 198 | 	char *rdata; | 
 | 199 | 	struct p9_req_t *req; | 
 | 200 | 	unsigned long flags; | 
 | 201 |  | 
 | 202 | 	if (*rc == NULL) { | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 203 | 		*rc = kmalloc(sizeof(struct p9_fcall) + t->msize, GFP_KERNEL); | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 204 | 		if (!*rc) | 
 | 205 | 			return -ENOMEM; | 
 | 206 | 	} | 
 | 207 |  | 
 | 208 | 	rdata = (char *)*rc+sizeof(struct p9_fcall); | 
 | 209 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 210 | 	n = P9_NOTAG; | 
 | 211 | 	if (tc->id != P9_TVERSION) { | 
 | 212 | 		n = p9_idpool_get(chan->tagpool); | 
 | 213 | 		if (n < 0) | 
 | 214 | 			return -ENOMEM; | 
 | 215 | 	} | 
 | 216 |  | 
| Eric Van Hensbergen | 7c7d90f | 2008-02-06 19:25:07 -0600 | [diff] [blame] | 217 | 	spin_lock_irqsave(&chan->lock, flags); | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 218 | 	req = p9_lookup_tag(chan, n); | 
 | 219 | 	spin_unlock_irqrestore(&chan->lock, flags); | 
 | 220 |  | 
 | 221 | 	p9_set_tag(tc, n); | 
 | 222 |  | 
 | 223 | 	P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n); | 
 | 224 |  | 
 | 225 | 	out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size); | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 226 | 	in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, t->msize); | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 227 |  | 
 | 228 | 	req->status = REQ_STATUS_SENT; | 
 | 229 |  | 
 | 230 | 	if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) { | 
 | 231 | 		P9_DPRINTK(P9_DEBUG_TRANS, | 
 | 232 | 			"9p debug: virtio rpc add_buf returned failure"); | 
 | 233 | 		return -EIO; | 
 | 234 | 	} | 
 | 235 |  | 
 | 236 | 	chan->vq->vq_ops->kick(chan->vq); | 
 | 237 |  | 
 | 238 | 	wait_event(*req->wq, req->status == REQ_STATUS_RCVD); | 
 | 239 |  | 
 | 240 | 	size = le32_to_cpu(*(__le32 *) rdata); | 
 | 241 |  | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 242 | 	err = p9_deserialize_fcall(rdata, size, *rc, t->extended); | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 243 | 	if (err < 0) { | 
 | 244 | 		P9_DPRINTK(P9_DEBUG_TRANS, | 
 | 245 | 			"9p debug: virtio rpc deserialize returned %d\n", err); | 
 | 246 | 		return err; | 
 | 247 | 	} | 
 | 248 |  | 
 | 249 | #ifdef CONFIG_NET_9P_DEBUG | 
 | 250 | 	if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { | 
 | 251 | 		char buf[150]; | 
 | 252 |  | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 253 | 		p9_printfcall(buf, sizeof(buf), *rc, t->extended); | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 254 | 		printk(KERN_NOTICE ">>> %p %s\n", t, buf); | 
 | 255 | 	} | 
 | 256 | #endif | 
 | 257 |  | 
 | 258 | 	if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool)) | 
 | 259 | 		p9_idpool_put(n, chan->tagpool); | 
 | 260 |  | 
 | 261 | 	req->status = REQ_STATUS_IDLE; | 
 | 262 |  | 
 | 263 | 	return 0; | 
 | 264 | } | 
 | 265 |  | 
 | 266 | static int p9_virtio_probe(struct virtio_device *vdev) | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 267 | { | 
 | 268 | 	int err; | 
 | 269 | 	struct virtio_chan *chan; | 
 | 270 | 	int index; | 
 | 271 |  | 
 | 272 | 	down(&virtio_9p_lock); | 
 | 273 | 	index = chan_index++; | 
 | 274 | 	chan = &channels[index]; | 
 | 275 | 	up(&virtio_9p_lock); | 
 | 276 |  | 
 | 277 | 	if (chan_index > MAX_9P_CHAN) { | 
 | 278 | 		printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n"); | 
 | 279 | 		BUG(); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 280 | 		err = -ENOMEM; | 
 | 281 | 		goto fail; | 
 | 282 | 	} | 
 | 283 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 284 | 	chan->vdev = vdev; | 
 | 285 |  | 
 | 286 | 	/* We expect one virtqueue, for requests. */ | 
 | 287 | 	chan->vq = vdev->config->find_vq(vdev, 0, req_done); | 
 | 288 | 	if (IS_ERR(chan->vq)) { | 
 | 289 | 		err = PTR_ERR(chan->vq); | 
 | 290 | 		goto out_free_vq; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 291 | 	} | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 292 | 	chan->vq->vdev->priv = chan; | 
 | 293 | 	spin_lock_init(&chan->lock); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 294 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 295 | 	sg_init_table(chan->sg, VIRTQUEUE_NUM); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 296 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 297 | 	chan->inuse = false; | 
 | 298 | 	chan->initialized = true; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 299 | 	return 0; | 
 | 300 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 301 | out_free_vq: | 
 | 302 | 	vdev->config->del_vq(chan->vq); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 303 | fail: | 
 | 304 | 	down(&virtio_9p_lock); | 
 | 305 | 	chan_index--; | 
 | 306 | 	up(&virtio_9p_lock); | 
 | 307 | 	return err; | 
 | 308 | } | 
 | 309 |  | 
 | 310 | /* This sets up a transport channel for 9p communication.  Right now | 
 | 311 |  * we only match the first available channel, but eventually we couldlook up | 
 | 312 |  * alternate channels by matching devname versus a virtio_config entry. | 
 | 313 |  * We use a simple reference count mechanism to ensure that only a single | 
 | 314 |  * mount has a channel open at a time. */ | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 315 | static struct p9_trans * | 
 | 316 | p9_virtio_create(const char *devname, char *args, int msize, | 
 | 317 | 							unsigned char extended) | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 318 | { | 
 | 319 | 	struct p9_trans *trans; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 320 | 	struct virtio_chan *chan = channels; | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 321 | 	int index = 0; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 322 |  | 
 | 323 | 	down(&virtio_9p_lock); | 
 | 324 | 	while (index < MAX_9P_CHAN) { | 
 | 325 | 		if (chan->initialized && !chan->inuse) { | 
 | 326 | 			chan->inuse = true; | 
 | 327 | 			break; | 
 | 328 | 		} else { | 
 | 329 | 			index++; | 
 | 330 | 			chan = &channels[index]; | 
 | 331 | 		} | 
 | 332 | 	} | 
 | 333 | 	up(&virtio_9p_lock); | 
 | 334 |  | 
 | 335 | 	if (index >= MAX_9P_CHAN) { | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 336 | 		printk(KERN_ERR "9p: no channels available\n"); | 
 | 337 | 		return ERR_PTR(-ENODEV); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 338 | 	} | 
 | 339 |  | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 340 | 	chan->tagpool = p9_idpool_create(); | 
 | 341 | 	if (IS_ERR(chan->tagpool)) { | 
 | 342 | 		printk(KERN_ERR "9p: couldn't allocate tagpool\n"); | 
 | 343 | 		return ERR_PTR(-ENOMEM); | 
 | 344 | 	} | 
 | 345 | 	p9_idpool_get(chan->tagpool); /* reserve tag 0 */ | 
 | 346 | 	chan->max_tag = 0; | 
 | 347 | 	chan->reqs = NULL; | 
 | 348 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 349 | 	trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); | 
 | 350 | 	if (!trans) { | 
 | 351 | 		printk(KERN_ERR "9p: couldn't allocate transport\n"); | 
 | 352 | 		return ERR_PTR(-ENOMEM); | 
 | 353 | 	} | 
| Eric Van Hensbergen | 8a0dc95 | 2008-02-06 19:25:03 -0600 | [diff] [blame] | 354 | 	trans->extended = extended; | 
 | 355 | 	trans->msize = msize; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 356 | 	trans->close = p9_virtio_close; | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 357 | 	trans->rpc = p9_virtio_rpc; | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 358 | 	trans->priv = chan; | 
 | 359 |  | 
 | 360 | 	return trans; | 
 | 361 | } | 
 | 362 |  | 
| Eric Van Hensbergen | f393354 | 2008-02-06 19:25:04 -0600 | [diff] [blame] | 363 | static void p9_virtio_remove(struct virtio_device *vdev) | 
 | 364 | { | 
 | 365 | 	struct virtio_chan *chan = vdev->priv; | 
 | 366 |  | 
 | 367 | 	BUG_ON(chan->inuse); | 
 | 368 |  | 
 | 369 | 	if (chan->initialized) { | 
 | 370 | 		vdev->config->del_vq(chan->vq); | 
 | 371 | 		chan->initialized = false; | 
 | 372 | 	} | 
 | 373 | } | 
 | 374 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 375 | #define VIRTIO_ID_9P 9 | 
 | 376 |  | 
 | 377 | static struct virtio_device_id id_table[] = { | 
 | 378 | 	{ VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID }, | 
 | 379 | 	{ 0 }, | 
 | 380 | }; | 
 | 381 |  | 
 | 382 | /* The standard "struct lguest_driver": */ | 
 | 383 | static struct virtio_driver p9_virtio_drv = { | 
 | 384 | 	.driver.name = 	KBUILD_MODNAME, | 
 | 385 | 	.driver.owner = THIS_MODULE, | 
 | 386 | 	.id_table =	id_table, | 
 | 387 | 	.probe = 	p9_virtio_probe, | 
| Eric Van Hensbergen | f393354 | 2008-02-06 19:25:04 -0600 | [diff] [blame] | 388 | 	.remove =	p9_virtio_remove, | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 389 | }; | 
 | 390 |  | 
 | 391 | static struct p9_trans_module p9_virtio_trans = { | 
 | 392 | 	.name = "virtio", | 
 | 393 | 	.create = p9_virtio_create, | 
| Eric Van Hensbergen | e2735b7 | 2008-02-06 19:25:58 -0600 | [diff] [blame] | 394 | 	.maxsize = PAGE_SIZE*16, | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 395 | 	.def = 0, | 
 | 396 | }; | 
 | 397 |  | 
 | 398 | /* The standard init function */ | 
 | 399 | static int __init p9_virtio_init(void) | 
 | 400 | { | 
 | 401 | 	int count; | 
 | 402 |  | 
 | 403 | 	for (count = 0; count < MAX_9P_CHAN; count++) | 
 | 404 | 		channels[count].initialized = false; | 
 | 405 |  | 
 | 406 | 	v9fs_register_trans(&p9_virtio_trans); | 
 | 407 | 	return register_virtio_driver(&p9_virtio_drv); | 
 | 408 | } | 
 | 409 |  | 
| Eric Van Hensbergen | f393354 | 2008-02-06 19:25:04 -0600 | [diff] [blame] | 410 | static void __exit p9_virtio_cleanup(void) | 
 | 411 | { | 
 | 412 | 	unregister_virtio_driver(&p9_virtio_drv); | 
 | 413 | } | 
 | 414 |  | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 415 | module_init(p9_virtio_init); | 
| Eric Van Hensbergen | f393354 | 2008-02-06 19:25:04 -0600 | [diff] [blame] | 416 | module_exit(p9_virtio_cleanup); | 
| Eric Van Hensbergen | b530cc7 | 2007-10-23 13:47:31 -0500 | [diff] [blame] | 417 |  | 
 | 418 | MODULE_DEVICE_TABLE(virtio, id_table); | 
 | 419 | MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); | 
 | 420 | MODULE_DESCRIPTION("Virtio 9p Transport"); | 
 | 421 | MODULE_LICENSE("GPL"); |