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