18c2ecf20Sopenharmony_ci/****************************************************************************** 28c2ecf20Sopenharmony_ci * evtchn.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Driver for receiving and demuxing event-channel signals. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2004-2005, K A Fraser 78c2ecf20Sopenharmony_ci * Multi-process extensions Copyright (c) 2004, Steven Smith 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License version 2 118c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; or, when distributed 128c2ecf20Sopenharmony_ci * separately from the Linux kernel or incorporated into other 138c2ecf20Sopenharmony_ci * software packages, subject to the following license: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 168c2ecf20Sopenharmony_ci * of this source file (the "Software"), to deal in the Software without 178c2ecf20Sopenharmony_ci * restriction, including without limitation the rights to use, copy, modify, 188c2ecf20Sopenharmony_ci * merge, publish, distribute, sublicense, and/or sell copies of the Software, 198c2ecf20Sopenharmony_ci * and to permit persons to whom the Software is furnished to do so, subject to 208c2ecf20Sopenharmony_ci * the following conditions: 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 238c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 268c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 278c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 288c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 298c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 308c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 318c2ecf20Sopenharmony_ci * IN THE SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/sched.h> 398c2ecf20Sopenharmony_ci#include <linux/slab.h> 408c2ecf20Sopenharmony_ci#include <linux/string.h> 418c2ecf20Sopenharmony_ci#include <linux/errno.h> 428c2ecf20Sopenharmony_ci#include <linux/fs.h> 438c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 448c2ecf20Sopenharmony_ci#include <linux/major.h> 458c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 468c2ecf20Sopenharmony_ci#include <linux/stat.h> 478c2ecf20Sopenharmony_ci#include <linux/poll.h> 488c2ecf20Sopenharmony_ci#include <linux/irq.h> 498c2ecf20Sopenharmony_ci#include <linux/init.h> 508c2ecf20Sopenharmony_ci#include <linux/mutex.h> 518c2ecf20Sopenharmony_ci#include <linux/cpu.h> 528c2ecf20Sopenharmony_ci#include <linux/mm.h> 538c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <xen/xen.h> 568c2ecf20Sopenharmony_ci#include <xen/events.h> 578c2ecf20Sopenharmony_ci#include <xen/evtchn.h> 588c2ecf20Sopenharmony_ci#include <xen/xen-ops.h> 598c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h> 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct per_user_data { 628c2ecf20Sopenharmony_ci struct mutex bind_mutex; /* serialize bind/unbind operations */ 638c2ecf20Sopenharmony_ci struct rb_root evtchns; 648c2ecf20Sopenharmony_ci unsigned int nr_evtchns; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Notification ring, accessed via /dev/xen/evtchn. */ 678c2ecf20Sopenharmony_ci unsigned int ring_size; 688c2ecf20Sopenharmony_ci evtchn_port_t *ring; 698c2ecf20Sopenharmony_ci unsigned int ring_cons, ring_prod, ring_overflow; 708c2ecf20Sopenharmony_ci struct mutex ring_cons_mutex; /* protect against concurrent readers */ 718c2ecf20Sopenharmony_ci spinlock_t ring_prod_lock; /* product against concurrent interrupts */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Processes wait on this queue when ring is empty. */ 748c2ecf20Sopenharmony_ci wait_queue_head_t evtchn_wait; 758c2ecf20Sopenharmony_ci struct fasync_struct *evtchn_async_queue; 768c2ecf20Sopenharmony_ci const char *name; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci domid_t restrict_domid; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define UNRESTRICTED_DOMID ((domid_t)-1) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct user_evtchn { 848c2ecf20Sopenharmony_ci struct rb_node node; 858c2ecf20Sopenharmony_ci struct per_user_data *user; 868c2ecf20Sopenharmony_ci evtchn_port_t port; 878c2ecf20Sopenharmony_ci bool enabled; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void evtchn_free_ring(evtchn_port_t *ring) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci kvfree(ring); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic unsigned int evtchn_ring_offset(struct per_user_data *u, 968c2ecf20Sopenharmony_ci unsigned int idx) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return idx & (u->ring_size - 1); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic evtchn_port_t *evtchn_ring_entry(struct per_user_data *u, 1028c2ecf20Sopenharmony_ci unsigned int idx) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return u->ring + evtchn_ring_offset(u, idx); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int add_evtchn(struct per_user_data *u, struct user_evtchn *evtchn) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct rb_node **new = &(u->evtchns.rb_node), *parent = NULL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci u->nr_evtchns++; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci while (*new) { 1148c2ecf20Sopenharmony_ci struct user_evtchn *this; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci this = rb_entry(*new, struct user_evtchn, node); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci parent = *new; 1198c2ecf20Sopenharmony_ci if (this->port < evtchn->port) 1208c2ecf20Sopenharmony_ci new = &((*new)->rb_left); 1218c2ecf20Sopenharmony_ci else if (this->port > evtchn->port) 1228c2ecf20Sopenharmony_ci new = &((*new)->rb_right); 1238c2ecf20Sopenharmony_ci else 1248c2ecf20Sopenharmony_ci return -EEXIST; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Add new node and rebalance tree. */ 1288c2ecf20Sopenharmony_ci rb_link_node(&evtchn->node, parent, new); 1298c2ecf20Sopenharmony_ci rb_insert_color(&evtchn->node, &u->evtchns); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void del_evtchn(struct per_user_data *u, struct user_evtchn *evtchn) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci u->nr_evtchns--; 1378c2ecf20Sopenharmony_ci rb_erase(&evtchn->node, &u->evtchns); 1388c2ecf20Sopenharmony_ci kfree(evtchn); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic struct user_evtchn *find_evtchn(struct per_user_data *u, 1428c2ecf20Sopenharmony_ci evtchn_port_t port) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct rb_node *node = u->evtchns.rb_node; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci while (node) { 1478c2ecf20Sopenharmony_ci struct user_evtchn *evtchn; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci evtchn = rb_entry(node, struct user_evtchn, node); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (evtchn->port < port) 1528c2ecf20Sopenharmony_ci node = node->rb_left; 1538c2ecf20Sopenharmony_ci else if (evtchn->port > port) 1548c2ecf20Sopenharmony_ci node = node->rb_right; 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci return evtchn; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci return NULL; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic irqreturn_t evtchn_interrupt(int irq, void *data) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct user_evtchn *evtchn = data; 1648c2ecf20Sopenharmony_ci struct per_user_data *u = evtchn->user; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci WARN(!evtchn->enabled, 1678c2ecf20Sopenharmony_ci "Interrupt for port %u, but apparently not enabled; per-user %p\n", 1688c2ecf20Sopenharmony_ci evtchn->port, u); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci evtchn->enabled = false; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spin_lock(&u->ring_prod_lock); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if ((u->ring_prod - u->ring_cons) < u->ring_size) { 1758c2ecf20Sopenharmony_ci *evtchn_ring_entry(u, u->ring_prod) = evtchn->port; 1768c2ecf20Sopenharmony_ci wmb(); /* Ensure ring contents visible */ 1778c2ecf20Sopenharmony_ci if (u->ring_cons == u->ring_prod++) { 1788c2ecf20Sopenharmony_ci wake_up_interruptible(&u->evtchn_wait); 1798c2ecf20Sopenharmony_ci kill_fasync(&u->evtchn_async_queue, 1808c2ecf20Sopenharmony_ci SIGIO, POLL_IN); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } else 1838c2ecf20Sopenharmony_ci u->ring_overflow = 1; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci spin_unlock(&u->ring_prod_lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic ssize_t evtchn_read(struct file *file, char __user *buf, 1918c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci int rc; 1948c2ecf20Sopenharmony_ci unsigned int c, p, bytes1 = 0, bytes2 = 0; 1958c2ecf20Sopenharmony_ci struct per_user_data *u = file->private_data; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Whole number of ports. */ 1988c2ecf20Sopenharmony_ci count &= ~(sizeof(evtchn_port_t)-1); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (count == 0) 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (count > PAGE_SIZE) 2048c2ecf20Sopenharmony_ci count = PAGE_SIZE; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (;;) { 2078c2ecf20Sopenharmony_ci mutex_lock(&u->ring_cons_mutex); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci rc = -EFBIG; 2108c2ecf20Sopenharmony_ci if (u->ring_overflow) 2118c2ecf20Sopenharmony_ci goto unlock_out; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci c = u->ring_cons; 2148c2ecf20Sopenharmony_ci p = u->ring_prod; 2158c2ecf20Sopenharmony_ci if (c != p) 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci mutex_unlock(&u->ring_cons_mutex); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 2218c2ecf20Sopenharmony_ci return -EAGAIN; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci rc = wait_event_interruptible(u->evtchn_wait, 2248c2ecf20Sopenharmony_ci u->ring_cons != u->ring_prod); 2258c2ecf20Sopenharmony_ci if (rc) 2268c2ecf20Sopenharmony_ci return rc; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */ 2308c2ecf20Sopenharmony_ci if (((c ^ p) & u->ring_size) != 0) { 2318c2ecf20Sopenharmony_ci bytes1 = (u->ring_size - evtchn_ring_offset(u, c)) * 2328c2ecf20Sopenharmony_ci sizeof(evtchn_port_t); 2338c2ecf20Sopenharmony_ci bytes2 = evtchn_ring_offset(u, p) * sizeof(evtchn_port_t); 2348c2ecf20Sopenharmony_ci } else { 2358c2ecf20Sopenharmony_ci bytes1 = (p - c) * sizeof(evtchn_port_t); 2368c2ecf20Sopenharmony_ci bytes2 = 0; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Truncate chunks according to caller's maximum byte count. */ 2408c2ecf20Sopenharmony_ci if (bytes1 > count) { 2418c2ecf20Sopenharmony_ci bytes1 = count; 2428c2ecf20Sopenharmony_ci bytes2 = 0; 2438c2ecf20Sopenharmony_ci } else if ((bytes1 + bytes2) > count) { 2448c2ecf20Sopenharmony_ci bytes2 = count - bytes1; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci rc = -EFAULT; 2488c2ecf20Sopenharmony_ci rmb(); /* Ensure that we see the port before we copy it. */ 2498c2ecf20Sopenharmony_ci if (copy_to_user(buf, evtchn_ring_entry(u, c), bytes1) || 2508c2ecf20Sopenharmony_ci ((bytes2 != 0) && 2518c2ecf20Sopenharmony_ci copy_to_user(&buf[bytes1], &u->ring[0], bytes2))) 2528c2ecf20Sopenharmony_ci goto unlock_out; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci u->ring_cons += (bytes1 + bytes2) / sizeof(evtchn_port_t); 2558c2ecf20Sopenharmony_ci rc = bytes1 + bytes2; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci unlock_out: 2588c2ecf20Sopenharmony_ci mutex_unlock(&u->ring_cons_mutex); 2598c2ecf20Sopenharmony_ci return rc; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic ssize_t evtchn_write(struct file *file, const char __user *buf, 2638c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci int rc, i; 2668c2ecf20Sopenharmony_ci evtchn_port_t *kbuf = (evtchn_port_t *)__get_free_page(GFP_KERNEL); 2678c2ecf20Sopenharmony_ci struct per_user_data *u = file->private_data; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (kbuf == NULL) 2708c2ecf20Sopenharmony_ci return -ENOMEM; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* Whole number of ports. */ 2738c2ecf20Sopenharmony_ci count &= ~(sizeof(evtchn_port_t)-1); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci rc = 0; 2768c2ecf20Sopenharmony_ci if (count == 0) 2778c2ecf20Sopenharmony_ci goto out; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (count > PAGE_SIZE) 2808c2ecf20Sopenharmony_ci count = PAGE_SIZE; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci rc = -EFAULT; 2838c2ecf20Sopenharmony_ci if (copy_from_user(kbuf, buf, count) != 0) 2848c2ecf20Sopenharmony_ci goto out; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci mutex_lock(&u->bind_mutex); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci for (i = 0; i < (count/sizeof(evtchn_port_t)); i++) { 2898c2ecf20Sopenharmony_ci evtchn_port_t port = kbuf[i]; 2908c2ecf20Sopenharmony_ci struct user_evtchn *evtchn; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci evtchn = find_evtchn(u, port); 2938c2ecf20Sopenharmony_ci if (evtchn && !evtchn->enabled) { 2948c2ecf20Sopenharmony_ci evtchn->enabled = true; 2958c2ecf20Sopenharmony_ci xen_irq_lateeoi(irq_from_evtchn(port), 0); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci mutex_unlock(&u->bind_mutex); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rc = count; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci out: 3048c2ecf20Sopenharmony_ci free_page((unsigned long)kbuf); 3058c2ecf20Sopenharmony_ci return rc; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int evtchn_resize_ring(struct per_user_data *u) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci unsigned int new_size; 3118c2ecf20Sopenharmony_ci evtchn_port_t *new_ring, *old_ring; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * Ensure the ring is large enough to capture all possible 3158c2ecf20Sopenharmony_ci * events. i.e., one free slot for each bound event. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci if (u->nr_evtchns <= u->ring_size) 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (u->ring_size == 0) 3218c2ecf20Sopenharmony_ci new_size = 64; 3228c2ecf20Sopenharmony_ci else 3238c2ecf20Sopenharmony_ci new_size = 2 * u->ring_size; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci new_ring = kvmalloc_array(new_size, sizeof(*new_ring), GFP_KERNEL); 3268c2ecf20Sopenharmony_ci if (!new_ring) 3278c2ecf20Sopenharmony_ci return -ENOMEM; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci old_ring = u->ring; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * Access to the ring contents is serialized by either the 3338c2ecf20Sopenharmony_ci * prod /or/ cons lock so take both when resizing. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci mutex_lock(&u->ring_cons_mutex); 3368c2ecf20Sopenharmony_ci spin_lock_irq(&u->ring_prod_lock); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * Copy the old ring contents to the new ring. 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * To take care of wrapping, a full ring, and the new index 3428c2ecf20Sopenharmony_ci * pointing into the second half, simply copy the old contents 3438c2ecf20Sopenharmony_ci * twice. 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * +---------+ +------------------+ 3468c2ecf20Sopenharmony_ci * |34567 12| -> |34567 1234567 12| 3478c2ecf20Sopenharmony_ci * +-----p-c-+ +-------c------p---+ 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci memcpy(new_ring, old_ring, u->ring_size * sizeof(*u->ring)); 3508c2ecf20Sopenharmony_ci memcpy(new_ring + u->ring_size, old_ring, 3518c2ecf20Sopenharmony_ci u->ring_size * sizeof(*u->ring)); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci u->ring = new_ring; 3548c2ecf20Sopenharmony_ci u->ring_size = new_size; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci spin_unlock_irq(&u->ring_prod_lock); 3578c2ecf20Sopenharmony_ci mutex_unlock(&u->ring_cons_mutex); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci evtchn_free_ring(old_ring); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int evtchn_bind_to_user(struct per_user_data *u, evtchn_port_t port) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct user_evtchn *evtchn; 3678c2ecf20Sopenharmony_ci struct evtchn_close close; 3688c2ecf20Sopenharmony_ci int rc = 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* 3718c2ecf20Sopenharmony_ci * Ports are never reused, so every caller should pass in a 3728c2ecf20Sopenharmony_ci * unique port. 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * (Locking not necessary because we haven't registered the 3758c2ecf20Sopenharmony_ci * interrupt handler yet, and our caller has already 3768c2ecf20Sopenharmony_ci * serialized bind operations.) 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci evtchn = kzalloc(sizeof(*evtchn), GFP_KERNEL); 3808c2ecf20Sopenharmony_ci if (!evtchn) 3818c2ecf20Sopenharmony_ci return -ENOMEM; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci evtchn->user = u; 3848c2ecf20Sopenharmony_ci evtchn->port = port; 3858c2ecf20Sopenharmony_ci evtchn->enabled = true; /* start enabled */ 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci rc = add_evtchn(u, evtchn); 3888c2ecf20Sopenharmony_ci if (rc < 0) 3898c2ecf20Sopenharmony_ci goto err; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci rc = evtchn_resize_ring(u); 3928c2ecf20Sopenharmony_ci if (rc < 0) 3938c2ecf20Sopenharmony_ci goto err; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci rc = bind_evtchn_to_irqhandler_lateeoi(port, evtchn_interrupt, 0, 3968c2ecf20Sopenharmony_ci u->name, evtchn); 3978c2ecf20Sopenharmony_ci if (rc < 0) 3988c2ecf20Sopenharmony_ci goto err; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci rc = evtchn_make_refcounted(port); 4018c2ecf20Sopenharmony_ci return rc; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cierr: 4048c2ecf20Sopenharmony_ci /* bind failed, should close the port now */ 4058c2ecf20Sopenharmony_ci close.port = port; 4068c2ecf20Sopenharmony_ci if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) 4078c2ecf20Sopenharmony_ci BUG(); 4088c2ecf20Sopenharmony_ci del_evtchn(u, evtchn); 4098c2ecf20Sopenharmony_ci return rc; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void evtchn_unbind_from_user(struct per_user_data *u, 4138c2ecf20Sopenharmony_ci struct user_evtchn *evtchn) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci int irq = irq_from_evtchn(evtchn->port); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci BUG_ON(irq < 0); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci unbind_from_irqhandler(irq, evtchn); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci del_evtchn(u, evtchn); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(int, bind_last_selected_cpu); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void evtchn_bind_interdom_next_vcpu(evtchn_port_t evtchn) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci unsigned int selected_cpu, irq; 4298c2ecf20Sopenharmony_ci struct irq_desc *desc; 4308c2ecf20Sopenharmony_ci unsigned long flags; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci irq = irq_from_evtchn(evtchn); 4338c2ecf20Sopenharmony_ci desc = irq_to_desc(irq); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (!desc) 4368c2ecf20Sopenharmony_ci return; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&desc->lock, flags); 4398c2ecf20Sopenharmony_ci selected_cpu = this_cpu_read(bind_last_selected_cpu); 4408c2ecf20Sopenharmony_ci selected_cpu = cpumask_next_and(selected_cpu, 4418c2ecf20Sopenharmony_ci desc->irq_common_data.affinity, cpu_online_mask); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (unlikely(selected_cpu >= nr_cpu_ids)) 4448c2ecf20Sopenharmony_ci selected_cpu = cpumask_first_and(desc->irq_common_data.affinity, 4458c2ecf20Sopenharmony_ci cpu_online_mask); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci this_cpu_write(bind_last_selected_cpu, selected_cpu); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* unmask expects irqs to be disabled */ 4508c2ecf20Sopenharmony_ci xen_set_affinity_evtchn(desc, selected_cpu); 4518c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&desc->lock, flags); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic long evtchn_ioctl(struct file *file, 4558c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci int rc; 4588c2ecf20Sopenharmony_ci struct per_user_data *u = file->private_data; 4598c2ecf20Sopenharmony_ci void __user *uarg = (void __user *) arg; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* Prevent bind from racing with unbind */ 4628c2ecf20Sopenharmony_ci mutex_lock(&u->bind_mutex); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci switch (cmd) { 4658c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_BIND_VIRQ: { 4668c2ecf20Sopenharmony_ci struct ioctl_evtchn_bind_virq bind; 4678c2ecf20Sopenharmony_ci struct evtchn_bind_virq bind_virq; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci rc = -EACCES; 4708c2ecf20Sopenharmony_ci if (u->restrict_domid != UNRESTRICTED_DOMID) 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci rc = -EFAULT; 4748c2ecf20Sopenharmony_ci if (copy_from_user(&bind, uarg, sizeof(bind))) 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci bind_virq.virq = bind.virq; 4788c2ecf20Sopenharmony_ci bind_virq.vcpu = xen_vcpu_nr(0); 4798c2ecf20Sopenharmony_ci rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, 4808c2ecf20Sopenharmony_ci &bind_virq); 4818c2ecf20Sopenharmony_ci if (rc != 0) 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci rc = evtchn_bind_to_user(u, bind_virq.port); 4858c2ecf20Sopenharmony_ci if (rc == 0) 4868c2ecf20Sopenharmony_ci rc = bind_virq.port; 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_BIND_INTERDOMAIN: { 4918c2ecf20Sopenharmony_ci struct ioctl_evtchn_bind_interdomain bind; 4928c2ecf20Sopenharmony_ci struct evtchn_bind_interdomain bind_interdomain; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci rc = -EFAULT; 4958c2ecf20Sopenharmony_ci if (copy_from_user(&bind, uarg, sizeof(bind))) 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci rc = -EACCES; 4998c2ecf20Sopenharmony_ci if (u->restrict_domid != UNRESTRICTED_DOMID && 5008c2ecf20Sopenharmony_ci u->restrict_domid != bind.remote_domain) 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci bind_interdomain.remote_dom = bind.remote_domain; 5048c2ecf20Sopenharmony_ci bind_interdomain.remote_port = bind.remote_port; 5058c2ecf20Sopenharmony_ci rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, 5068c2ecf20Sopenharmony_ci &bind_interdomain); 5078c2ecf20Sopenharmony_ci if (rc != 0) 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci rc = evtchn_bind_to_user(u, bind_interdomain.local_port); 5118c2ecf20Sopenharmony_ci if (rc == 0) { 5128c2ecf20Sopenharmony_ci rc = bind_interdomain.local_port; 5138c2ecf20Sopenharmony_ci evtchn_bind_interdom_next_vcpu(rc); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_BIND_UNBOUND_PORT: { 5198c2ecf20Sopenharmony_ci struct ioctl_evtchn_bind_unbound_port bind; 5208c2ecf20Sopenharmony_ci struct evtchn_alloc_unbound alloc_unbound; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci rc = -EACCES; 5238c2ecf20Sopenharmony_ci if (u->restrict_domid != UNRESTRICTED_DOMID) 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci rc = -EFAULT; 5278c2ecf20Sopenharmony_ci if (copy_from_user(&bind, uarg, sizeof(bind))) 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci alloc_unbound.dom = DOMID_SELF; 5318c2ecf20Sopenharmony_ci alloc_unbound.remote_dom = bind.remote_domain; 5328c2ecf20Sopenharmony_ci rc = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, 5338c2ecf20Sopenharmony_ci &alloc_unbound); 5348c2ecf20Sopenharmony_ci if (rc != 0) 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci rc = evtchn_bind_to_user(u, alloc_unbound.port); 5388c2ecf20Sopenharmony_ci if (rc == 0) 5398c2ecf20Sopenharmony_ci rc = alloc_unbound.port; 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_UNBIND: { 5448c2ecf20Sopenharmony_ci struct ioctl_evtchn_unbind unbind; 5458c2ecf20Sopenharmony_ci struct user_evtchn *evtchn; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci rc = -EFAULT; 5488c2ecf20Sopenharmony_ci if (copy_from_user(&unbind, uarg, sizeof(unbind))) 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci rc = -EINVAL; 5528c2ecf20Sopenharmony_ci if (unbind.port >= xen_evtchn_nr_channels()) 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rc = -ENOTCONN; 5568c2ecf20Sopenharmony_ci evtchn = find_evtchn(u, unbind.port); 5578c2ecf20Sopenharmony_ci if (!evtchn) 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci disable_irq(irq_from_evtchn(unbind.port)); 5618c2ecf20Sopenharmony_ci evtchn_unbind_from_user(u, evtchn); 5628c2ecf20Sopenharmony_ci rc = 0; 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_NOTIFY: { 5678c2ecf20Sopenharmony_ci struct ioctl_evtchn_notify notify; 5688c2ecf20Sopenharmony_ci struct user_evtchn *evtchn; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci rc = -EFAULT; 5718c2ecf20Sopenharmony_ci if (copy_from_user(¬ify, uarg, sizeof(notify))) 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci rc = -ENOTCONN; 5758c2ecf20Sopenharmony_ci evtchn = find_evtchn(u, notify.port); 5768c2ecf20Sopenharmony_ci if (evtchn) { 5778c2ecf20Sopenharmony_ci notify_remote_via_evtchn(notify.port); 5788c2ecf20Sopenharmony_ci rc = 0; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_RESET: { 5848c2ecf20Sopenharmony_ci /* Initialise the ring to empty. Clear errors. */ 5858c2ecf20Sopenharmony_ci mutex_lock(&u->ring_cons_mutex); 5868c2ecf20Sopenharmony_ci spin_lock_irq(&u->ring_prod_lock); 5878c2ecf20Sopenharmony_ci u->ring_cons = u->ring_prod = u->ring_overflow = 0; 5888c2ecf20Sopenharmony_ci spin_unlock_irq(&u->ring_prod_lock); 5898c2ecf20Sopenharmony_ci mutex_unlock(&u->ring_cons_mutex); 5908c2ecf20Sopenharmony_ci rc = 0; 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci case IOCTL_EVTCHN_RESTRICT_DOMID: { 5958c2ecf20Sopenharmony_ci struct ioctl_evtchn_restrict_domid ierd; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci rc = -EACCES; 5988c2ecf20Sopenharmony_ci if (u->restrict_domid != UNRESTRICTED_DOMID) 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci rc = -EFAULT; 6028c2ecf20Sopenharmony_ci if (copy_from_user(&ierd, uarg, sizeof(ierd))) 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci rc = -EINVAL; 6068c2ecf20Sopenharmony_ci if (ierd.domid == 0 || ierd.domid >= DOMID_FIRST_RESERVED) 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci u->restrict_domid = ierd.domid; 6108c2ecf20Sopenharmony_ci rc = 0; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci default: 6168c2ecf20Sopenharmony_ci rc = -ENOSYS; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci mutex_unlock(&u->bind_mutex); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return rc; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic __poll_t evtchn_poll(struct file *file, poll_table *wait) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci __poll_t mask = EPOLLOUT | EPOLLWRNORM; 6278c2ecf20Sopenharmony_ci struct per_user_data *u = file->private_data; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci poll_wait(file, &u->evtchn_wait, wait); 6308c2ecf20Sopenharmony_ci if (u->ring_cons != u->ring_prod) 6318c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 6328c2ecf20Sopenharmony_ci if (u->ring_overflow) 6338c2ecf20Sopenharmony_ci mask = EPOLLERR; 6348c2ecf20Sopenharmony_ci return mask; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic int evtchn_fasync(int fd, struct file *filp, int on) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct per_user_data *u = filp->private_data; 6408c2ecf20Sopenharmony_ci return fasync_helper(fd, filp, on, &u->evtchn_async_queue); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int evtchn_open(struct inode *inode, struct file *filp) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct per_user_data *u; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci u = kzalloc(sizeof(*u), GFP_KERNEL); 6488c2ecf20Sopenharmony_ci if (u == NULL) 6498c2ecf20Sopenharmony_ci return -ENOMEM; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci u->name = kasprintf(GFP_KERNEL, "evtchn:%s", current->comm); 6528c2ecf20Sopenharmony_ci if (u->name == NULL) { 6538c2ecf20Sopenharmony_ci kfree(u); 6548c2ecf20Sopenharmony_ci return -ENOMEM; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci init_waitqueue_head(&u->evtchn_wait); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci mutex_init(&u->bind_mutex); 6608c2ecf20Sopenharmony_ci mutex_init(&u->ring_cons_mutex); 6618c2ecf20Sopenharmony_ci spin_lock_init(&u->ring_prod_lock); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci u->restrict_domid = UNRESTRICTED_DOMID; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci filp->private_data = u; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return stream_open(inode, filp); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int evtchn_release(struct inode *inode, struct file *filp) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct per_user_data *u = filp->private_data; 6738c2ecf20Sopenharmony_ci struct rb_node *node; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci while ((node = u->evtchns.rb_node)) { 6768c2ecf20Sopenharmony_ci struct user_evtchn *evtchn; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci evtchn = rb_entry(node, struct user_evtchn, node); 6798c2ecf20Sopenharmony_ci disable_irq(irq_from_evtchn(evtchn->port)); 6808c2ecf20Sopenharmony_ci evtchn_unbind_from_user(u, evtchn); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci evtchn_free_ring(u->ring); 6848c2ecf20Sopenharmony_ci kfree(u->name); 6858c2ecf20Sopenharmony_ci kfree(u); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic const struct file_operations evtchn_fops = { 6918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6928c2ecf20Sopenharmony_ci .read = evtchn_read, 6938c2ecf20Sopenharmony_ci .write = evtchn_write, 6948c2ecf20Sopenharmony_ci .unlocked_ioctl = evtchn_ioctl, 6958c2ecf20Sopenharmony_ci .poll = evtchn_poll, 6968c2ecf20Sopenharmony_ci .fasync = evtchn_fasync, 6978c2ecf20Sopenharmony_ci .open = evtchn_open, 6988c2ecf20Sopenharmony_ci .release = evtchn_release, 6998c2ecf20Sopenharmony_ci .llseek = no_llseek, 7008c2ecf20Sopenharmony_ci}; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic struct miscdevice evtchn_miscdev = { 7038c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 7048c2ecf20Sopenharmony_ci .name = "xen/evtchn", 7058c2ecf20Sopenharmony_ci .fops = &evtchn_fops, 7068c2ecf20Sopenharmony_ci}; 7078c2ecf20Sopenharmony_cistatic int __init evtchn_init(void) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci int err; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (!xen_domain()) 7128c2ecf20Sopenharmony_ci return -ENODEV; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Create '/dev/xen/evtchn'. */ 7158c2ecf20Sopenharmony_ci err = misc_register(&evtchn_miscdev); 7168c2ecf20Sopenharmony_ci if (err != 0) { 7178c2ecf20Sopenharmony_ci pr_err("Could not register /dev/xen/evtchn\n"); 7188c2ecf20Sopenharmony_ci return err; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci pr_info("Event-channel device installed\n"); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic void __exit evtchn_cleanup(void) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci misc_deregister(&evtchn_miscdev); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cimodule_init(evtchn_init); 7328c2ecf20Sopenharmony_cimodule_exit(evtchn_cleanup); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 735