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