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