xref: /openbmc/linux/drivers/xen/events/events_fifo.c (revision c37fe6aff89cb0d842993fe2f69e48bf3ebe0ab0)
11fe56551SDavid Vrabel /*
21fe56551SDavid Vrabel  * Xen event channels (FIFO-based ABI)
31fe56551SDavid Vrabel  *
41fe56551SDavid Vrabel  * Copyright (C) 2013 Citrix Systems R&D ltd.
51fe56551SDavid Vrabel  *
61fe56551SDavid Vrabel  * This source code is free software; you can redistribute it and/or
71fe56551SDavid Vrabel  * modify it under the terms of the GNU General Public License as
81fe56551SDavid Vrabel  * published by the Free Software Foundation; either version 2 of the
91fe56551SDavid Vrabel  * License, or (at your option) any later version.
101fe56551SDavid Vrabel  *
111fe56551SDavid Vrabel  * Or, when distributed separately from the Linux kernel or
121fe56551SDavid Vrabel  * incorporated into other software packages, subject to the following
131fe56551SDavid Vrabel  * license:
141fe56551SDavid Vrabel  *
151fe56551SDavid Vrabel  * Permission is hereby granted, free of charge, to any person obtaining a copy
161fe56551SDavid Vrabel  * of this source file (the "Software"), to deal in the Software without
171fe56551SDavid Vrabel  * restriction, including without limitation the rights to use, copy, modify,
181fe56551SDavid Vrabel  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
191fe56551SDavid Vrabel  * and to permit persons to whom the Software is furnished to do so, subject to
201fe56551SDavid Vrabel  * the following conditions:
211fe56551SDavid Vrabel  *
221fe56551SDavid Vrabel  * The above copyright notice and this permission notice shall be included in
231fe56551SDavid Vrabel  * all copies or substantial portions of the Software.
241fe56551SDavid Vrabel  *
251fe56551SDavid Vrabel  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
261fe56551SDavid Vrabel  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
271fe56551SDavid Vrabel  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
281fe56551SDavid Vrabel  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
291fe56551SDavid Vrabel  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
301fe56551SDavid Vrabel  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
311fe56551SDavid Vrabel  * IN THE SOFTWARE.
321fe56551SDavid Vrabel  */
331fe56551SDavid Vrabel 
341fe56551SDavid Vrabel #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
351fe56551SDavid Vrabel 
361fe56551SDavid Vrabel #include <linux/linkage.h>
371fe56551SDavid Vrabel #include <linux/interrupt.h>
381fe56551SDavid Vrabel #include <linux/irq.h>
391fe56551SDavid Vrabel #include <linux/smp.h>
401fe56551SDavid Vrabel #include <linux/percpu.h>
411fe56551SDavid Vrabel #include <linux/cpu.h>
421fe56551SDavid Vrabel 
4323492754SMichael S. Tsirkin #include <asm/barrier.h>
441fe56551SDavid Vrabel #include <asm/sync_bitops.h>
451fe56551SDavid Vrabel #include <asm/xen/hypercall.h>
461fe56551SDavid Vrabel #include <asm/xen/hypervisor.h>
471fe56551SDavid Vrabel 
481fe56551SDavid Vrabel #include <xen/xen.h>
491fe56551SDavid Vrabel #include <xen/xen-ops.h>
501fe56551SDavid Vrabel #include <xen/events.h>
511fe56551SDavid Vrabel #include <xen/interface/xen.h>
521fe56551SDavid Vrabel #include <xen/interface/event_channel.h>
53a9fd60e2SJulien Grall #include <xen/page.h>
541fe56551SDavid Vrabel 
551fe56551SDavid Vrabel #include "events_internal.h"
561fe56551SDavid Vrabel 
57a001c9d9SJulien Grall #define EVENT_WORDS_PER_PAGE (XEN_PAGE_SIZE / sizeof(event_word_t))
581fe56551SDavid Vrabel #define MAX_EVENT_ARRAY_PAGES (EVTCHN_FIFO_NR_CHANNELS / EVENT_WORDS_PER_PAGE)
591fe56551SDavid Vrabel 
601fe56551SDavid Vrabel struct evtchn_fifo_queue {
611fe56551SDavid Vrabel 	uint32_t head[EVTCHN_FIFO_MAX_QUEUES];
621fe56551SDavid Vrabel };
631fe56551SDavid Vrabel 
641fe56551SDavid Vrabel static DEFINE_PER_CPU(struct evtchn_fifo_control_block *, cpu_control_block);
651fe56551SDavid Vrabel static DEFINE_PER_CPU(struct evtchn_fifo_queue, cpu_queue);
661fe56551SDavid Vrabel static event_word_t *event_array[MAX_EVENT_ARRAY_PAGES] __read_mostly;
671fe56551SDavid Vrabel static unsigned event_array_pages __read_mostly;
681fe56551SDavid Vrabel 
6905a812acSVladimir Murzin /*
70dcecb8fdSDavid Vrabel  * sync_set_bit() and friends must be unsigned long aligned.
7105a812acSVladimir Murzin  */
72dcecb8fdSDavid Vrabel #if BITS_PER_LONG > 32
7305a812acSVladimir Murzin 
7405a812acSVladimir Murzin #define BM(w) (unsigned long *)((unsigned long)w & ~0x7UL)
7505a812acSVladimir Murzin #define EVTCHN_FIFO_BIT(b, w) \
7605a812acSVladimir Murzin     (((unsigned long)w & 0x4UL) ? (EVTCHN_FIFO_ ##b + 32) : EVTCHN_FIFO_ ##b)
7705a812acSVladimir Murzin 
7805a812acSVladimir Murzin #else
7905a812acSVladimir Murzin 
801fe56551SDavid Vrabel #define BM(w) ((unsigned long *)(w))
8105a812acSVladimir Murzin #define EVTCHN_FIFO_BIT(b, w) EVTCHN_FIFO_ ##b
8205a812acSVladimir Murzin 
8305a812acSVladimir Murzin #endif
841fe56551SDavid Vrabel 
event_word_from_port(evtchn_port_t port)850102e4efSYan Yankovskyi static inline event_word_t *event_word_from_port(evtchn_port_t port)
861fe56551SDavid Vrabel {
871fe56551SDavid Vrabel 	unsigned i = port / EVENT_WORDS_PER_PAGE;
881fe56551SDavid Vrabel 
891fe56551SDavid Vrabel 	return event_array[i] + port % EVENT_WORDS_PER_PAGE;
901fe56551SDavid Vrabel }
911fe56551SDavid Vrabel 
evtchn_fifo_max_channels(void)921fe56551SDavid Vrabel static unsigned evtchn_fifo_max_channels(void)
931fe56551SDavid Vrabel {
941fe56551SDavid Vrabel 	return EVTCHN_FIFO_NR_CHANNELS;
951fe56551SDavid Vrabel }
961fe56551SDavid Vrabel 
evtchn_fifo_nr_channels(void)971fe56551SDavid Vrabel static unsigned evtchn_fifo_nr_channels(void)
981fe56551SDavid Vrabel {
991fe56551SDavid Vrabel 	return event_array_pages * EVENT_WORDS_PER_PAGE;
1001fe56551SDavid Vrabel }
1011fe56551SDavid Vrabel 
init_control_block(int cpu,struct evtchn_fifo_control_block * control_block)102c12784c3SDavid Vrabel static int init_control_block(int cpu,
103c12784c3SDavid Vrabel                               struct evtchn_fifo_control_block *control_block)
104c12784c3SDavid Vrabel {
105c12784c3SDavid Vrabel 	struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
106c12784c3SDavid Vrabel 	struct evtchn_init_control init_control;
107c12784c3SDavid Vrabel 	unsigned int i;
108c12784c3SDavid Vrabel 
109c12784c3SDavid Vrabel 	/* Reset the control block and the local HEADs. */
110c12784c3SDavid Vrabel 	clear_page(control_block);
111c12784c3SDavid Vrabel 	for (i = 0; i < EVTCHN_FIFO_MAX_QUEUES; i++)
112c12784c3SDavid Vrabel 		q->head[i] = 0;
113c12784c3SDavid Vrabel 
1140df4f266SJulien Grall 	init_control.control_gfn = virt_to_gfn(control_block);
115c12784c3SDavid Vrabel 	init_control.offset      = 0;
116be78da1cSVitaly Kuznetsov 	init_control.vcpu        = xen_vcpu_nr(cpu);
117c12784c3SDavid Vrabel 
118c12784c3SDavid Vrabel 	return HYPERVISOR_event_channel_op(EVTCHNOP_init_control, &init_control);
119c12784c3SDavid Vrabel }
120c12784c3SDavid Vrabel 
free_unused_array_pages(void)1211fe56551SDavid Vrabel static void free_unused_array_pages(void)
1221fe56551SDavid Vrabel {
1231fe56551SDavid Vrabel 	unsigned i;
1241fe56551SDavid Vrabel 
1251fe56551SDavid Vrabel 	for (i = event_array_pages; i < MAX_EVENT_ARRAY_PAGES; i++) {
1261fe56551SDavid Vrabel 		if (!event_array[i])
1271fe56551SDavid Vrabel 			break;
1281fe56551SDavid Vrabel 		free_page((unsigned long)event_array[i]);
1291fe56551SDavid Vrabel 		event_array[i] = NULL;
1301fe56551SDavid Vrabel 	}
1311fe56551SDavid Vrabel }
1321fe56551SDavid Vrabel 
init_array_page(event_word_t * array_page)1331fe56551SDavid Vrabel static void init_array_page(event_word_t *array_page)
1341fe56551SDavid Vrabel {
1351fe56551SDavid Vrabel 	unsigned i;
1361fe56551SDavid Vrabel 
1371fe56551SDavid Vrabel 	for (i = 0; i < EVENT_WORDS_PER_PAGE; i++)
1381fe56551SDavid Vrabel 		array_page[i] = 1 << EVTCHN_FIFO_MASKED;
1391fe56551SDavid Vrabel }
1401fe56551SDavid Vrabel 
evtchn_fifo_setup(evtchn_port_t port)1417e14cde1SJuergen Gross static int evtchn_fifo_setup(evtchn_port_t port)
1421fe56551SDavid Vrabel {
1431fe56551SDavid Vrabel 	unsigned new_array_pages;
144be1403b9SWei Yongjun 	int ret;
1451fe56551SDavid Vrabel 
1461fe56551SDavid Vrabel 	new_array_pages = port / EVENT_WORDS_PER_PAGE + 1;
1471fe56551SDavid Vrabel 
1481fe56551SDavid Vrabel 	if (new_array_pages > MAX_EVENT_ARRAY_PAGES)
1491fe56551SDavid Vrabel 		return -EINVAL;
1501fe56551SDavid Vrabel 
1511fe56551SDavid Vrabel 	while (event_array_pages < new_array_pages) {
1521fe56551SDavid Vrabel 		void *array_page;
1531fe56551SDavid Vrabel 		struct evtchn_expand_array expand_array;
1541fe56551SDavid Vrabel 
1551fe56551SDavid Vrabel 		/* Might already have a page if we've resumed. */
1561fe56551SDavid Vrabel 		array_page = event_array[event_array_pages];
1571fe56551SDavid Vrabel 		if (!array_page) {
1581fe56551SDavid Vrabel 			array_page = (void *)__get_free_page(GFP_KERNEL);
159be1403b9SWei Yongjun 			if (array_page == NULL) {
160be1403b9SWei Yongjun 				ret = -ENOMEM;
1611fe56551SDavid Vrabel 				goto error;
162be1403b9SWei Yongjun 			}
1631fe56551SDavid Vrabel 			event_array[event_array_pages] = array_page;
1641fe56551SDavid Vrabel 		}
1651fe56551SDavid Vrabel 
1661fe56551SDavid Vrabel 		/* Mask all events in this page before adding it. */
1671fe56551SDavid Vrabel 		init_array_page(array_page);
1681fe56551SDavid Vrabel 
1690df4f266SJulien Grall 		expand_array.array_gfn = virt_to_gfn(array_page);
1701fe56551SDavid Vrabel 
1711fe56551SDavid Vrabel 		ret = HYPERVISOR_event_channel_op(EVTCHNOP_expand_array, &expand_array);
1721fe56551SDavid Vrabel 		if (ret < 0)
1731fe56551SDavid Vrabel 			goto error;
1741fe56551SDavid Vrabel 
1751fe56551SDavid Vrabel 		event_array_pages++;
1761fe56551SDavid Vrabel 	}
1771fe56551SDavid Vrabel 	return 0;
1781fe56551SDavid Vrabel 
1791fe56551SDavid Vrabel   error:
1801fe56551SDavid Vrabel 	if (event_array_pages == 0)
1811fe56551SDavid Vrabel 		panic("xen: unable to expand event array with initial page (%d)\n", ret);
1821fe56551SDavid Vrabel 	else
1831fe56551SDavid Vrabel 		pr_err("unable to expand event array (%d)\n", ret);
1841fe56551SDavid Vrabel 	free_unused_array_pages();
1851fe56551SDavid Vrabel 	return ret;
1861fe56551SDavid Vrabel }
1871fe56551SDavid Vrabel 
evtchn_fifo_bind_to_cpu(evtchn_port_t evtchn,unsigned int cpu,unsigned int old_cpu)1887e14cde1SJuergen Gross static void evtchn_fifo_bind_to_cpu(evtchn_port_t evtchn, unsigned int cpu,
1897e14cde1SJuergen Gross 				    unsigned int old_cpu)
1901fe56551SDavid Vrabel {
1911fe56551SDavid Vrabel 	/* no-op */
1921fe56551SDavid Vrabel }
1931fe56551SDavid Vrabel 
evtchn_fifo_clear_pending(evtchn_port_t port)1940102e4efSYan Yankovskyi static void evtchn_fifo_clear_pending(evtchn_port_t port)
1951fe56551SDavid Vrabel {
1961fe56551SDavid Vrabel 	event_word_t *word = event_word_from_port(port);
19705a812acSVladimir Murzin 	sync_clear_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
1981fe56551SDavid Vrabel }
1991fe56551SDavid Vrabel 
evtchn_fifo_set_pending(evtchn_port_t port)2000102e4efSYan Yankovskyi static void evtchn_fifo_set_pending(evtchn_port_t port)
2011fe56551SDavid Vrabel {
2021fe56551SDavid Vrabel 	event_word_t *word = event_word_from_port(port);
20305a812acSVladimir Murzin 	sync_set_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
2041fe56551SDavid Vrabel }
2051fe56551SDavid Vrabel 
evtchn_fifo_is_pending(evtchn_port_t port)2060102e4efSYan Yankovskyi static bool evtchn_fifo_is_pending(evtchn_port_t port)
2071fe56551SDavid Vrabel {
2081fe56551SDavid Vrabel 	event_word_t *word = event_word_from_port(port);
20905a812acSVladimir Murzin 	return sync_test_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
2101fe56551SDavid Vrabel }
2111fe56551SDavid Vrabel 
evtchn_fifo_mask(evtchn_port_t port)2120102e4efSYan Yankovskyi static void evtchn_fifo_mask(evtchn_port_t port)
2131fe56551SDavid Vrabel {
2141fe56551SDavid Vrabel 	event_word_t *word = event_word_from_port(port);
21505a812acSVladimir Murzin 	sync_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
2161fe56551SDavid Vrabel }
2171fe56551SDavid Vrabel 
evtchn_fifo_is_masked(evtchn_port_t port)2180102e4efSYan Yankovskyi static bool evtchn_fifo_is_masked(evtchn_port_t port)
21905a812acSVladimir Murzin {
22005a812acSVladimir Murzin 	event_word_t *word = event_word_from_port(port);
22105a812acSVladimir Murzin 	return sync_test_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
22205a812acSVladimir Murzin }
2231fe56551SDavid Vrabel /*
224f0133719SJuergen Gross  * Clear MASKED if not PENDING, spinning if BUSY is set.
225f0133719SJuergen Gross  * Return true if mask was cleared.
2261fe56551SDavid Vrabel  */
clear_masked_cond(volatile event_word_t * word)227f0133719SJuergen Gross static bool clear_masked_cond(volatile event_word_t *word)
2281fe56551SDavid Vrabel {
2291fe56551SDavid Vrabel 	event_word_t new, old, w;
2301fe56551SDavid Vrabel 
2311fe56551SDavid Vrabel 	w = *word;
2321fe56551SDavid Vrabel 
2331fe56551SDavid Vrabel 	do {
234*eabe7417SJuergen Gross 		if (!(w & (1 << EVTCHN_FIFO_MASKED)))
235*eabe7417SJuergen Gross 			return true;
236*eabe7417SJuergen Gross 
237f0133719SJuergen Gross 		if (w & (1 << EVTCHN_FIFO_PENDING))
238f0133719SJuergen Gross 			return false;
239f0133719SJuergen Gross 
2401fe56551SDavid Vrabel 		old = w & ~(1 << EVTCHN_FIFO_BUSY);
2411fe56551SDavid Vrabel 		new = old & ~(1 << EVTCHN_FIFO_MASKED);
2421fe56551SDavid Vrabel 		w = sync_cmpxchg(word, old, new);
2431fe56551SDavid Vrabel 	} while (w != old);
244f0133719SJuergen Gross 
245f0133719SJuergen Gross 	return true;
2461fe56551SDavid Vrabel }
2471fe56551SDavid Vrabel 
evtchn_fifo_unmask(evtchn_port_t port)2480102e4efSYan Yankovskyi static void evtchn_fifo_unmask(evtchn_port_t port)
2491fe56551SDavid Vrabel {
2501fe56551SDavid Vrabel 	event_word_t *word = event_word_from_port(port);
2511fe56551SDavid Vrabel 
2521fe56551SDavid Vrabel 	BUG_ON(!irqs_disabled());
2531fe56551SDavid Vrabel 
254f0133719SJuergen Gross 	if (!clear_masked_cond(word)) {
2551fe56551SDavid Vrabel 		struct evtchn_unmask unmask = { .port = port };
2561fe56551SDavid Vrabel 		(void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask);
2571fe56551SDavid Vrabel 	}
2581fe56551SDavid Vrabel }
2591fe56551SDavid Vrabel 
clear_linked(volatile event_word_t * word)2601fe56551SDavid Vrabel static uint32_t clear_linked(volatile event_word_t *word)
2611fe56551SDavid Vrabel {
2621fe56551SDavid Vrabel 	event_word_t new, old, w;
2631fe56551SDavid Vrabel 
2641fe56551SDavid Vrabel 	w = *word;
2651fe56551SDavid Vrabel 
2661fe56551SDavid Vrabel 	do {
2671fe56551SDavid Vrabel 		old = w;
2681fe56551SDavid Vrabel 		new = (w & ~((1 << EVTCHN_FIFO_LINKED)
2691fe56551SDavid Vrabel 			     | EVTCHN_FIFO_LINK_MASK));
2701fe56551SDavid Vrabel 	} while ((w = sync_cmpxchg(word, old, new)) != old);
2711fe56551SDavid Vrabel 
2721fe56551SDavid Vrabel 	return w & EVTCHN_FIFO_LINK_MASK;
2731fe56551SDavid Vrabel }
2741fe56551SDavid Vrabel 
consume_one_event(unsigned cpu,struct evtchn_loop_ctrl * ctrl,struct evtchn_fifo_control_block * control_block,unsigned priority,unsigned long * ready)275e99502f7SJuergen Gross static void consume_one_event(unsigned cpu, struct evtchn_loop_ctrl *ctrl,
2761fe56551SDavid Vrabel 			      struct evtchn_fifo_control_block *control_block,
277e99502f7SJuergen Gross 			      unsigned priority, unsigned long *ready)
2781fe56551SDavid Vrabel {
2791fe56551SDavid Vrabel 	struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
2801fe56551SDavid Vrabel 	uint32_t head;
2810102e4efSYan Yankovskyi 	evtchn_port_t port;
2821fe56551SDavid Vrabel 	event_word_t *word;
2831fe56551SDavid Vrabel 
2841fe56551SDavid Vrabel 	head = q->head[priority];
2851fe56551SDavid Vrabel 
2861fe56551SDavid Vrabel 	/*
2871fe56551SDavid Vrabel 	 * Reached the tail last time?  Read the new HEAD from the
2881fe56551SDavid Vrabel 	 * control block.
2891fe56551SDavid Vrabel 	 */
2901fe56551SDavid Vrabel 	if (head == 0) {
29123492754SMichael S. Tsirkin 		virt_rmb(); /* Ensure word is up-to-date before reading head. */
2921fe56551SDavid Vrabel 		head = control_block->head[priority];
2931fe56551SDavid Vrabel 	}
2941fe56551SDavid Vrabel 
2951fe56551SDavid Vrabel 	port = head;
2961fe56551SDavid Vrabel 	word = event_word_from_port(port);
2971fe56551SDavid Vrabel 	head = clear_linked(word);
2981fe56551SDavid Vrabel 
2991fe56551SDavid Vrabel 	/*
3001fe56551SDavid Vrabel 	 * If the link is non-zero, there are more events in the
3011fe56551SDavid Vrabel 	 * queue, otherwise the queue is empty.
3021fe56551SDavid Vrabel 	 *
3031fe56551SDavid Vrabel 	 * If the queue is empty, clear this priority from our local
3041fe56551SDavid Vrabel 	 * copy of the ready word.
3051fe56551SDavid Vrabel 	 */
3061fe56551SDavid Vrabel 	if (head == 0)
30705a812acSVladimir Murzin 		clear_bit(priority, ready);
3081fe56551SDavid Vrabel 
3093de88d62SRoss Lagerwall 	if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) {
310e99502f7SJuergen Gross 		if (unlikely(!ctrl))
3113de88d62SRoss Lagerwall 			pr_warn("Dropping pending event for port %u\n", port);
3123de88d62SRoss Lagerwall 		else
313e99502f7SJuergen Gross 			handle_irq_for_port(port, ctrl);
3143de88d62SRoss Lagerwall 	}
3151fe56551SDavid Vrabel 
3161fe56551SDavid Vrabel 	q->head[priority] = head;
3171fe56551SDavid Vrabel }
3181fe56551SDavid Vrabel 
__evtchn_fifo_handle_events(unsigned cpu,struct evtchn_loop_ctrl * ctrl)319e99502f7SJuergen Gross static void __evtchn_fifo_handle_events(unsigned cpu,
320e99502f7SJuergen Gross 					struct evtchn_loop_ctrl *ctrl)
3211fe56551SDavid Vrabel {
3221fe56551SDavid Vrabel 	struct evtchn_fifo_control_block *control_block;
32305a812acSVladimir Murzin 	unsigned long ready;
3241fe56551SDavid Vrabel 	unsigned q;
3251fe56551SDavid Vrabel 
3261fe56551SDavid Vrabel 	control_block = per_cpu(cpu_control_block, cpu);
3271fe56551SDavid Vrabel 
3281fe56551SDavid Vrabel 	ready = xchg(&control_block->ready, 0);
3291fe56551SDavid Vrabel 
3301fe56551SDavid Vrabel 	while (ready) {
331e4a74312SFrediano Ziglio 		q = find_first_bit(&ready, EVTCHN_FIFO_MAX_QUEUES);
332e99502f7SJuergen Gross 		consume_one_event(cpu, ctrl, control_block, q, &ready);
3331fe56551SDavid Vrabel 		ready |= xchg(&control_block->ready, 0);
3341fe56551SDavid Vrabel 	}
3351fe56551SDavid Vrabel }
3361fe56551SDavid Vrabel 
evtchn_fifo_handle_events(unsigned cpu,struct evtchn_loop_ctrl * ctrl)337e99502f7SJuergen Gross static void evtchn_fifo_handle_events(unsigned cpu,
338e99502f7SJuergen Gross 				      struct evtchn_loop_ctrl *ctrl)
3393de88d62SRoss Lagerwall {
340e99502f7SJuergen Gross 	__evtchn_fifo_handle_events(cpu, ctrl);
3413de88d62SRoss Lagerwall }
3423de88d62SRoss Lagerwall 
evtchn_fifo_resume(void)3431fe56551SDavid Vrabel static void evtchn_fifo_resume(void)
3441fe56551SDavid Vrabel {
3451fe56551SDavid Vrabel 	unsigned cpu;
3461fe56551SDavid Vrabel 
3471fe56551SDavid Vrabel 	for_each_possible_cpu(cpu) {
3481fe56551SDavid Vrabel 		void *control_block = per_cpu(cpu_control_block, cpu);
3491fe56551SDavid Vrabel 		int ret;
3501fe56551SDavid Vrabel 
3511fe56551SDavid Vrabel 		if (!control_block)
3521fe56551SDavid Vrabel 			continue;
3531fe56551SDavid Vrabel 
3541fe56551SDavid Vrabel 		/*
3551fe56551SDavid Vrabel 		 * If this CPU is offline, take the opportunity to
3561fe56551SDavid Vrabel 		 * free the control block while it is not being
3571fe56551SDavid Vrabel 		 * used.
3581fe56551SDavid Vrabel 		 */
3591fe56551SDavid Vrabel 		if (!cpu_online(cpu)) {
3601fe56551SDavid Vrabel 			free_page((unsigned long)control_block);
3611fe56551SDavid Vrabel 			per_cpu(cpu_control_block, cpu) = NULL;
3621fe56551SDavid Vrabel 			continue;
3631fe56551SDavid Vrabel 		}
3641fe56551SDavid Vrabel 
365c12784c3SDavid Vrabel 		ret = init_control_block(cpu, control_block);
366f9751a60SShyam Saini 		BUG_ON(ret < 0);
3671fe56551SDavid Vrabel 	}
3681fe56551SDavid Vrabel 
3691fe56551SDavid Vrabel 	/*
3701fe56551SDavid Vrabel 	 * The event array starts out as empty again and is extended
3711fe56551SDavid Vrabel 	 * as normal when events are bound.  The existing pages will
3721fe56551SDavid Vrabel 	 * be reused.
3731fe56551SDavid Vrabel 	 */
3741fe56551SDavid Vrabel 	event_array_pages = 0;
3751fe56551SDavid Vrabel }
3761fe56551SDavid Vrabel 
evtchn_fifo_alloc_control_block(unsigned cpu)377c12784c3SDavid Vrabel static int evtchn_fifo_alloc_control_block(unsigned cpu)
3781fe56551SDavid Vrabel {
379c12784c3SDavid Vrabel 	void *control_block = NULL;
3801fe56551SDavid Vrabel 	int ret = -ENOMEM;
3811fe56551SDavid Vrabel 
382c12784c3SDavid Vrabel 	control_block = (void *)__get_free_page(GFP_KERNEL);
3831fe56551SDavid Vrabel 	if (control_block == NULL)
3841fe56551SDavid Vrabel 		goto error;
3851fe56551SDavid Vrabel 
386c12784c3SDavid Vrabel 	ret = init_control_block(cpu, control_block);
3871fe56551SDavid Vrabel 	if (ret < 0)
3881fe56551SDavid Vrabel 		goto error;
3891fe56551SDavid Vrabel 
390c12784c3SDavid Vrabel 	per_cpu(cpu_control_block, cpu) = control_block;
3911fe56551SDavid Vrabel 
3921fe56551SDavid Vrabel 	return 0;
3931fe56551SDavid Vrabel 
3941fe56551SDavid Vrabel   error:
395c12784c3SDavid Vrabel 	free_page((unsigned long)control_block);
3961fe56551SDavid Vrabel 	return ret;
3971fe56551SDavid Vrabel }
3981fe56551SDavid Vrabel 
evtchn_fifo_percpu_init(unsigned int cpu)3997beb290cSJuergen Gross static int evtchn_fifo_percpu_init(unsigned int cpu)
4001fe56551SDavid Vrabel {
4011fe56551SDavid Vrabel 	if (!per_cpu(cpu_control_block, cpu))
402c8761e20SSebastian Andrzej Siewior 		return evtchn_fifo_alloc_control_block(cpu);
403c8761e20SSebastian Andrzej Siewior 	return 0;
4041fe56551SDavid Vrabel }
4051fe56551SDavid Vrabel 
evtchn_fifo_percpu_deinit(unsigned int cpu)4067beb290cSJuergen Gross static int evtchn_fifo_percpu_deinit(unsigned int cpu)
407c8761e20SSebastian Andrzej Siewior {
408e99502f7SJuergen Gross 	__evtchn_fifo_handle_events(cpu, NULL);
409c8761e20SSebastian Andrzej Siewior 	return 0;
410c8761e20SSebastian Andrzej Siewior }
4111fe56551SDavid Vrabel 
4127beb290cSJuergen Gross static const struct evtchn_ops evtchn_ops_fifo = {
4137beb290cSJuergen Gross 	.max_channels      = evtchn_fifo_max_channels,
4147beb290cSJuergen Gross 	.nr_channels       = evtchn_fifo_nr_channels,
4157beb290cSJuergen Gross 	.setup             = evtchn_fifo_setup,
4167beb290cSJuergen Gross 	.bind_to_cpu       = evtchn_fifo_bind_to_cpu,
4177beb290cSJuergen Gross 	.clear_pending     = evtchn_fifo_clear_pending,
4187beb290cSJuergen Gross 	.set_pending       = evtchn_fifo_set_pending,
4197beb290cSJuergen Gross 	.is_pending        = evtchn_fifo_is_pending,
4207beb290cSJuergen Gross 	.mask              = evtchn_fifo_mask,
4217beb290cSJuergen Gross 	.unmask            = evtchn_fifo_unmask,
4227beb290cSJuergen Gross 	.handle_events     = evtchn_fifo_handle_events,
4237beb290cSJuergen Gross 	.resume            = evtchn_fifo_resume,
4247beb290cSJuergen Gross 	.percpu_init       = evtchn_fifo_percpu_init,
4257beb290cSJuergen Gross 	.percpu_deinit     = evtchn_fifo_percpu_deinit,
4267beb290cSJuergen Gross };
4277beb290cSJuergen Gross 
xen_evtchn_fifo_init(void)4281fe56551SDavid Vrabel int __init xen_evtchn_fifo_init(void)
4291fe56551SDavid Vrabel {
43022f12f0dSJulien Grall 	int cpu = smp_processor_id();
4311fe56551SDavid Vrabel 	int ret;
4321fe56551SDavid Vrabel 
433c12784c3SDavid Vrabel 	ret = evtchn_fifo_alloc_control_block(cpu);
4341fe56551SDavid Vrabel 	if (ret < 0)
43522f12f0dSJulien Grall 		return ret;
4361fe56551SDavid Vrabel 
4371fe56551SDavid Vrabel 	pr_info("Using FIFO-based ABI\n");
4381fe56551SDavid Vrabel 
4391fe56551SDavid Vrabel 	evtchn_ops = &evtchn_ops_fifo;
4401fe56551SDavid Vrabel 
4411fe56551SDavid Vrabel 	return ret;
4421fe56551SDavid Vrabel }
443