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