Stefano Stabellini | 416efba | 2017-10-30 15:40:51 -0700 | [diff] [blame] | 1 | /* |
| 2 | * (c) 2017 Stefano Stabellini <stefano@aporeto.com> |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation; either version 2 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/module.h> |
| 16 | |
| 17 | #include <xen/events.h> |
| 18 | #include <xen/grant_table.h> |
| 19 | #include <xen/xen.h> |
| 20 | #include <xen/xenbus.h> |
| 21 | #include <xen/interface/io/pvcalls.h> |
| 22 | |
Stefano Stabellini | aa7ba37 | 2017-10-30 15:40:52 -0700 | [diff] [blame^] | 23 | #define PVCALLS_INVALID_ID UINT_MAX |
| 24 | #define PVCALLS_RING_ORDER XENBUS_MAX_RING_GRANT_ORDER |
| 25 | #define PVCALLS_NR_RSP_PER_RING __CONST_RING_SIZE(xen_pvcalls, XEN_PAGE_SIZE) |
| 26 | |
| 27 | struct pvcalls_bedata { |
| 28 | struct xen_pvcalls_front_ring ring; |
| 29 | grant_ref_t ref; |
| 30 | int irq; |
| 31 | |
| 32 | struct list_head socket_mappings; |
| 33 | spinlock_t socket_lock; |
| 34 | |
| 35 | wait_queue_head_t inflight_req; |
| 36 | struct xen_pvcalls_response rsp[PVCALLS_NR_RSP_PER_RING]; |
| 37 | }; |
| 38 | /* Only one front/back connection supported. */ |
| 39 | static struct xenbus_device *pvcalls_front_dev; |
| 40 | static atomic_t pvcalls_refcount; |
| 41 | |
| 42 | /* first increment refcount, then proceed */ |
| 43 | #define pvcalls_enter() { \ |
| 44 | atomic_inc(&pvcalls_refcount); \ |
| 45 | } |
| 46 | |
| 47 | /* first complete other operations, then decrement refcount */ |
| 48 | #define pvcalls_exit() { \ |
| 49 | atomic_dec(&pvcalls_refcount); \ |
| 50 | } |
| 51 | |
| 52 | struct sock_mapping { |
| 53 | bool active_socket; |
| 54 | struct list_head list; |
| 55 | struct socket *sock; |
| 56 | }; |
| 57 | |
| 58 | static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id) |
| 59 | { |
| 60 | return IRQ_HANDLED; |
| 61 | } |
| 62 | |
| 63 | static void pvcalls_front_free_map(struct pvcalls_bedata *bedata, |
| 64 | struct sock_mapping *map) |
| 65 | { |
| 66 | } |
| 67 | |
Stefano Stabellini | 416efba | 2017-10-30 15:40:51 -0700 | [diff] [blame] | 68 | static const struct xenbus_device_id pvcalls_front_ids[] = { |
| 69 | { "pvcalls" }, |
| 70 | { "" } |
| 71 | }; |
| 72 | |
| 73 | static int pvcalls_front_remove(struct xenbus_device *dev) |
| 74 | { |
Stefano Stabellini | aa7ba37 | 2017-10-30 15:40:52 -0700 | [diff] [blame^] | 75 | struct pvcalls_bedata *bedata; |
| 76 | struct sock_mapping *map = NULL, *n; |
| 77 | |
| 78 | bedata = dev_get_drvdata(&pvcalls_front_dev->dev); |
| 79 | dev_set_drvdata(&dev->dev, NULL); |
| 80 | pvcalls_front_dev = NULL; |
| 81 | if (bedata->irq >= 0) |
| 82 | unbind_from_irqhandler(bedata->irq, dev); |
| 83 | |
| 84 | smp_mb(); |
| 85 | while (atomic_read(&pvcalls_refcount) > 0) |
| 86 | cpu_relax(); |
| 87 | list_for_each_entry_safe(map, n, &bedata->socket_mappings, list) { |
| 88 | if (map->active_socket) { |
| 89 | /* No need to lock, refcount is 0 */ |
| 90 | pvcalls_front_free_map(bedata, map); |
| 91 | } else { |
| 92 | list_del(&map->list); |
| 93 | kfree(map); |
| 94 | } |
| 95 | } |
| 96 | if (bedata->ref >= 0) |
| 97 | gnttab_end_foreign_access(bedata->ref, 0, 0); |
| 98 | kfree(bedata->ring.sring); |
| 99 | kfree(bedata); |
| 100 | xenbus_switch_state(dev, XenbusStateClosed); |
Stefano Stabellini | 416efba | 2017-10-30 15:40:51 -0700 | [diff] [blame] | 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | static int pvcalls_front_probe(struct xenbus_device *dev, |
| 105 | const struct xenbus_device_id *id) |
| 106 | { |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | static void pvcalls_front_changed(struct xenbus_device *dev, |
| 111 | enum xenbus_state backend_state) |
| 112 | { |
| 113 | } |
| 114 | |
| 115 | static struct xenbus_driver pvcalls_front_driver = { |
| 116 | .ids = pvcalls_front_ids, |
| 117 | .probe = pvcalls_front_probe, |
| 118 | .remove = pvcalls_front_remove, |
| 119 | .otherend_changed = pvcalls_front_changed, |
| 120 | }; |
| 121 | |
| 122 | static int __init pvcalls_frontend_init(void) |
| 123 | { |
| 124 | if (!xen_domain()) |
| 125 | return -ENODEV; |
| 126 | |
| 127 | pr_info("Initialising Xen pvcalls frontend driver\n"); |
| 128 | |
| 129 | return xenbus_register_frontend(&pvcalls_front_driver); |
| 130 | } |
| 131 | |
| 132 | module_init(pvcalls_frontend_init); |