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(&notify, 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