162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/9p/trans_xen
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Xen transport layer.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2017 by Stefano Stabellini <stefano@aporeto.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <xen/events.h>
1162306a36Sopenharmony_ci#include <xen/grant_table.h>
1262306a36Sopenharmony_ci#include <xen/xen.h>
1362306a36Sopenharmony_ci#include <xen/xenbus.h>
1462306a36Sopenharmony_ci#include <xen/interface/io/9pfs.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci#include <net/9p/9p.h>
1962306a36Sopenharmony_ci#include <net/9p/client.h>
2062306a36Sopenharmony_ci#include <net/9p/transport.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define XEN_9PFS_NUM_RINGS 2
2362306a36Sopenharmony_ci#define XEN_9PFS_RING_ORDER 9
2462306a36Sopenharmony_ci#define XEN_9PFS_RING_SIZE(ring)  XEN_FLEX_RING_SIZE(ring->intf->ring_order)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct xen_9pfs_header {
2762306a36Sopenharmony_ci	uint32_t size;
2862306a36Sopenharmony_ci	uint8_t id;
2962306a36Sopenharmony_ci	uint16_t tag;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* uint8_t sdata[]; */
3262306a36Sopenharmony_ci} __attribute__((packed));
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* One per ring, more than one per 9pfs share */
3562306a36Sopenharmony_cistruct xen_9pfs_dataring {
3662306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	struct xen_9pfs_data_intf *intf;
3962306a36Sopenharmony_ci	grant_ref_t ref;
4062306a36Sopenharmony_ci	int evtchn;
4162306a36Sopenharmony_ci	int irq;
4262306a36Sopenharmony_ci	/* protect a ring from concurrent accesses */
4362306a36Sopenharmony_ci	spinlock_t lock;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	struct xen_9pfs_data data;
4662306a36Sopenharmony_ci	wait_queue_head_t wq;
4762306a36Sopenharmony_ci	struct work_struct work;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* One per 9pfs share */
5162306a36Sopenharmony_cistruct xen_9pfs_front_priv {
5262306a36Sopenharmony_ci	struct list_head list;
5362306a36Sopenharmony_ci	struct xenbus_device *dev;
5462306a36Sopenharmony_ci	char *tag;
5562306a36Sopenharmony_ci	struct p9_client *client;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	int num_rings;
5862306a36Sopenharmony_ci	struct xen_9pfs_dataring *rings;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic LIST_HEAD(xen_9pfs_devs);
6262306a36Sopenharmony_cistatic DEFINE_RWLOCK(xen_9pfs_lock);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* We don't currently allow canceling of requests */
6562306a36Sopenharmony_cistatic int p9_xen_cancel(struct p9_client *client, struct p9_req_t *req)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	return 1;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int p9_xen_create(struct p9_client *client, const char *addr, char *args)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (addr == NULL)
7562306a36Sopenharmony_ci		return -EINVAL;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	read_lock(&xen_9pfs_lock);
7862306a36Sopenharmony_ci	list_for_each_entry(priv, &xen_9pfs_devs, list) {
7962306a36Sopenharmony_ci		if (!strcmp(priv->tag, addr)) {
8062306a36Sopenharmony_ci			priv->client = client;
8162306a36Sopenharmony_ci			read_unlock(&xen_9pfs_lock);
8262306a36Sopenharmony_ci			return 0;
8362306a36Sopenharmony_ci		}
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	read_unlock(&xen_9pfs_lock);
8662306a36Sopenharmony_ci	return -EINVAL;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void p9_xen_close(struct p9_client *client)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	read_lock(&xen_9pfs_lock);
9462306a36Sopenharmony_ci	list_for_each_entry(priv, &xen_9pfs_devs, list) {
9562306a36Sopenharmony_ci		if (priv->client == client) {
9662306a36Sopenharmony_ci			priv->client = NULL;
9762306a36Sopenharmony_ci			read_unlock(&xen_9pfs_lock);
9862306a36Sopenharmony_ci			return;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci	read_unlock(&xen_9pfs_lock);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic bool p9_xen_write_todo(struct xen_9pfs_dataring *ring, RING_IDX size)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	RING_IDX cons, prod;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	cons = ring->intf->out_cons;
10962306a36Sopenharmony_ci	prod = ring->intf->out_prod;
11062306a36Sopenharmony_ci	virt_mb();
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return XEN_9PFS_RING_SIZE(ring) -
11362306a36Sopenharmony_ci		xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) >= size;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int p9_xen_request(struct p9_client *client, struct p9_req_t *p9_req)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
11962306a36Sopenharmony_ci	RING_IDX cons, prod, masked_cons, masked_prod;
12062306a36Sopenharmony_ci	unsigned long flags;
12162306a36Sopenharmony_ci	u32 size = p9_req->tc.size;
12262306a36Sopenharmony_ci	struct xen_9pfs_dataring *ring;
12362306a36Sopenharmony_ci	int num;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	read_lock(&xen_9pfs_lock);
12662306a36Sopenharmony_ci	list_for_each_entry(priv, &xen_9pfs_devs, list) {
12762306a36Sopenharmony_ci		if (priv->client == client)
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci	read_unlock(&xen_9pfs_lock);
13162306a36Sopenharmony_ci	if (list_entry_is_head(priv, &xen_9pfs_devs, list))
13262306a36Sopenharmony_ci		return -EINVAL;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	num = p9_req->tc.tag % priv->num_rings;
13562306a36Sopenharmony_ci	ring = &priv->rings[num];
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciagain:
13862306a36Sopenharmony_ci	while (wait_event_killable(ring->wq,
13962306a36Sopenharmony_ci				   p9_xen_write_todo(ring, size)) != 0)
14062306a36Sopenharmony_ci		;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	spin_lock_irqsave(&ring->lock, flags);
14362306a36Sopenharmony_ci	cons = ring->intf->out_cons;
14462306a36Sopenharmony_ci	prod = ring->intf->out_prod;
14562306a36Sopenharmony_ci	virt_mb();
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (XEN_9PFS_RING_SIZE(ring) -
14862306a36Sopenharmony_ci	    xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) < size) {
14962306a36Sopenharmony_ci		spin_unlock_irqrestore(&ring->lock, flags);
15062306a36Sopenharmony_ci		goto again;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE(ring));
15462306a36Sopenharmony_ci	masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	xen_9pfs_write_packet(ring->data.out, p9_req->tc.sdata, size,
15762306a36Sopenharmony_ci			      &masked_prod, masked_cons,
15862306a36Sopenharmony_ci			      XEN_9PFS_RING_SIZE(ring));
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	WRITE_ONCE(p9_req->status, REQ_STATUS_SENT);
16162306a36Sopenharmony_ci	virt_wmb();			/* write ring before updating pointer */
16262306a36Sopenharmony_ci	prod += size;
16362306a36Sopenharmony_ci	ring->intf->out_prod = prod;
16462306a36Sopenharmony_ci	spin_unlock_irqrestore(&ring->lock, flags);
16562306a36Sopenharmony_ci	notify_remote_via_irq(ring->irq);
16662306a36Sopenharmony_ci	p9_req_put(client, p9_req);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void p9_xen_response(struct work_struct *work)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
17462306a36Sopenharmony_ci	struct xen_9pfs_dataring *ring;
17562306a36Sopenharmony_ci	RING_IDX cons, prod, masked_cons, masked_prod;
17662306a36Sopenharmony_ci	struct xen_9pfs_header h;
17762306a36Sopenharmony_ci	struct p9_req_t *req;
17862306a36Sopenharmony_ci	int status;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ring = container_of(work, struct xen_9pfs_dataring, work);
18162306a36Sopenharmony_ci	priv = ring->priv;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	while (1) {
18462306a36Sopenharmony_ci		cons = ring->intf->in_cons;
18562306a36Sopenharmony_ci		prod = ring->intf->in_prod;
18662306a36Sopenharmony_ci		virt_rmb();
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		if (xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) <
18962306a36Sopenharmony_ci		    sizeof(h)) {
19062306a36Sopenharmony_ci			notify_remote_via_irq(ring->irq);
19162306a36Sopenharmony_ci			return;
19262306a36Sopenharmony_ci		}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE(ring));
19562306a36Sopenharmony_ci		masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		/* First, read just the header */
19862306a36Sopenharmony_ci		xen_9pfs_read_packet(&h, ring->data.in, sizeof(h),
19962306a36Sopenharmony_ci				     masked_prod, &masked_cons,
20062306a36Sopenharmony_ci				     XEN_9PFS_RING_SIZE(ring));
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		req = p9_tag_lookup(priv->client, h.tag);
20362306a36Sopenharmony_ci		if (!req || req->status != REQ_STATUS_SENT) {
20462306a36Sopenharmony_ci			dev_warn(&priv->dev->dev, "Wrong req tag=%x\n", h.tag);
20562306a36Sopenharmony_ci			cons += h.size;
20662306a36Sopenharmony_ci			virt_mb();
20762306a36Sopenharmony_ci			ring->intf->in_cons = cons;
20862306a36Sopenharmony_ci			continue;
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		if (h.size > req->rc.capacity) {
21262306a36Sopenharmony_ci			dev_warn(&priv->dev->dev,
21362306a36Sopenharmony_ci				 "requested packet size too big: %d for tag %d with capacity %zd\n",
21462306a36Sopenharmony_ci				 h.size, h.tag, req->rc.capacity);
21562306a36Sopenharmony_ci			WRITE_ONCE(req->status, REQ_STATUS_ERROR);
21662306a36Sopenharmony_ci			goto recv_error;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		req->rc.size = h.size;
22062306a36Sopenharmony_ci		req->rc.id = h.id;
22162306a36Sopenharmony_ci		req->rc.tag = h.tag;
22262306a36Sopenharmony_ci		req->rc.offset = 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
22562306a36Sopenharmony_ci		/* Then, read the whole packet (including the header) */
22662306a36Sopenharmony_ci		xen_9pfs_read_packet(req->rc.sdata, ring->data.in, h.size,
22762306a36Sopenharmony_ci				     masked_prod, &masked_cons,
22862306a36Sopenharmony_ci				     XEN_9PFS_RING_SIZE(ring));
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cirecv_error:
23162306a36Sopenharmony_ci		virt_mb();
23262306a36Sopenharmony_ci		cons += h.size;
23362306a36Sopenharmony_ci		ring->intf->in_cons = cons;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		status = (req->status != REQ_STATUS_ERROR) ?
23662306a36Sopenharmony_ci			REQ_STATUS_RCVD : REQ_STATUS_ERROR;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		p9_client_cb(priv->client, req, status);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic irqreturn_t xen_9pfs_front_event_handler(int irq, void *r)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct xen_9pfs_dataring *ring = r;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!ring || !ring->priv->client) {
24762306a36Sopenharmony_ci		/* ignore spurious interrupt */
24862306a36Sopenharmony_ci		return IRQ_HANDLED;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	wake_up_interruptible(&ring->wq);
25262306a36Sopenharmony_ci	schedule_work(&ring->work);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return IRQ_HANDLED;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic struct p9_trans_module p9_xen_trans = {
25862306a36Sopenharmony_ci	.name = "xen",
25962306a36Sopenharmony_ci	.maxsize = 1 << (XEN_9PFS_RING_ORDER + XEN_PAGE_SHIFT - 2),
26062306a36Sopenharmony_ci	.pooled_rbuffers = false,
26162306a36Sopenharmony_ci	.def = 1,
26262306a36Sopenharmony_ci	.create = p9_xen_create,
26362306a36Sopenharmony_ci	.close = p9_xen_close,
26462306a36Sopenharmony_ci	.request = p9_xen_request,
26562306a36Sopenharmony_ci	.cancel = p9_xen_cancel,
26662306a36Sopenharmony_ci	.owner = THIS_MODULE,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic const struct xenbus_device_id xen_9pfs_front_ids[] = {
27062306a36Sopenharmony_ci	{ "9pfs" },
27162306a36Sopenharmony_ci	{ "" }
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	int i, j;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	write_lock(&xen_9pfs_lock);
27962306a36Sopenharmony_ci	list_del(&priv->list);
28062306a36Sopenharmony_ci	write_unlock(&xen_9pfs_lock);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for (i = 0; i < priv->num_rings; i++) {
28362306a36Sopenharmony_ci		struct xen_9pfs_dataring *ring = &priv->rings[i];
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		cancel_work_sync(&ring->work);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		if (!priv->rings[i].intf)
28862306a36Sopenharmony_ci			break;
28962306a36Sopenharmony_ci		if (priv->rings[i].irq > 0)
29062306a36Sopenharmony_ci			unbind_from_irqhandler(priv->rings[i].irq, priv->dev);
29162306a36Sopenharmony_ci		if (priv->rings[i].data.in) {
29262306a36Sopenharmony_ci			for (j = 0;
29362306a36Sopenharmony_ci			     j < (1 << priv->rings[i].intf->ring_order);
29462306a36Sopenharmony_ci			     j++) {
29562306a36Sopenharmony_ci				grant_ref_t ref;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci				ref = priv->rings[i].intf->ref[j];
29862306a36Sopenharmony_ci				gnttab_end_foreign_access(ref, NULL);
29962306a36Sopenharmony_ci			}
30062306a36Sopenharmony_ci			free_pages_exact(priv->rings[i].data.in,
30162306a36Sopenharmony_ci				   1UL << (priv->rings[i].intf->ring_order +
30262306a36Sopenharmony_ci					   XEN_PAGE_SHIFT));
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci		gnttab_end_foreign_access(priv->rings[i].ref, NULL);
30562306a36Sopenharmony_ci		free_page((unsigned long)priv->rings[i].intf);
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	kfree(priv->rings);
30862306a36Sopenharmony_ci	kfree(priv->tag);
30962306a36Sopenharmony_ci	kfree(priv);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void xen_9pfs_front_remove(struct xenbus_device *dev)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
31762306a36Sopenharmony_ci	xen_9pfs_front_free(priv);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev,
32162306a36Sopenharmony_ci					 struct xen_9pfs_dataring *ring,
32262306a36Sopenharmony_ci					 unsigned int order)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int i = 0;
32562306a36Sopenharmony_ci	int ret = -ENOMEM;
32662306a36Sopenharmony_ci	void *bytes = NULL;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	init_waitqueue_head(&ring->wq);
32962306a36Sopenharmony_ci	spin_lock_init(&ring->lock);
33062306a36Sopenharmony_ci	INIT_WORK(&ring->work, p9_xen_response);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	ring->intf = (struct xen_9pfs_data_intf *)get_zeroed_page(GFP_KERNEL);
33362306a36Sopenharmony_ci	if (!ring->intf)
33462306a36Sopenharmony_ci		return ret;
33562306a36Sopenharmony_ci	ret = gnttab_grant_foreign_access(dev->otherend_id,
33662306a36Sopenharmony_ci					  virt_to_gfn(ring->intf), 0);
33762306a36Sopenharmony_ci	if (ret < 0)
33862306a36Sopenharmony_ci		goto out;
33962306a36Sopenharmony_ci	ring->ref = ret;
34062306a36Sopenharmony_ci	bytes = alloc_pages_exact(1UL << (order + XEN_PAGE_SHIFT),
34162306a36Sopenharmony_ci				  GFP_KERNEL | __GFP_ZERO);
34262306a36Sopenharmony_ci	if (!bytes) {
34362306a36Sopenharmony_ci		ret = -ENOMEM;
34462306a36Sopenharmony_ci		goto out;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	for (; i < (1 << order); i++) {
34762306a36Sopenharmony_ci		ret = gnttab_grant_foreign_access(
34862306a36Sopenharmony_ci				dev->otherend_id, virt_to_gfn(bytes) + i, 0);
34962306a36Sopenharmony_ci		if (ret < 0)
35062306a36Sopenharmony_ci			goto out;
35162306a36Sopenharmony_ci		ring->intf->ref[i] = ret;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci	ring->intf->ring_order = order;
35462306a36Sopenharmony_ci	ring->data.in = bytes;
35562306a36Sopenharmony_ci	ring->data.out = bytes + XEN_FLEX_RING_SIZE(order);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = xenbus_alloc_evtchn(dev, &ring->evtchn);
35862306a36Sopenharmony_ci	if (ret)
35962306a36Sopenharmony_ci		goto out;
36062306a36Sopenharmony_ci	ring->irq = bind_evtchn_to_irqhandler(ring->evtchn,
36162306a36Sopenharmony_ci					      xen_9pfs_front_event_handler,
36262306a36Sopenharmony_ci					      0, "xen_9pfs-frontend", ring);
36362306a36Sopenharmony_ci	if (ring->irq >= 0)
36462306a36Sopenharmony_ci		return 0;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	xenbus_free_evtchn(dev, ring->evtchn);
36762306a36Sopenharmony_ci	ret = ring->irq;
36862306a36Sopenharmony_ciout:
36962306a36Sopenharmony_ci	if (bytes) {
37062306a36Sopenharmony_ci		for (i--; i >= 0; i--)
37162306a36Sopenharmony_ci			gnttab_end_foreign_access(ring->intf->ref[i], NULL);
37262306a36Sopenharmony_ci		free_pages_exact(bytes, 1UL << (order + XEN_PAGE_SHIFT));
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	gnttab_end_foreign_access(ring->ref, NULL);
37562306a36Sopenharmony_ci	free_page((unsigned long)ring->intf);
37662306a36Sopenharmony_ci	return ret;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int xen_9pfs_front_init(struct xenbus_device *dev)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int ret, i;
38262306a36Sopenharmony_ci	struct xenbus_transaction xbt;
38362306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
38462306a36Sopenharmony_ci	char *versions, *v;
38562306a36Sopenharmony_ci	unsigned int max_rings, max_ring_order, len = 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len);
38862306a36Sopenharmony_ci	if (IS_ERR(versions))
38962306a36Sopenharmony_ci		return PTR_ERR(versions);
39062306a36Sopenharmony_ci	for (v = versions; *v; v++) {
39162306a36Sopenharmony_ci		if (simple_strtoul(v, &v, 10) == 1) {
39262306a36Sopenharmony_ci			v = NULL;
39362306a36Sopenharmony_ci			break;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	if (v) {
39762306a36Sopenharmony_ci		kfree(versions);
39862306a36Sopenharmony_ci		return -EINVAL;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci	kfree(versions);
40162306a36Sopenharmony_ci	max_rings = xenbus_read_unsigned(dev->otherend, "max-rings", 0);
40262306a36Sopenharmony_ci	if (max_rings < XEN_9PFS_NUM_RINGS)
40362306a36Sopenharmony_ci		return -EINVAL;
40462306a36Sopenharmony_ci	max_ring_order = xenbus_read_unsigned(dev->otherend,
40562306a36Sopenharmony_ci					      "max-ring-page-order", 0);
40662306a36Sopenharmony_ci	if (max_ring_order > XEN_9PFS_RING_ORDER)
40762306a36Sopenharmony_ci		max_ring_order = XEN_9PFS_RING_ORDER;
40862306a36Sopenharmony_ci	if (p9_xen_trans.maxsize > XEN_FLEX_RING_SIZE(max_ring_order))
40962306a36Sopenharmony_ci		p9_xen_trans.maxsize = XEN_FLEX_RING_SIZE(max_ring_order) / 2;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	priv->num_rings = XEN_9PFS_NUM_RINGS;
41262306a36Sopenharmony_ci	priv->rings = kcalloc(priv->num_rings, sizeof(*priv->rings),
41362306a36Sopenharmony_ci			      GFP_KERNEL);
41462306a36Sopenharmony_ci	if (!priv->rings) {
41562306a36Sopenharmony_ci		kfree(priv);
41662306a36Sopenharmony_ci		return -ENOMEM;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	for (i = 0; i < priv->num_rings; i++) {
42062306a36Sopenharmony_ci		priv->rings[i].priv = priv;
42162306a36Sopenharmony_ci		ret = xen_9pfs_front_alloc_dataring(dev, &priv->rings[i],
42262306a36Sopenharmony_ci						    max_ring_order);
42362306a36Sopenharmony_ci		if (ret < 0)
42462306a36Sopenharmony_ci			goto error;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci again:
42862306a36Sopenharmony_ci	ret = xenbus_transaction_start(&xbt);
42962306a36Sopenharmony_ci	if (ret) {
43062306a36Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "starting transaction");
43162306a36Sopenharmony_ci		goto error;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, "version", "%u", 1);
43462306a36Sopenharmony_ci	if (ret)
43562306a36Sopenharmony_ci		goto error_xenbus;
43662306a36Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, "num-rings", "%u",
43762306a36Sopenharmony_ci			    priv->num_rings);
43862306a36Sopenharmony_ci	if (ret)
43962306a36Sopenharmony_ci		goto error_xenbus;
44062306a36Sopenharmony_ci	for (i = 0; i < priv->num_rings; i++) {
44162306a36Sopenharmony_ci		char str[16];
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		BUILD_BUG_ON(XEN_9PFS_NUM_RINGS > 9);
44462306a36Sopenharmony_ci		sprintf(str, "ring-ref%d", i);
44562306a36Sopenharmony_ci		ret = xenbus_printf(xbt, dev->nodename, str, "%d",
44662306a36Sopenharmony_ci				    priv->rings[i].ref);
44762306a36Sopenharmony_ci		if (ret)
44862306a36Sopenharmony_ci			goto error_xenbus;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		sprintf(str, "event-channel-%d", i);
45162306a36Sopenharmony_ci		ret = xenbus_printf(xbt, dev->nodename, str, "%u",
45262306a36Sopenharmony_ci				    priv->rings[i].evtchn);
45362306a36Sopenharmony_ci		if (ret)
45462306a36Sopenharmony_ci			goto error_xenbus;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci	priv->tag = xenbus_read(xbt, dev->nodename, "tag", NULL);
45762306a36Sopenharmony_ci	if (IS_ERR(priv->tag)) {
45862306a36Sopenharmony_ci		ret = PTR_ERR(priv->tag);
45962306a36Sopenharmony_ci		goto error_xenbus;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	ret = xenbus_transaction_end(xbt, 0);
46262306a36Sopenharmony_ci	if (ret) {
46362306a36Sopenharmony_ci		if (ret == -EAGAIN)
46462306a36Sopenharmony_ci			goto again;
46562306a36Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "completing transaction");
46662306a36Sopenharmony_ci		goto error;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci error_xenbus:
47262306a36Sopenharmony_ci	xenbus_transaction_end(xbt, 1);
47362306a36Sopenharmony_ci	xenbus_dev_fatal(dev, ret, "writing xenstore");
47462306a36Sopenharmony_ci error:
47562306a36Sopenharmony_ci	xen_9pfs_front_free(priv);
47662306a36Sopenharmony_ci	return ret;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic int xen_9pfs_front_probe(struct xenbus_device *dev,
48062306a36Sopenharmony_ci				const struct xenbus_device_id *id)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct xen_9pfs_front_priv *priv = NULL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
48562306a36Sopenharmony_ci	if (!priv)
48662306a36Sopenharmony_ci		return -ENOMEM;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	priv->dev = dev;
48962306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, priv);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	write_lock(&xen_9pfs_lock);
49262306a36Sopenharmony_ci	list_add_tail(&priv->list, &xen_9pfs_devs);
49362306a36Sopenharmony_ci	write_unlock(&xen_9pfs_lock);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return 0;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic int xen_9pfs_front_resume(struct xenbus_device *dev)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	dev_warn(&dev->dev, "suspend/resume unsupported\n");
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void xen_9pfs_front_changed(struct xenbus_device *dev,
50562306a36Sopenharmony_ci				   enum xenbus_state backend_state)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	switch (backend_state) {
50862306a36Sopenharmony_ci	case XenbusStateReconfiguring:
50962306a36Sopenharmony_ci	case XenbusStateReconfigured:
51062306a36Sopenharmony_ci	case XenbusStateInitialising:
51162306a36Sopenharmony_ci	case XenbusStateInitialised:
51262306a36Sopenharmony_ci	case XenbusStateUnknown:
51362306a36Sopenharmony_ci		break;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	case XenbusStateInitWait:
51662306a36Sopenharmony_ci		if (!xen_9pfs_front_init(dev))
51762306a36Sopenharmony_ci			xenbus_switch_state(dev, XenbusStateInitialised);
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	case XenbusStateConnected:
52162306a36Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateConnected);
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	case XenbusStateClosed:
52562306a36Sopenharmony_ci		if (dev->state == XenbusStateClosed)
52662306a36Sopenharmony_ci			break;
52762306a36Sopenharmony_ci		fallthrough;	/* Missed the backend's CLOSING state */
52862306a36Sopenharmony_ci	case XenbusStateClosing:
52962306a36Sopenharmony_ci		xenbus_frontend_closed(dev);
53062306a36Sopenharmony_ci		break;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic struct xenbus_driver xen_9pfs_front_driver = {
53562306a36Sopenharmony_ci	.ids = xen_9pfs_front_ids,
53662306a36Sopenharmony_ci	.probe = xen_9pfs_front_probe,
53762306a36Sopenharmony_ci	.remove = xen_9pfs_front_remove,
53862306a36Sopenharmony_ci	.resume = xen_9pfs_front_resume,
53962306a36Sopenharmony_ci	.otherend_changed = xen_9pfs_front_changed,
54062306a36Sopenharmony_ci};
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic int __init p9_trans_xen_init(void)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	int rc;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (!xen_domain())
54762306a36Sopenharmony_ci		return -ENODEV;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	pr_info("Initialising Xen transport for 9pfs\n");
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	v9fs_register_trans(&p9_xen_trans);
55262306a36Sopenharmony_ci	rc = xenbus_register_frontend(&xen_9pfs_front_driver);
55362306a36Sopenharmony_ci	if (rc)
55462306a36Sopenharmony_ci		v9fs_unregister_trans(&p9_xen_trans);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return rc;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_cimodule_init(p9_trans_xen_init);
55962306a36Sopenharmony_ciMODULE_ALIAS_9P("xen");
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void __exit p9_trans_xen_exit(void)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	v9fs_unregister_trans(&p9_xen_trans);
56462306a36Sopenharmony_ci	return xenbus_unregister_driver(&xen_9pfs_front_driver);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_cimodule_exit(p9_trans_xen_exit);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ciMODULE_ALIAS("xen:9pfs");
56962306a36Sopenharmony_ciMODULE_AUTHOR("Stefano Stabellini <stefano@aporeto.com>");
57062306a36Sopenharmony_ciMODULE_DESCRIPTION("Xen Transport for 9P");
57162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
572