xref: /openbmc/linux/net/9p/trans_xen.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
1024b7d6aSDominique Martinet // SPDX-License-Identifier: GPL-2.0-only
2868eb122SStefano Stabellini /*
3868eb122SStefano Stabellini  * linux/fs/9p/trans_xen
4868eb122SStefano Stabellini  *
5868eb122SStefano Stabellini  * Xen transport layer.
6868eb122SStefano Stabellini  *
7868eb122SStefano Stabellini  * Copyright (C) 2017 by Stefano Stabellini <stefano@aporeto.com>
8868eb122SStefano Stabellini  */
9868eb122SStefano Stabellini 
10868eb122SStefano Stabellini #include <xen/events.h>
11868eb122SStefano Stabellini #include <xen/grant_table.h>
12868eb122SStefano Stabellini #include <xen/xen.h>
13868eb122SStefano Stabellini #include <xen/xenbus.h>
14868eb122SStefano Stabellini #include <xen/interface/io/9pfs.h>
15868eb122SStefano Stabellini 
16868eb122SStefano Stabellini #include <linux/module.h>
1771ebd719SStefano Stabellini #include <linux/spinlock.h>
18868eb122SStefano Stabellini #include <net/9p/9p.h>
19868eb122SStefano Stabellini #include <net/9p/client.h>
20868eb122SStefano Stabellini #include <net/9p/transport.h>
21868eb122SStefano Stabellini 
2271ebd719SStefano Stabellini #define XEN_9PFS_NUM_RINGS 2
2336f99675SStefano Stabellini #define XEN_9PFS_RING_ORDER 9
2436f99675SStefano Stabellini #define XEN_9PFS_RING_SIZE(ring)  XEN_FLEX_RING_SIZE(ring->intf->ring_order)
2571ebd719SStefano Stabellini 
2671ebd719SStefano Stabellini struct xen_9pfs_header {
2771ebd719SStefano Stabellini 	uint32_t size;
2871ebd719SStefano Stabellini 	uint8_t id;
2971ebd719SStefano Stabellini 	uint16_t tag;
3071ebd719SStefano Stabellini 
3171ebd719SStefano Stabellini 	/* uint8_t sdata[]; */
3271ebd719SStefano Stabellini } __attribute__((packed));
3371ebd719SStefano Stabellini 
3471ebd719SStefano Stabellini /* One per ring, more than one per 9pfs share */
3571ebd719SStefano Stabellini struct xen_9pfs_dataring {
3671ebd719SStefano Stabellini 	struct xen_9pfs_front_priv *priv;
3771ebd719SStefano Stabellini 
3871ebd719SStefano Stabellini 	struct xen_9pfs_data_intf *intf;
3971ebd719SStefano Stabellini 	grant_ref_t ref;
4071ebd719SStefano Stabellini 	int evtchn;
4171ebd719SStefano Stabellini 	int irq;
4271ebd719SStefano Stabellini 	/* protect a ring from concurrent accesses */
4371ebd719SStefano Stabellini 	spinlock_t lock;
4471ebd719SStefano Stabellini 
4571ebd719SStefano Stabellini 	struct xen_9pfs_data data;
4671ebd719SStefano Stabellini 	wait_queue_head_t wq;
4771ebd719SStefano Stabellini 	struct work_struct work;
4871ebd719SStefano Stabellini };
4971ebd719SStefano Stabellini 
5071ebd719SStefano Stabellini /* One per 9pfs share */
5171ebd719SStefano Stabellini struct xen_9pfs_front_priv {
5271ebd719SStefano Stabellini 	struct list_head list;
5371ebd719SStefano Stabellini 	struct xenbus_device *dev;
5471ebd719SStefano Stabellini 	char *tag;
5571ebd719SStefano Stabellini 	struct p9_client *client;
5671ebd719SStefano Stabellini 
5771ebd719SStefano Stabellini 	int num_rings;
5871ebd719SStefano Stabellini 	struct xen_9pfs_dataring *rings;
5971ebd719SStefano Stabellini };
6071ebd719SStefano Stabellini 
6171ebd719SStefano Stabellini static LIST_HEAD(xen_9pfs_devs);
6271ebd719SStefano Stabellini static DEFINE_RWLOCK(xen_9pfs_lock);
6371ebd719SStefano Stabellini 
64f023f18dSStefano Stabellini /* We don't currently allow canceling of requests */
p9_xen_cancel(struct p9_client * client,struct p9_req_t * req)65868eb122SStefano Stabellini static int p9_xen_cancel(struct p9_client *client, struct p9_req_t *req)
66868eb122SStefano Stabellini {
67f023f18dSStefano Stabellini 	return 1;
68868eb122SStefano Stabellini }
69868eb122SStefano Stabellini 
p9_xen_create(struct p9_client * client,const char * addr,char * args)70868eb122SStefano Stabellini static int p9_xen_create(struct p9_client *client, const char *addr, char *args)
71868eb122SStefano Stabellini {
72f023f18dSStefano Stabellini 	struct xen_9pfs_front_priv *priv;
73f023f18dSStefano Stabellini 
7410aa1452STomas Bortoli 	if (addr == NULL)
7510aa1452STomas Bortoli 		return -EINVAL;
7610aa1452STomas Bortoli 
77f023f18dSStefano Stabellini 	read_lock(&xen_9pfs_lock);
78f023f18dSStefano Stabellini 	list_for_each_entry(priv, &xen_9pfs_devs, list) {
79f023f18dSStefano Stabellini 		if (!strcmp(priv->tag, addr)) {
80f023f18dSStefano Stabellini 			priv->client = client;
81f023f18dSStefano Stabellini 			read_unlock(&xen_9pfs_lock);
82868eb122SStefano Stabellini 			return 0;
83868eb122SStefano Stabellini 		}
84f023f18dSStefano Stabellini 	}
85f023f18dSStefano Stabellini 	read_unlock(&xen_9pfs_lock);
86f023f18dSStefano Stabellini 	return -EINVAL;
87f023f18dSStefano Stabellini }
88868eb122SStefano Stabellini 
p9_xen_close(struct p9_client * client)89868eb122SStefano Stabellini static void p9_xen_close(struct p9_client *client)
90868eb122SStefano Stabellini {
91f023f18dSStefano Stabellini 	struct xen_9pfs_front_priv *priv;
92f023f18dSStefano Stabellini 
93f023f18dSStefano Stabellini 	read_lock(&xen_9pfs_lock);
94f023f18dSStefano Stabellini 	list_for_each_entry(priv, &xen_9pfs_devs, list) {
95f023f18dSStefano Stabellini 		if (priv->client == client) {
96f023f18dSStefano Stabellini 			priv->client = NULL;
97f023f18dSStefano Stabellini 			read_unlock(&xen_9pfs_lock);
98f023f18dSStefano Stabellini 			return;
99f023f18dSStefano Stabellini 		}
100f023f18dSStefano Stabellini 	}
101f023f18dSStefano Stabellini 	read_unlock(&xen_9pfs_lock);
102f023f18dSStefano Stabellini }
103f023f18dSStefano Stabellini 
p9_xen_write_todo(struct xen_9pfs_dataring * ring,RING_IDX size)104f023f18dSStefano Stabellini static bool p9_xen_write_todo(struct xen_9pfs_dataring *ring, RING_IDX size)
105f023f18dSStefano Stabellini {
106f023f18dSStefano Stabellini 	RING_IDX cons, prod;
107f023f18dSStefano Stabellini 
108f023f18dSStefano Stabellini 	cons = ring->intf->out_cons;
109f023f18dSStefano Stabellini 	prod = ring->intf->out_prod;
110f023f18dSStefano Stabellini 	virt_mb();
111f023f18dSStefano Stabellini 
11236f99675SStefano Stabellini 	return XEN_9PFS_RING_SIZE(ring) -
11336f99675SStefano Stabellini 		xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) >= size;
114868eb122SStefano Stabellini }
115868eb122SStefano Stabellini 
p9_xen_request(struct p9_client * client,struct p9_req_t * p9_req)116868eb122SStefano Stabellini static int p9_xen_request(struct p9_client *client, struct p9_req_t *p9_req)
117868eb122SStefano Stabellini {
118732b33d0SHarshvardhan Jha 	struct xen_9pfs_front_priv *priv;
119f023f18dSStefano Stabellini 	RING_IDX cons, prod, masked_cons, masked_prod;
120f023f18dSStefano Stabellini 	unsigned long flags;
121523adb6cSDominique Martinet 	u32 size = p9_req->tc.size;
122f023f18dSStefano Stabellini 	struct xen_9pfs_dataring *ring;
123f023f18dSStefano Stabellini 	int num;
124f023f18dSStefano Stabellini 
125f023f18dSStefano Stabellini 	read_lock(&xen_9pfs_lock);
126f023f18dSStefano Stabellini 	list_for_each_entry(priv, &xen_9pfs_devs, list) {
127f023f18dSStefano Stabellini 		if (priv->client == client)
128f023f18dSStefano Stabellini 			break;
129f023f18dSStefano Stabellini 	}
130f023f18dSStefano Stabellini 	read_unlock(&xen_9pfs_lock);
131732b33d0SHarshvardhan Jha 	if (list_entry_is_head(priv, &xen_9pfs_devs, list))
132f023f18dSStefano Stabellini 		return -EINVAL;
133f023f18dSStefano Stabellini 
134523adb6cSDominique Martinet 	num = p9_req->tc.tag % priv->num_rings;
135f023f18dSStefano Stabellini 	ring = &priv->rings[num];
136f023f18dSStefano Stabellini 
137f023f18dSStefano Stabellini again:
1389523feacSTuomas Tynkkynen 	while (wait_event_killable(ring->wq,
139f023f18dSStefano Stabellini 				   p9_xen_write_todo(ring, size)) != 0)
140f023f18dSStefano Stabellini 		;
141f023f18dSStefano Stabellini 
142f023f18dSStefano Stabellini 	spin_lock_irqsave(&ring->lock, flags);
143f023f18dSStefano Stabellini 	cons = ring->intf->out_cons;
144f023f18dSStefano Stabellini 	prod = ring->intf->out_prod;
145f023f18dSStefano Stabellini 	virt_mb();
146f023f18dSStefano Stabellini 
14736f99675SStefano Stabellini 	if (XEN_9PFS_RING_SIZE(ring) -
14836f99675SStefano Stabellini 	    xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) < size) {
149f023f18dSStefano Stabellini 		spin_unlock_irqrestore(&ring->lock, flags);
150f023f18dSStefano Stabellini 		goto again;
151f023f18dSStefano Stabellini 	}
152f023f18dSStefano Stabellini 
15336f99675SStefano Stabellini 	masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE(ring));
15436f99675SStefano Stabellini 	masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
155f023f18dSStefano Stabellini 
156523adb6cSDominique Martinet 	xen_9pfs_write_packet(ring->data.out, p9_req->tc.sdata, size,
15736f99675SStefano Stabellini 			      &masked_prod, masked_cons,
15836f99675SStefano Stabellini 			      XEN_9PFS_RING_SIZE(ring));
159f023f18dSStefano Stabellini 
1601a4f69efSDominique Martinet 	WRITE_ONCE(p9_req->status, REQ_STATUS_SENT);
161f023f18dSStefano Stabellini 	virt_wmb();			/* write ring before updating pointer */
162f023f18dSStefano Stabellini 	prod += size;
163f023f18dSStefano Stabellini 	ring->intf->out_prod = prod;
164f023f18dSStefano Stabellini 	spin_unlock_irqrestore(&ring->lock, flags);
165f023f18dSStefano Stabellini 	notify_remote_via_irq(ring->irq);
1668b11ff09SKent Overstreet 	p9_req_put(client, p9_req);
167f023f18dSStefano Stabellini 
168868eb122SStefano Stabellini 	return 0;
169868eb122SStefano Stabellini }
170868eb122SStefano Stabellini 
p9_xen_response(struct work_struct * work)17171ebd719SStefano Stabellini static void p9_xen_response(struct work_struct *work)
17271ebd719SStefano Stabellini {
173f66c72beSStefano Stabellini 	struct xen_9pfs_front_priv *priv;
174f66c72beSStefano Stabellini 	struct xen_9pfs_dataring *ring;
175f66c72beSStefano Stabellini 	RING_IDX cons, prod, masked_cons, masked_prod;
176f66c72beSStefano Stabellini 	struct xen_9pfs_header h;
177f66c72beSStefano Stabellini 	struct p9_req_t *req;
178f66c72beSStefano Stabellini 	int status;
179f66c72beSStefano Stabellini 
180f66c72beSStefano Stabellini 	ring = container_of(work, struct xen_9pfs_dataring, work);
181f66c72beSStefano Stabellini 	priv = ring->priv;
182f66c72beSStefano Stabellini 
183f66c72beSStefano Stabellini 	while (1) {
184f66c72beSStefano Stabellini 		cons = ring->intf->in_cons;
185f66c72beSStefano Stabellini 		prod = ring->intf->in_prod;
186f66c72beSStefano Stabellini 		virt_rmb();
187f66c72beSStefano Stabellini 
18836f99675SStefano Stabellini 		if (xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) <
189f66c72beSStefano Stabellini 		    sizeof(h)) {
190f66c72beSStefano Stabellini 			notify_remote_via_irq(ring->irq);
191f66c72beSStefano Stabellini 			return;
192f66c72beSStefano Stabellini 		}
193f66c72beSStefano Stabellini 
19436f99675SStefano Stabellini 		masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE(ring));
19536f99675SStefano Stabellini 		masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
196f66c72beSStefano Stabellini 
197f66c72beSStefano Stabellini 		/* First, read just the header */
198f66c72beSStefano Stabellini 		xen_9pfs_read_packet(&h, ring->data.in, sizeof(h),
199f66c72beSStefano Stabellini 				     masked_prod, &masked_cons,
20036f99675SStefano Stabellini 				     XEN_9PFS_RING_SIZE(ring));
201f66c72beSStefano Stabellini 
202f66c72beSStefano Stabellini 		req = p9_tag_lookup(priv->client, h.tag);
203f66c72beSStefano Stabellini 		if (!req || req->status != REQ_STATUS_SENT) {
204f66c72beSStefano Stabellini 			dev_warn(&priv->dev->dev, "Wrong req tag=%x\n", h.tag);
205f66c72beSStefano Stabellini 			cons += h.size;
206f66c72beSStefano Stabellini 			virt_mb();
207f66c72beSStefano Stabellini 			ring->intf->in_cons = cons;
208f66c72beSStefano Stabellini 			continue;
209f66c72beSStefano Stabellini 		}
210f66c72beSStefano Stabellini 
211391c18cfSDominique Martinet 		if (h.size > req->rc.capacity) {
212391c18cfSDominique Martinet 			dev_warn(&priv->dev->dev,
213391c18cfSDominique Martinet 				 "requested packet size too big: %d for tag %d with capacity %zd\n",
214391c18cfSDominique Martinet 				 h.size, h.tag, req->rc.capacity);
2151a4f69efSDominique Martinet 			WRITE_ONCE(req->status, REQ_STATUS_ERROR);
216391c18cfSDominique Martinet 			goto recv_error;
217391c18cfSDominique Martinet 		}
218391c18cfSDominique Martinet 
219f15e006bSDominique Martinet 		req->rc.size = h.size;
220f15e006bSDominique Martinet 		req->rc.id = h.id;
221f15e006bSDominique Martinet 		req->rc.tag = h.tag;
222523adb6cSDominique Martinet 		req->rc.offset = 0;
223f66c72beSStefano Stabellini 
22436f99675SStefano Stabellini 		masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
225f66c72beSStefano Stabellini 		/* Then, read the whole packet (including the header) */
226523adb6cSDominique Martinet 		xen_9pfs_read_packet(req->rc.sdata, ring->data.in, h.size,
227f66c72beSStefano Stabellini 				     masked_prod, &masked_cons,
22836f99675SStefano Stabellini 				     XEN_9PFS_RING_SIZE(ring));
229f66c72beSStefano Stabellini 
230391c18cfSDominique Martinet recv_error:
231f66c72beSStefano Stabellini 		virt_mb();
232f66c72beSStefano Stabellini 		cons += h.size;
233f66c72beSStefano Stabellini 		ring->intf->in_cons = cons;
234f66c72beSStefano Stabellini 
235f66c72beSStefano Stabellini 		status = (req->status != REQ_STATUS_ERROR) ?
236f66c72beSStefano Stabellini 			REQ_STATUS_RCVD : REQ_STATUS_ERROR;
237f66c72beSStefano Stabellini 
238f66c72beSStefano Stabellini 		p9_client_cb(priv->client, req, status);
239f66c72beSStefano Stabellini 	}
24071ebd719SStefano Stabellini }
24171ebd719SStefano Stabellini 
xen_9pfs_front_event_handler(int irq,void * r)24271ebd719SStefano Stabellini static irqreturn_t xen_9pfs_front_event_handler(int irq, void *r)
24371ebd719SStefano Stabellini {
24471ebd719SStefano Stabellini 	struct xen_9pfs_dataring *ring = r;
24571ebd719SStefano Stabellini 
24671ebd719SStefano Stabellini 	if (!ring || !ring->priv->client) {
24771ebd719SStefano Stabellini 		/* ignore spurious interrupt */
24871ebd719SStefano Stabellini 		return IRQ_HANDLED;
24971ebd719SStefano Stabellini 	}
25071ebd719SStefano Stabellini 
25171ebd719SStefano Stabellini 	wake_up_interruptible(&ring->wq);
25271ebd719SStefano Stabellini 	schedule_work(&ring->work);
25371ebd719SStefano Stabellini 
25471ebd719SStefano Stabellini 	return IRQ_HANDLED;
25571ebd719SStefano Stabellini }
25671ebd719SStefano Stabellini 
257868eb122SStefano Stabellini static struct p9_trans_module p9_xen_trans = {
258868eb122SStefano Stabellini 	.name = "xen",
25936f99675SStefano Stabellini 	.maxsize = 1 << (XEN_9PFS_RING_ORDER + XEN_PAGE_SHIFT - 2),
26001d205d9SChristian Schoenebeck 	.pooled_rbuffers = false,
261868eb122SStefano Stabellini 	.def = 1,
262868eb122SStefano Stabellini 	.create = p9_xen_create,
263868eb122SStefano Stabellini 	.close = p9_xen_close,
264868eb122SStefano Stabellini 	.request = p9_xen_request,
265868eb122SStefano Stabellini 	.cancel = p9_xen_cancel,
266868eb122SStefano Stabellini 	.owner = THIS_MODULE,
267868eb122SStefano Stabellini };
268868eb122SStefano Stabellini 
269868eb122SStefano Stabellini static const struct xenbus_device_id xen_9pfs_front_ids[] = {
270868eb122SStefano Stabellini 	{ "9pfs" },
271868eb122SStefano Stabellini 	{ "" }
272868eb122SStefano Stabellini };
273868eb122SStefano Stabellini 
xen_9pfs_front_free(struct xen_9pfs_front_priv * priv)27471ebd719SStefano Stabellini static void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv)
27571ebd719SStefano Stabellini {
27671ebd719SStefano Stabellini 	int i, j;
27771ebd719SStefano Stabellini 
27871ebd719SStefano Stabellini 	write_lock(&xen_9pfs_lock);
27971ebd719SStefano Stabellini 	list_del(&priv->list);
28071ebd719SStefano Stabellini 	write_unlock(&xen_9pfs_lock);
28171ebd719SStefano Stabellini 
28271ebd719SStefano Stabellini 	for (i = 0; i < priv->num_rings; i++) {
283ea4f1009SZheng Wang 		struct xen_9pfs_dataring *ring = &priv->rings[i];
284ea4f1009SZheng Wang 
285ea4f1009SZheng Wang 		cancel_work_sync(&ring->work);
286ea4f1009SZheng Wang 
28771ebd719SStefano Stabellini 		if (!priv->rings[i].intf)
28871ebd719SStefano Stabellini 			break;
28971ebd719SStefano Stabellini 		if (priv->rings[i].irq > 0)
290*2bb3ee1bSAlex Zenla 			unbind_from_irqhandler(priv->rings[i].irq, ring);
29171ebd719SStefano Stabellini 		if (priv->rings[i].data.in) {
29236f99675SStefano Stabellini 			for (j = 0;
29336f99675SStefano Stabellini 			     j < (1 << priv->rings[i].intf->ring_order);
29436f99675SStefano Stabellini 			     j++) {
29571ebd719SStefano Stabellini 				grant_ref_t ref;
29671ebd719SStefano Stabellini 
29771ebd719SStefano Stabellini 				ref = priv->rings[i].intf->ref[j];
29849f8b459SJuergen Gross 				gnttab_end_foreign_access(ref, NULL);
29971ebd719SStefano Stabellini 			}
3005cadd4bbSJuergen Gross 			free_pages_exact(priv->rings[i].data.in,
3015cadd4bbSJuergen Gross 				   1UL << (priv->rings[i].intf->ring_order +
3025cadd4bbSJuergen Gross 					   XEN_PAGE_SHIFT));
30371ebd719SStefano Stabellini 		}
30449f8b459SJuergen Gross 		gnttab_end_foreign_access(priv->rings[i].ref, NULL);
30571ebd719SStefano Stabellini 		free_page((unsigned long)priv->rings[i].intf);
30671ebd719SStefano Stabellini 	}
30771ebd719SStefano Stabellini 	kfree(priv->rings);
30871ebd719SStefano Stabellini 	kfree(priv->tag);
30971ebd719SStefano Stabellini 	kfree(priv);
31071ebd719SStefano Stabellini }
31171ebd719SStefano Stabellini 
xen_9pfs_front_remove(struct xenbus_device * dev)3127cffcadeSDawei Li static void xen_9pfs_front_remove(struct xenbus_device *dev)
313868eb122SStefano Stabellini {
31471ebd719SStefano Stabellini 	struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
31571ebd719SStefano Stabellini 
31671ebd719SStefano Stabellini 	dev_set_drvdata(&dev->dev, NULL);
31771ebd719SStefano Stabellini 	xen_9pfs_front_free(priv);
318868eb122SStefano Stabellini }
319868eb122SStefano Stabellini 
xen_9pfs_front_alloc_dataring(struct xenbus_device * dev,struct xen_9pfs_dataring * ring,unsigned int order)32071ebd719SStefano Stabellini static int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev,
32136f99675SStefano Stabellini 					 struct xen_9pfs_dataring *ring,
32236f99675SStefano Stabellini 					 unsigned int order)
32371ebd719SStefano Stabellini {
32471ebd719SStefano Stabellini 	int i = 0;
32571ebd719SStefano Stabellini 	int ret = -ENOMEM;
32671ebd719SStefano Stabellini 	void *bytes = NULL;
32771ebd719SStefano Stabellini 
32871ebd719SStefano Stabellini 	init_waitqueue_head(&ring->wq);
32971ebd719SStefano Stabellini 	spin_lock_init(&ring->lock);
33071ebd719SStefano Stabellini 	INIT_WORK(&ring->work, p9_xen_response);
33171ebd719SStefano Stabellini 
33271ebd719SStefano Stabellini 	ring->intf = (struct xen_9pfs_data_intf *)get_zeroed_page(GFP_KERNEL);
33371ebd719SStefano Stabellini 	if (!ring->intf)
33471ebd719SStefano Stabellini 		return ret;
33571ebd719SStefano Stabellini 	ret = gnttab_grant_foreign_access(dev->otherend_id,
33671ebd719SStefano Stabellini 					  virt_to_gfn(ring->intf), 0);
33771ebd719SStefano Stabellini 	if (ret < 0)
33871ebd719SStefano Stabellini 		goto out;
33971ebd719SStefano Stabellini 	ring->ref = ret;
3405cadd4bbSJuergen Gross 	bytes = alloc_pages_exact(1UL << (order + XEN_PAGE_SHIFT),
3415cadd4bbSJuergen Gross 				  GFP_KERNEL | __GFP_ZERO);
34271ebd719SStefano Stabellini 	if (!bytes) {
34371ebd719SStefano Stabellini 		ret = -ENOMEM;
34471ebd719SStefano Stabellini 		goto out;
34571ebd719SStefano Stabellini 	}
34636f99675SStefano Stabellini 	for (; i < (1 << order); i++) {
34771ebd719SStefano Stabellini 		ret = gnttab_grant_foreign_access(
34871ebd719SStefano Stabellini 				dev->otherend_id, virt_to_gfn(bytes) + i, 0);
34971ebd719SStefano Stabellini 		if (ret < 0)
35071ebd719SStefano Stabellini 			goto out;
35171ebd719SStefano Stabellini 		ring->intf->ref[i] = ret;
35271ebd719SStefano Stabellini 	}
35336f99675SStefano Stabellini 	ring->intf->ring_order = order;
35471ebd719SStefano Stabellini 	ring->data.in = bytes;
35536f99675SStefano Stabellini 	ring->data.out = bytes + XEN_FLEX_RING_SIZE(order);
35671ebd719SStefano Stabellini 
35771ebd719SStefano Stabellini 	ret = xenbus_alloc_evtchn(dev, &ring->evtchn);
35871ebd719SStefano Stabellini 	if (ret)
35971ebd719SStefano Stabellini 		goto out;
36071ebd719SStefano Stabellini 	ring->irq = bind_evtchn_to_irqhandler(ring->evtchn,
36171ebd719SStefano Stabellini 					      xen_9pfs_front_event_handler,
36271ebd719SStefano Stabellini 					      0, "xen_9pfs-frontend", ring);
36371ebd719SStefano Stabellini 	if (ring->irq >= 0)
36471ebd719SStefano Stabellini 		return 0;
36571ebd719SStefano Stabellini 
36671ebd719SStefano Stabellini 	xenbus_free_evtchn(dev, ring->evtchn);
36771ebd719SStefano Stabellini 	ret = ring->irq;
36871ebd719SStefano Stabellini out:
36971ebd719SStefano Stabellini 	if (bytes) {
37071ebd719SStefano Stabellini 		for (i--; i >= 0; i--)
37149f8b459SJuergen Gross 			gnttab_end_foreign_access(ring->intf->ref[i], NULL);
3725cadd4bbSJuergen Gross 		free_pages_exact(bytes, 1UL << (order + XEN_PAGE_SHIFT));
37371ebd719SStefano Stabellini 	}
37449f8b459SJuergen Gross 	gnttab_end_foreign_access(ring->ref, NULL);
37571ebd719SStefano Stabellini 	free_page((unsigned long)ring->intf);
37671ebd719SStefano Stabellini 	return ret;
37771ebd719SStefano Stabellini }
37871ebd719SStefano Stabellini 
xen_9pfs_front_init(struct xenbus_device * dev)379c15fe55dSJuergen Gross static int xen_9pfs_front_init(struct xenbus_device *dev)
380868eb122SStefano Stabellini {
38171ebd719SStefano Stabellini 	int ret, i;
38271ebd719SStefano Stabellini 	struct xenbus_transaction xbt;
383c15fe55dSJuergen Gross 	struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
384f1956f4eSJuergen Gross 	char *versions, *v;
38531d47266SStefano Stabellini 	unsigned int max_rings, max_ring_order, len = 0;
38671ebd719SStefano Stabellini 
38771ebd719SStefano Stabellini 	versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len);
3882f9ad0acSDominique Martinet 	if (IS_ERR(versions))
3892f9ad0acSDominique Martinet 		return PTR_ERR(versions);
390f1956f4eSJuergen Gross 	for (v = versions; *v; v++) {
391f1956f4eSJuergen Gross 		if (simple_strtoul(v, &v, 10) == 1) {
392f1956f4eSJuergen Gross 			v = NULL;
393f1956f4eSJuergen Gross 			break;
394f1956f4eSJuergen Gross 		}
395f1956f4eSJuergen Gross 	}
396f1956f4eSJuergen Gross 	if (v) {
39771ebd719SStefano Stabellini 		kfree(versions);
39871ebd719SStefano Stabellini 		return -EINVAL;
39971ebd719SStefano Stabellini 	}
40071ebd719SStefano Stabellini 	kfree(versions);
40171ebd719SStefano Stabellini 	max_rings = xenbus_read_unsigned(dev->otherend, "max-rings", 0);
40271ebd719SStefano Stabellini 	if (max_rings < XEN_9PFS_NUM_RINGS)
40371ebd719SStefano Stabellini 		return -EINVAL;
40471ebd719SStefano Stabellini 	max_ring_order = xenbus_read_unsigned(dev->otherend,
40571ebd719SStefano Stabellini 					      "max-ring-page-order", 0);
40636f99675SStefano Stabellini 	if (max_ring_order > XEN_9PFS_RING_ORDER)
40736f99675SStefano Stabellini 		max_ring_order = XEN_9PFS_RING_ORDER;
40836f99675SStefano Stabellini 	if (p9_xen_trans.maxsize > XEN_FLEX_RING_SIZE(max_ring_order))
40936f99675SStefano Stabellini 		p9_xen_trans.maxsize = XEN_FLEX_RING_SIZE(max_ring_order) / 2;
41071ebd719SStefano Stabellini 
41171ebd719SStefano Stabellini 	priv->num_rings = XEN_9PFS_NUM_RINGS;
41271ebd719SStefano Stabellini 	priv->rings = kcalloc(priv->num_rings, sizeof(*priv->rings),
41371ebd719SStefano Stabellini 			      GFP_KERNEL);
41471ebd719SStefano Stabellini 	if (!priv->rings) {
41571ebd719SStefano Stabellini 		kfree(priv);
41671ebd719SStefano Stabellini 		return -ENOMEM;
41771ebd719SStefano Stabellini 	}
41871ebd719SStefano Stabellini 
41971ebd719SStefano Stabellini 	for (i = 0; i < priv->num_rings; i++) {
42071ebd719SStefano Stabellini 		priv->rings[i].priv = priv;
42136f99675SStefano Stabellini 		ret = xen_9pfs_front_alloc_dataring(dev, &priv->rings[i],
42236f99675SStefano Stabellini 						    max_ring_order);
42371ebd719SStefano Stabellini 		if (ret < 0)
42471ebd719SStefano Stabellini 			goto error;
42571ebd719SStefano Stabellini 	}
42671ebd719SStefano Stabellini 
42771ebd719SStefano Stabellini  again:
42871ebd719SStefano Stabellini 	ret = xenbus_transaction_start(&xbt);
42971ebd719SStefano Stabellini 	if (ret) {
43071ebd719SStefano Stabellini 		xenbus_dev_fatal(dev, ret, "starting transaction");
43171ebd719SStefano Stabellini 		goto error;
43271ebd719SStefano Stabellini 	}
43371ebd719SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "version", "%u", 1);
43471ebd719SStefano Stabellini 	if (ret)
43571ebd719SStefano Stabellini 		goto error_xenbus;
43671ebd719SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "num-rings", "%u",
43771ebd719SStefano Stabellini 			    priv->num_rings);
43871ebd719SStefano Stabellini 	if (ret)
43971ebd719SStefano Stabellini 		goto error_xenbus;
44071ebd719SStefano Stabellini 	for (i = 0; i < priv->num_rings; i++) {
44171ebd719SStefano Stabellini 		char str[16];
44271ebd719SStefano Stabellini 
44371ebd719SStefano Stabellini 		BUILD_BUG_ON(XEN_9PFS_NUM_RINGS > 9);
444316a1befSYe Bin 		sprintf(str, "ring-ref%d", i);
44571ebd719SStefano Stabellini 		ret = xenbus_printf(xbt, dev->nodename, str, "%d",
44671ebd719SStefano Stabellini 				    priv->rings[i].ref);
44771ebd719SStefano Stabellini 		if (ret)
44871ebd719SStefano Stabellini 			goto error_xenbus;
44971ebd719SStefano Stabellini 
450316a1befSYe Bin 		sprintf(str, "event-channel-%d", i);
45171ebd719SStefano Stabellini 		ret = xenbus_printf(xbt, dev->nodename, str, "%u",
45271ebd719SStefano Stabellini 				    priv->rings[i].evtchn);
45371ebd719SStefano Stabellini 		if (ret)
45471ebd719SStefano Stabellini 			goto error_xenbus;
45571ebd719SStefano Stabellini 	}
45671ebd719SStefano Stabellini 	priv->tag = xenbus_read(xbt, dev->nodename, "tag", NULL);
45714e3995eSWei Yongjun 	if (IS_ERR(priv->tag)) {
45814e3995eSWei Yongjun 		ret = PTR_ERR(priv->tag);
45971ebd719SStefano Stabellini 		goto error_xenbus;
46071ebd719SStefano Stabellini 	}
46171ebd719SStefano Stabellini 	ret = xenbus_transaction_end(xbt, 0);
46271ebd719SStefano Stabellini 	if (ret) {
46371ebd719SStefano Stabellini 		if (ret == -EAGAIN)
46471ebd719SStefano Stabellini 			goto again;
46571ebd719SStefano Stabellini 		xenbus_dev_fatal(dev, ret, "completing transaction");
46671ebd719SStefano Stabellini 		goto error;
46771ebd719SStefano Stabellini 	}
46871ebd719SStefano Stabellini 
469e978643cSAlex Zenla 	xenbus_switch_state(dev, XenbusStateInitialised);
470868eb122SStefano Stabellini 	return 0;
47171ebd719SStefano Stabellini 
47271ebd719SStefano Stabellini  error_xenbus:
47371ebd719SStefano Stabellini 	xenbus_transaction_end(xbt, 1);
47471ebd719SStefano Stabellini 	xenbus_dev_fatal(dev, ret, "writing xenstore");
47571ebd719SStefano Stabellini  error:
47671ebd719SStefano Stabellini 	xen_9pfs_front_free(priv);
47771ebd719SStefano Stabellini 	return ret;
478868eb122SStefano Stabellini }
479868eb122SStefano Stabellini 
xen_9pfs_front_probe(struct xenbus_device * dev,const struct xenbus_device_id * id)480c15fe55dSJuergen Gross static int xen_9pfs_front_probe(struct xenbus_device *dev,
481c15fe55dSJuergen Gross 				const struct xenbus_device_id *id)
482c15fe55dSJuergen Gross {
483c15fe55dSJuergen Gross 	struct xen_9pfs_front_priv *priv = NULL;
484c15fe55dSJuergen Gross 
485c15fe55dSJuergen Gross 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
486c15fe55dSJuergen Gross 	if (!priv)
487c15fe55dSJuergen Gross 		return -ENOMEM;
488c15fe55dSJuergen Gross 
489c15fe55dSJuergen Gross 	priv->dev = dev;
490c15fe55dSJuergen Gross 	dev_set_drvdata(&dev->dev, priv);
491c15fe55dSJuergen Gross 
492c15fe55dSJuergen Gross 	write_lock(&xen_9pfs_lock);
493c15fe55dSJuergen Gross 	list_add_tail(&priv->list, &xen_9pfs_devs);
494c15fe55dSJuergen Gross 	write_unlock(&xen_9pfs_lock);
495c15fe55dSJuergen Gross 
496c15fe55dSJuergen Gross 	return 0;
497c15fe55dSJuergen Gross }
498c15fe55dSJuergen Gross 
xen_9pfs_front_resume(struct xenbus_device * dev)499868eb122SStefano Stabellini static int xen_9pfs_front_resume(struct xenbus_device *dev)
500868eb122SStefano Stabellini {
501680a2846SColin Ian King 	dev_warn(&dev->dev, "suspend/resume unsupported\n");
502868eb122SStefano Stabellini 	return 0;
503868eb122SStefano Stabellini }
504868eb122SStefano Stabellini 
xen_9pfs_front_changed(struct xenbus_device * dev,enum xenbus_state backend_state)505868eb122SStefano Stabellini static void xen_9pfs_front_changed(struct xenbus_device *dev,
506868eb122SStefano Stabellini 				   enum xenbus_state backend_state)
507868eb122SStefano Stabellini {
50871ebd719SStefano Stabellini 	switch (backend_state) {
50971ebd719SStefano Stabellini 	case XenbusStateReconfiguring:
51071ebd719SStefano Stabellini 	case XenbusStateReconfigured:
51171ebd719SStefano Stabellini 	case XenbusStateInitialising:
51271ebd719SStefano Stabellini 	case XenbusStateInitialised:
51371ebd719SStefano Stabellini 	case XenbusStateUnknown:
51471ebd719SStefano Stabellini 		break;
51571ebd719SStefano Stabellini 
51671ebd719SStefano Stabellini 	case XenbusStateInitWait:
517e978643cSAlex Zenla 		if (dev->state != XenbusStateInitialising)
518e978643cSAlex Zenla 			break;
519e978643cSAlex Zenla 
520e978643cSAlex Zenla 		xen_9pfs_front_init(dev);
52171ebd719SStefano Stabellini 		break;
52271ebd719SStefano Stabellini 
52371ebd719SStefano Stabellini 	case XenbusStateConnected:
52471ebd719SStefano Stabellini 		xenbus_switch_state(dev, XenbusStateConnected);
52571ebd719SStefano Stabellini 		break;
52671ebd719SStefano Stabellini 
52771ebd719SStefano Stabellini 	case XenbusStateClosed:
52871ebd719SStefano Stabellini 		if (dev->state == XenbusStateClosed)
52971ebd719SStefano Stabellini 			break;
530df561f66SGustavo A. R. Silva 		fallthrough;	/* Missed the backend's CLOSING state */
53171ebd719SStefano Stabellini 	case XenbusStateClosing:
53271ebd719SStefano Stabellini 		xenbus_frontend_closed(dev);
53371ebd719SStefano Stabellini 		break;
53471ebd719SStefano Stabellini 	}
535868eb122SStefano Stabellini }
536868eb122SStefano Stabellini 
537868eb122SStefano Stabellini static struct xenbus_driver xen_9pfs_front_driver = {
538868eb122SStefano Stabellini 	.ids = xen_9pfs_front_ids,
539868eb122SStefano Stabellini 	.probe = xen_9pfs_front_probe,
540868eb122SStefano Stabellini 	.remove = xen_9pfs_front_remove,
541868eb122SStefano Stabellini 	.resume = xen_9pfs_front_resume,
542868eb122SStefano Stabellini 	.otherend_changed = xen_9pfs_front_changed,
543868eb122SStefano Stabellini };
544868eb122SStefano Stabellini 
p9_trans_xen_init(void)5450664c63aSXiu Jianfeng static int __init p9_trans_xen_init(void)
546868eb122SStefano Stabellini {
54780a316ffSYueHaibing 	int rc;
54880a316ffSYueHaibing 
549868eb122SStefano Stabellini 	if (!xen_domain())
550868eb122SStefano Stabellini 		return -ENODEV;
551868eb122SStefano Stabellini 
552868eb122SStefano Stabellini 	pr_info("Initialising Xen transport for 9pfs\n");
553868eb122SStefano Stabellini 
554868eb122SStefano Stabellini 	v9fs_register_trans(&p9_xen_trans);
55580a316ffSYueHaibing 	rc = xenbus_register_frontend(&xen_9pfs_front_driver);
55680a316ffSYueHaibing 	if (rc)
55780a316ffSYueHaibing 		v9fs_unregister_trans(&p9_xen_trans);
55880a316ffSYueHaibing 
55980a316ffSYueHaibing 	return rc;
560868eb122SStefano Stabellini }
561868eb122SStefano Stabellini module_init(p9_trans_xen_init);
5624cd82a5bSThomas Weißschuh MODULE_ALIAS_9P("xen");
563868eb122SStefano Stabellini 
p9_trans_xen_exit(void)5640664c63aSXiu Jianfeng static void __exit p9_trans_xen_exit(void)
565868eb122SStefano Stabellini {
566868eb122SStefano Stabellini 	v9fs_unregister_trans(&p9_xen_trans);
567868eb122SStefano Stabellini 	return xenbus_unregister_driver(&xen_9pfs_front_driver);
568868eb122SStefano Stabellini }
569868eb122SStefano Stabellini module_exit(p9_trans_xen_exit);
570d542296aSStephen Hemminger 
57199aa673eSThomas Weißschuh MODULE_ALIAS("xen:9pfs");
572d542296aSStephen Hemminger MODULE_AUTHOR("Stefano Stabellini <stefano@aporeto.com>");
573d542296aSStephen Hemminger MODULE_DESCRIPTION("Xen Transport for 9P");
574d542296aSStephen Hemminger MODULE_LICENSE("GPL");
575