18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * linux/fs/9p/trans_xen
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Xen transport layer.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2017 by Stefano Stabellini <stefano@aporeto.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
98c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2
108c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; or, when distributed
118c2ecf20Sopenharmony_ci * separately from the Linux kernel or incorporated into other
128c2ecf20Sopenharmony_ci * software packages, subject to the following license:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
158c2ecf20Sopenharmony_ci * of this source file (the "Software"), to deal in the Software without
168c2ecf20Sopenharmony_ci * restriction, including without limitation the rights to use, copy, modify,
178c2ecf20Sopenharmony_ci * merge, publish, distribute, sublicense, and/or sell copies of the Software,
188c2ecf20Sopenharmony_ci * and to permit persons to whom the Software is furnished to do so, subject to
198c2ecf20Sopenharmony_ci * the following conditions:
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
228c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
258c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
268c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
278c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
288c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
298c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
308c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <xen/events.h>
348c2ecf20Sopenharmony_ci#include <xen/grant_table.h>
358c2ecf20Sopenharmony_ci#include <xen/xen.h>
368c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
378c2ecf20Sopenharmony_ci#include <xen/interface/io/9pfs.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <linux/module.h>
408c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
418c2ecf20Sopenharmony_ci#include <net/9p/9p.h>
428c2ecf20Sopenharmony_ci#include <net/9p/client.h>
438c2ecf20Sopenharmony_ci#include <net/9p/transport.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define XEN_9PFS_NUM_RINGS 2
468c2ecf20Sopenharmony_ci#define XEN_9PFS_RING_ORDER 9
478c2ecf20Sopenharmony_ci#define XEN_9PFS_RING_SIZE(ring)  XEN_FLEX_RING_SIZE(ring->intf->ring_order)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct xen_9pfs_header {
508c2ecf20Sopenharmony_ci	uint32_t size;
518c2ecf20Sopenharmony_ci	uint8_t id;
528c2ecf20Sopenharmony_ci	uint16_t tag;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* uint8_t sdata[]; */
558c2ecf20Sopenharmony_ci} __attribute__((packed));
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* One per ring, more than one per 9pfs share */
588c2ecf20Sopenharmony_cistruct xen_9pfs_dataring {
598c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	struct xen_9pfs_data_intf *intf;
628c2ecf20Sopenharmony_ci	grant_ref_t ref;
638c2ecf20Sopenharmony_ci	int evtchn;
648c2ecf20Sopenharmony_ci	int irq;
658c2ecf20Sopenharmony_ci	/* protect a ring from concurrent accesses */
668c2ecf20Sopenharmony_ci	spinlock_t lock;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	struct xen_9pfs_data data;
698c2ecf20Sopenharmony_ci	wait_queue_head_t wq;
708c2ecf20Sopenharmony_ci	struct work_struct work;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* One per 9pfs share */
748c2ecf20Sopenharmony_cistruct xen_9pfs_front_priv {
758c2ecf20Sopenharmony_ci	struct list_head list;
768c2ecf20Sopenharmony_ci	struct xenbus_device *dev;
778c2ecf20Sopenharmony_ci	char *tag;
788c2ecf20Sopenharmony_ci	struct p9_client *client;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	int num_rings;
818c2ecf20Sopenharmony_ci	struct xen_9pfs_dataring *rings;
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic LIST_HEAD(xen_9pfs_devs);
858c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(xen_9pfs_lock);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* We don't currently allow canceling of requests */
888c2ecf20Sopenharmony_cistatic int p9_xen_cancel(struct p9_client *client, struct p9_req_t *req)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	return 1;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int p9_xen_create(struct p9_client *client, const char *addr, char *args)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (addr == NULL)
988c2ecf20Sopenharmony_ci		return -EINVAL;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	read_lock(&xen_9pfs_lock);
1018c2ecf20Sopenharmony_ci	list_for_each_entry(priv, &xen_9pfs_devs, list) {
1028c2ecf20Sopenharmony_ci		if (!strcmp(priv->tag, addr)) {
1038c2ecf20Sopenharmony_ci			priv->client = client;
1048c2ecf20Sopenharmony_ci			read_unlock(&xen_9pfs_lock);
1058c2ecf20Sopenharmony_ci			return 0;
1068c2ecf20Sopenharmony_ci		}
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	read_unlock(&xen_9pfs_lock);
1098c2ecf20Sopenharmony_ci	return -EINVAL;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void p9_xen_close(struct p9_client *client)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	read_lock(&xen_9pfs_lock);
1178c2ecf20Sopenharmony_ci	list_for_each_entry(priv, &xen_9pfs_devs, list) {
1188c2ecf20Sopenharmony_ci		if (priv->client == client) {
1198c2ecf20Sopenharmony_ci			priv->client = NULL;
1208c2ecf20Sopenharmony_ci			read_unlock(&xen_9pfs_lock);
1218c2ecf20Sopenharmony_ci			return;
1228c2ecf20Sopenharmony_ci		}
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	read_unlock(&xen_9pfs_lock);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic bool p9_xen_write_todo(struct xen_9pfs_dataring *ring, RING_IDX size)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	RING_IDX cons, prod;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	cons = ring->intf->out_cons;
1328c2ecf20Sopenharmony_ci	prod = ring->intf->out_prod;
1338c2ecf20Sopenharmony_ci	virt_mb();
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return XEN_9PFS_RING_SIZE(ring) -
1368c2ecf20Sopenharmony_ci		xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) >= size;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int p9_xen_request(struct p9_client *client, struct p9_req_t *p9_req)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
1428c2ecf20Sopenharmony_ci	RING_IDX cons, prod, masked_cons, masked_prod;
1438c2ecf20Sopenharmony_ci	unsigned long flags;
1448c2ecf20Sopenharmony_ci	u32 size = p9_req->tc.size;
1458c2ecf20Sopenharmony_ci	struct xen_9pfs_dataring *ring;
1468c2ecf20Sopenharmony_ci	int num;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	read_lock(&xen_9pfs_lock);
1498c2ecf20Sopenharmony_ci	list_for_each_entry(priv, &xen_9pfs_devs, list) {
1508c2ecf20Sopenharmony_ci		if (priv->client == client)
1518c2ecf20Sopenharmony_ci			break;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	read_unlock(&xen_9pfs_lock);
1548c2ecf20Sopenharmony_ci	if (list_entry_is_head(priv, &xen_9pfs_devs, list))
1558c2ecf20Sopenharmony_ci		return -EINVAL;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	num = p9_req->tc.tag % priv->num_rings;
1588c2ecf20Sopenharmony_ci	ring = &priv->rings[num];
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ciagain:
1618c2ecf20Sopenharmony_ci	while (wait_event_killable(ring->wq,
1628c2ecf20Sopenharmony_ci				   p9_xen_write_todo(ring, size)) != 0)
1638c2ecf20Sopenharmony_ci		;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ring->lock, flags);
1668c2ecf20Sopenharmony_ci	cons = ring->intf->out_cons;
1678c2ecf20Sopenharmony_ci	prod = ring->intf->out_prod;
1688c2ecf20Sopenharmony_ci	virt_mb();
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (XEN_9PFS_RING_SIZE(ring) -
1718c2ecf20Sopenharmony_ci	    xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) < size) {
1728c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ring->lock, flags);
1738c2ecf20Sopenharmony_ci		goto again;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE(ring));
1778c2ecf20Sopenharmony_ci	masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	xen_9pfs_write_packet(ring->data.out, p9_req->tc.sdata, size,
1808c2ecf20Sopenharmony_ci			      &masked_prod, masked_cons,
1818c2ecf20Sopenharmony_ci			      XEN_9PFS_RING_SIZE(ring));
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	p9_req->status = REQ_STATUS_SENT;
1848c2ecf20Sopenharmony_ci	virt_wmb();			/* write ring before updating pointer */
1858c2ecf20Sopenharmony_ci	prod += size;
1868c2ecf20Sopenharmony_ci	ring->intf->out_prod = prod;
1878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ring->lock, flags);
1888c2ecf20Sopenharmony_ci	notify_remote_via_irq(ring->irq);
1898c2ecf20Sopenharmony_ci	p9_req_put(p9_req);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void p9_xen_response(struct work_struct *work)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv;
1978c2ecf20Sopenharmony_ci	struct xen_9pfs_dataring *ring;
1988c2ecf20Sopenharmony_ci	RING_IDX cons, prod, masked_cons, masked_prod;
1998c2ecf20Sopenharmony_ci	struct xen_9pfs_header h;
2008c2ecf20Sopenharmony_ci	struct p9_req_t *req;
2018c2ecf20Sopenharmony_ci	int status;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	ring = container_of(work, struct xen_9pfs_dataring, work);
2048c2ecf20Sopenharmony_ci	priv = ring->priv;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	while (1) {
2078c2ecf20Sopenharmony_ci		cons = ring->intf->in_cons;
2088c2ecf20Sopenharmony_ci		prod = ring->intf->in_prod;
2098c2ecf20Sopenharmony_ci		virt_rmb();
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		if (xen_9pfs_queued(prod, cons, XEN_9PFS_RING_SIZE(ring)) <
2128c2ecf20Sopenharmony_ci		    sizeof(h)) {
2138c2ecf20Sopenharmony_ci			notify_remote_via_irq(ring->irq);
2148c2ecf20Sopenharmony_ci			return;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		masked_prod = xen_9pfs_mask(prod, XEN_9PFS_RING_SIZE(ring));
2188c2ecf20Sopenharmony_ci		masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		/* First, read just the header */
2218c2ecf20Sopenharmony_ci		xen_9pfs_read_packet(&h, ring->data.in, sizeof(h),
2228c2ecf20Sopenharmony_ci				     masked_prod, &masked_cons,
2238c2ecf20Sopenharmony_ci				     XEN_9PFS_RING_SIZE(ring));
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		req = p9_tag_lookup(priv->client, h.tag);
2268c2ecf20Sopenharmony_ci		if (!req || req->status != REQ_STATUS_SENT) {
2278c2ecf20Sopenharmony_ci			dev_warn(&priv->dev->dev, "Wrong req tag=%x\n", h.tag);
2288c2ecf20Sopenharmony_ci			cons += h.size;
2298c2ecf20Sopenharmony_ci			virt_mb();
2308c2ecf20Sopenharmony_ci			ring->intf->in_cons = cons;
2318c2ecf20Sopenharmony_ci			continue;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		if (h.size > req->rc.capacity) {
2358c2ecf20Sopenharmony_ci			dev_warn(&priv->dev->dev,
2368c2ecf20Sopenharmony_ci				 "requested packet size too big: %d for tag %d with capacity %zd\n",
2378c2ecf20Sopenharmony_ci				 h.size, h.tag, req->rc.capacity);
2388c2ecf20Sopenharmony_ci			req->status = REQ_STATUS_ERROR;
2398c2ecf20Sopenharmony_ci			goto recv_error;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		memcpy(&req->rc, &h, sizeof(h));
2438c2ecf20Sopenharmony_ci		req->rc.offset = 0;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring));
2468c2ecf20Sopenharmony_ci		/* Then, read the whole packet (including the header) */
2478c2ecf20Sopenharmony_ci		xen_9pfs_read_packet(req->rc.sdata, ring->data.in, h.size,
2488c2ecf20Sopenharmony_ci				     masked_prod, &masked_cons,
2498c2ecf20Sopenharmony_ci				     XEN_9PFS_RING_SIZE(ring));
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cirecv_error:
2528c2ecf20Sopenharmony_ci		virt_mb();
2538c2ecf20Sopenharmony_ci		cons += h.size;
2548c2ecf20Sopenharmony_ci		ring->intf->in_cons = cons;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		status = (req->status != REQ_STATUS_ERROR) ?
2578c2ecf20Sopenharmony_ci			REQ_STATUS_RCVD : REQ_STATUS_ERROR;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		p9_client_cb(priv->client, req, status);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic irqreturn_t xen_9pfs_front_event_handler(int irq, void *r)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct xen_9pfs_dataring *ring = r;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (!ring || !ring->priv->client) {
2688c2ecf20Sopenharmony_ci		/* ignore spurious interrupt */
2698c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	wake_up_interruptible(&ring->wq);
2738c2ecf20Sopenharmony_ci	schedule_work(&ring->work);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic struct p9_trans_module p9_xen_trans = {
2798c2ecf20Sopenharmony_ci	.name = "xen",
2808c2ecf20Sopenharmony_ci	.maxsize = 1 << (XEN_9PFS_RING_ORDER + XEN_PAGE_SHIFT - 2),
2818c2ecf20Sopenharmony_ci	.def = 1,
2828c2ecf20Sopenharmony_ci	.create = p9_xen_create,
2838c2ecf20Sopenharmony_ci	.close = p9_xen_close,
2848c2ecf20Sopenharmony_ci	.request = p9_xen_request,
2858c2ecf20Sopenharmony_ci	.cancel = p9_xen_cancel,
2868c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic const struct xenbus_device_id xen_9pfs_front_ids[] = {
2908c2ecf20Sopenharmony_ci	{ "9pfs" },
2918c2ecf20Sopenharmony_ci	{ "" }
2928c2ecf20Sopenharmony_ci};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	int i, j;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	write_lock(&xen_9pfs_lock);
2998c2ecf20Sopenharmony_ci	list_del(&priv->list);
3008c2ecf20Sopenharmony_ci	write_unlock(&xen_9pfs_lock);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	for (i = 0; i < priv->num_rings; i++) {
3038c2ecf20Sopenharmony_ci		struct xen_9pfs_dataring *ring = &priv->rings[i];
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		cancel_work_sync(&ring->work);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		if (!priv->rings[i].intf)
3088c2ecf20Sopenharmony_ci			break;
3098c2ecf20Sopenharmony_ci		if (priv->rings[i].irq > 0)
3108c2ecf20Sopenharmony_ci			unbind_from_irqhandler(priv->rings[i].irq, priv->dev);
3118c2ecf20Sopenharmony_ci		if (priv->rings[i].data.in) {
3128c2ecf20Sopenharmony_ci			for (j = 0;
3138c2ecf20Sopenharmony_ci			     j < (1 << priv->rings[i].intf->ring_order);
3148c2ecf20Sopenharmony_ci			     j++) {
3158c2ecf20Sopenharmony_ci				grant_ref_t ref;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci				ref = priv->rings[i].intf->ref[j];
3188c2ecf20Sopenharmony_ci				gnttab_end_foreign_access(ref, 0, 0);
3198c2ecf20Sopenharmony_ci			}
3208c2ecf20Sopenharmony_ci			free_pages_exact(priv->rings[i].data.in,
3218c2ecf20Sopenharmony_ci				   1UL << (priv->rings[i].intf->ring_order +
3228c2ecf20Sopenharmony_ci					   XEN_PAGE_SHIFT));
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		gnttab_end_foreign_access(priv->rings[i].ref, 0, 0);
3258c2ecf20Sopenharmony_ci		free_page((unsigned long)priv->rings[i].intf);
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	kfree(priv->rings);
3288c2ecf20Sopenharmony_ci	kfree(priv->tag);
3298c2ecf20Sopenharmony_ci	kfree(priv);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int xen_9pfs_front_remove(struct xenbus_device *dev)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
3378c2ecf20Sopenharmony_ci	xen_9pfs_front_free(priv);
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev,
3428c2ecf20Sopenharmony_ci					 struct xen_9pfs_dataring *ring,
3438c2ecf20Sopenharmony_ci					 unsigned int order)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	int i = 0;
3468c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
3478c2ecf20Sopenharmony_ci	void *bytes = NULL;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	init_waitqueue_head(&ring->wq);
3508c2ecf20Sopenharmony_ci	spin_lock_init(&ring->lock);
3518c2ecf20Sopenharmony_ci	INIT_WORK(&ring->work, p9_xen_response);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	ring->intf = (struct xen_9pfs_data_intf *)get_zeroed_page(GFP_KERNEL);
3548c2ecf20Sopenharmony_ci	if (!ring->intf)
3558c2ecf20Sopenharmony_ci		return ret;
3568c2ecf20Sopenharmony_ci	ret = gnttab_grant_foreign_access(dev->otherend_id,
3578c2ecf20Sopenharmony_ci					  virt_to_gfn(ring->intf), 0);
3588c2ecf20Sopenharmony_ci	if (ret < 0)
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci	ring->ref = ret;
3618c2ecf20Sopenharmony_ci	bytes = alloc_pages_exact(1UL << (order + XEN_PAGE_SHIFT),
3628c2ecf20Sopenharmony_ci				  GFP_KERNEL | __GFP_ZERO);
3638c2ecf20Sopenharmony_ci	if (!bytes) {
3648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3658c2ecf20Sopenharmony_ci		goto out;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci	for (; i < (1 << order); i++) {
3688c2ecf20Sopenharmony_ci		ret = gnttab_grant_foreign_access(
3698c2ecf20Sopenharmony_ci				dev->otherend_id, virt_to_gfn(bytes) + i, 0);
3708c2ecf20Sopenharmony_ci		if (ret < 0)
3718c2ecf20Sopenharmony_ci			goto out;
3728c2ecf20Sopenharmony_ci		ring->intf->ref[i] = ret;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	ring->intf->ring_order = order;
3758c2ecf20Sopenharmony_ci	ring->data.in = bytes;
3768c2ecf20Sopenharmony_ci	ring->data.out = bytes + XEN_FLEX_RING_SIZE(order);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	ret = xenbus_alloc_evtchn(dev, &ring->evtchn);
3798c2ecf20Sopenharmony_ci	if (ret)
3808c2ecf20Sopenharmony_ci		goto out;
3818c2ecf20Sopenharmony_ci	ring->irq = bind_evtchn_to_irqhandler(ring->evtchn,
3828c2ecf20Sopenharmony_ci					      xen_9pfs_front_event_handler,
3838c2ecf20Sopenharmony_ci					      0, "xen_9pfs-frontend", ring);
3848c2ecf20Sopenharmony_ci	if (ring->irq >= 0)
3858c2ecf20Sopenharmony_ci		return 0;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	xenbus_free_evtchn(dev, ring->evtchn);
3888c2ecf20Sopenharmony_ci	ret = ring->irq;
3898c2ecf20Sopenharmony_ciout:
3908c2ecf20Sopenharmony_ci	if (bytes) {
3918c2ecf20Sopenharmony_ci		for (i--; i >= 0; i--)
3928c2ecf20Sopenharmony_ci			gnttab_end_foreign_access(ring->intf->ref[i], 0, 0);
3938c2ecf20Sopenharmony_ci		free_pages_exact(bytes, 1UL << (order + XEN_PAGE_SHIFT));
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci	gnttab_end_foreign_access(ring->ref, 0, 0);
3968c2ecf20Sopenharmony_ci	free_page((unsigned long)ring->intf);
3978c2ecf20Sopenharmony_ci	return ret;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int xen_9pfs_front_init(struct xenbus_device *dev)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	int ret, i;
4038c2ecf20Sopenharmony_ci	struct xenbus_transaction xbt;
4048c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
4058c2ecf20Sopenharmony_ci	char *versions, *v;
4068c2ecf20Sopenharmony_ci	unsigned int max_rings, max_ring_order, len = 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len);
4098c2ecf20Sopenharmony_ci	if (IS_ERR(versions))
4108c2ecf20Sopenharmony_ci		return PTR_ERR(versions);
4118c2ecf20Sopenharmony_ci	for (v = versions; *v; v++) {
4128c2ecf20Sopenharmony_ci		if (simple_strtoul(v, &v, 10) == 1) {
4138c2ecf20Sopenharmony_ci			v = NULL;
4148c2ecf20Sopenharmony_ci			break;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci	if (v) {
4188c2ecf20Sopenharmony_ci		kfree(versions);
4198c2ecf20Sopenharmony_ci		return -EINVAL;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci	kfree(versions);
4228c2ecf20Sopenharmony_ci	max_rings = xenbus_read_unsigned(dev->otherend, "max-rings", 0);
4238c2ecf20Sopenharmony_ci	if (max_rings < XEN_9PFS_NUM_RINGS)
4248c2ecf20Sopenharmony_ci		return -EINVAL;
4258c2ecf20Sopenharmony_ci	max_ring_order = xenbus_read_unsigned(dev->otherend,
4268c2ecf20Sopenharmony_ci					      "max-ring-page-order", 0);
4278c2ecf20Sopenharmony_ci	if (max_ring_order > XEN_9PFS_RING_ORDER)
4288c2ecf20Sopenharmony_ci		max_ring_order = XEN_9PFS_RING_ORDER;
4298c2ecf20Sopenharmony_ci	if (p9_xen_trans.maxsize > XEN_FLEX_RING_SIZE(max_ring_order))
4308c2ecf20Sopenharmony_ci		p9_xen_trans.maxsize = XEN_FLEX_RING_SIZE(max_ring_order) / 2;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	priv->num_rings = XEN_9PFS_NUM_RINGS;
4338c2ecf20Sopenharmony_ci	priv->rings = kcalloc(priv->num_rings, sizeof(*priv->rings),
4348c2ecf20Sopenharmony_ci			      GFP_KERNEL);
4358c2ecf20Sopenharmony_ci	if (!priv->rings) {
4368c2ecf20Sopenharmony_ci		kfree(priv);
4378c2ecf20Sopenharmony_ci		return -ENOMEM;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	for (i = 0; i < priv->num_rings; i++) {
4418c2ecf20Sopenharmony_ci		priv->rings[i].priv = priv;
4428c2ecf20Sopenharmony_ci		ret = xen_9pfs_front_alloc_dataring(dev, &priv->rings[i],
4438c2ecf20Sopenharmony_ci						    max_ring_order);
4448c2ecf20Sopenharmony_ci		if (ret < 0)
4458c2ecf20Sopenharmony_ci			goto error;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci again:
4498c2ecf20Sopenharmony_ci	ret = xenbus_transaction_start(&xbt);
4508c2ecf20Sopenharmony_ci	if (ret) {
4518c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "starting transaction");
4528c2ecf20Sopenharmony_ci		goto error;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, "version", "%u", 1);
4558c2ecf20Sopenharmony_ci	if (ret)
4568c2ecf20Sopenharmony_ci		goto error_xenbus;
4578c2ecf20Sopenharmony_ci	ret = xenbus_printf(xbt, dev->nodename, "num-rings", "%u",
4588c2ecf20Sopenharmony_ci			    priv->num_rings);
4598c2ecf20Sopenharmony_ci	if (ret)
4608c2ecf20Sopenharmony_ci		goto error_xenbus;
4618c2ecf20Sopenharmony_ci	for (i = 0; i < priv->num_rings; i++) {
4628c2ecf20Sopenharmony_ci		char str[16];
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		BUILD_BUG_ON(XEN_9PFS_NUM_RINGS > 9);
4658c2ecf20Sopenharmony_ci		sprintf(str, "ring-ref%d", i);
4668c2ecf20Sopenharmony_ci		ret = xenbus_printf(xbt, dev->nodename, str, "%d",
4678c2ecf20Sopenharmony_ci				    priv->rings[i].ref);
4688c2ecf20Sopenharmony_ci		if (ret)
4698c2ecf20Sopenharmony_ci			goto error_xenbus;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		sprintf(str, "event-channel-%d", i);
4728c2ecf20Sopenharmony_ci		ret = xenbus_printf(xbt, dev->nodename, str, "%u",
4738c2ecf20Sopenharmony_ci				    priv->rings[i].evtchn);
4748c2ecf20Sopenharmony_ci		if (ret)
4758c2ecf20Sopenharmony_ci			goto error_xenbus;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	priv->tag = xenbus_read(xbt, dev->nodename, "tag", NULL);
4788c2ecf20Sopenharmony_ci	if (IS_ERR(priv->tag)) {
4798c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->tag);
4808c2ecf20Sopenharmony_ci		goto error_xenbus;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	ret = xenbus_transaction_end(xbt, 0);
4838c2ecf20Sopenharmony_ci	if (ret) {
4848c2ecf20Sopenharmony_ci		if (ret == -EAGAIN)
4858c2ecf20Sopenharmony_ci			goto again;
4868c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, ret, "completing transaction");
4878c2ecf20Sopenharmony_ci		goto error;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	return 0;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci error_xenbus:
4938c2ecf20Sopenharmony_ci	xenbus_transaction_end(xbt, 1);
4948c2ecf20Sopenharmony_ci	xenbus_dev_fatal(dev, ret, "writing xenstore");
4958c2ecf20Sopenharmony_ci error:
4968c2ecf20Sopenharmony_ci	xen_9pfs_front_free(priv);
4978c2ecf20Sopenharmony_ci	return ret;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int xen_9pfs_front_probe(struct xenbus_device *dev,
5018c2ecf20Sopenharmony_ci				const struct xenbus_device_id *id)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct xen_9pfs_front_priv *priv = NULL;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
5068c2ecf20Sopenharmony_ci	if (!priv)
5078c2ecf20Sopenharmony_ci		return -ENOMEM;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	priv->dev = dev;
5108c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, priv);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	write_lock(&xen_9pfs_lock);
5138c2ecf20Sopenharmony_ci	list_add_tail(&priv->list, &xen_9pfs_devs);
5148c2ecf20Sopenharmony_ci	write_unlock(&xen_9pfs_lock);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return 0;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int xen_9pfs_front_resume(struct xenbus_device *dev)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	dev_warn(&dev->dev, "suspend/resume unsupported\n");
5228c2ecf20Sopenharmony_ci	return 0;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic void xen_9pfs_front_changed(struct xenbus_device *dev,
5268c2ecf20Sopenharmony_ci				   enum xenbus_state backend_state)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	switch (backend_state) {
5298c2ecf20Sopenharmony_ci	case XenbusStateReconfiguring:
5308c2ecf20Sopenharmony_ci	case XenbusStateReconfigured:
5318c2ecf20Sopenharmony_ci	case XenbusStateInitialising:
5328c2ecf20Sopenharmony_ci	case XenbusStateInitialised:
5338c2ecf20Sopenharmony_ci	case XenbusStateUnknown:
5348c2ecf20Sopenharmony_ci		break;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	case XenbusStateInitWait:
5378c2ecf20Sopenharmony_ci		if (!xen_9pfs_front_init(dev))
5388c2ecf20Sopenharmony_ci			xenbus_switch_state(dev, XenbusStateInitialised);
5398c2ecf20Sopenharmony_ci		break;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	case XenbusStateConnected:
5428c2ecf20Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateConnected);
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	case XenbusStateClosed:
5468c2ecf20Sopenharmony_ci		if (dev->state == XenbusStateClosed)
5478c2ecf20Sopenharmony_ci			break;
5488c2ecf20Sopenharmony_ci		fallthrough;	/* Missed the backend's CLOSING state */
5498c2ecf20Sopenharmony_ci	case XenbusStateClosing:
5508c2ecf20Sopenharmony_ci		xenbus_frontend_closed(dev);
5518c2ecf20Sopenharmony_ci		break;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic struct xenbus_driver xen_9pfs_front_driver = {
5568c2ecf20Sopenharmony_ci	.ids = xen_9pfs_front_ids,
5578c2ecf20Sopenharmony_ci	.probe = xen_9pfs_front_probe,
5588c2ecf20Sopenharmony_ci	.remove = xen_9pfs_front_remove,
5598c2ecf20Sopenharmony_ci	.resume = xen_9pfs_front_resume,
5608c2ecf20Sopenharmony_ci	.otherend_changed = xen_9pfs_front_changed,
5618c2ecf20Sopenharmony_ci};
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int p9_trans_xen_init(void)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	int rc;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (!xen_domain())
5688c2ecf20Sopenharmony_ci		return -ENODEV;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	pr_info("Initialising Xen transport for 9pfs\n");
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	v9fs_register_trans(&p9_xen_trans);
5738c2ecf20Sopenharmony_ci	rc = xenbus_register_frontend(&xen_9pfs_front_driver);
5748c2ecf20Sopenharmony_ci	if (rc)
5758c2ecf20Sopenharmony_ci		v9fs_unregister_trans(&p9_xen_trans);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return rc;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_cimodule_init(p9_trans_xen_init);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic void p9_trans_xen_exit(void)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	v9fs_unregister_trans(&p9_xen_trans);
5848c2ecf20Sopenharmony_ci	return xenbus_unregister_driver(&xen_9pfs_front_driver);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_cimodule_exit(p9_trans_xen_exit);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefano Stabellini <stefano@aporeto.com>");
5898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xen Transport for 9P");
5908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
591