aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/events/events_fifo.c
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@citrix.com>2013-03-15 09:02:35 -0400
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2014-01-06 10:07:57 -0500
commit1fe565517b57676884349dccfd6ce853ec338636 (patch)
treea3e71bb46092dc611a7164f039a5f0cc2de719aa /drivers/xen/events/events_fifo.c
parent8785c67663b6ba023c7c6d61d37d8e08c00d86a8 (diff)
xen/events: use the FIFO-based ABI if available
Implement all the event channel port ops for the FIFO-based ABI. If the hypervisor supports the FIFO-based ABI, enable it by initializing the control block for the boot VCPU and subsequent VCPUs as they are brought up and on resume. The event array is expanded as required when event ports are setup. The 'xen.fifo_events=0' command line option may be used to disable use of the FIFO-based ABI. Signed-off-by: David Vrabel <david.vrabel@citrix.com> Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Diffstat (limited to 'drivers/xen/events/events_fifo.c')
-rw-r--r--drivers/xen/events/events_fifo.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c
new file mode 100644
index 000000000000..e2bf9571f7fe
--- /dev/null
+++ b/drivers/xen/events/events_fifo.c
@@ -0,0 +1,426 @@
1/*
2 * Xen event channels (FIFO-based ABI)
3 *
4 * Copyright (C) 2013 Citrix Systems R&D ltd.
5 *
6 * This source code is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * Or, when distributed separately from the Linux kernel or
12 * incorporated into other software packages, subject to the following
13 * license:
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this source file (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use, copy, modify,
18 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31 * IN THE SOFTWARE.
32 */
33
34#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
35
36#include <linux/linkage.h>
37#include <linux/interrupt.h>
38#include <linux/irq.h>
39#include <linux/module.h>
40#include <linux/smp.h>
41#include <linux/percpu.h>
42#include <linux/cpu.h>
43
44#include <asm/sync_bitops.h>
45#include <asm/xen/hypercall.h>
46#include <asm/xen/hypervisor.h>
47#include <asm/xen/page.h>
48
49#include <xen/xen.h>
50#include <xen/xen-ops.h>
51#include <xen/events.h>
52#include <xen/interface/xen.h>
53#include <xen/interface/event_channel.h>
54
55#include "events_internal.h"
56
57#define EVENT_WORDS_PER_PAGE (PAGE_SIZE / sizeof(event_word_t))
58#define MAX_EVENT_ARRAY_PAGES (EVTCHN_FIFO_NR_CHANNELS / EVENT_WORDS_PER_PAGE)
59
60struct evtchn_fifo_queue {
61 uint32_t head[EVTCHN_FIFO_MAX_QUEUES];
62};
63
64static DEFINE_PER_CPU(struct evtchn_fifo_control_block *, cpu_control_block);
65static DEFINE_PER_CPU(struct evtchn_fifo_queue, cpu_queue);
66static event_word_t *event_array[MAX_EVENT_ARRAY_PAGES] __read_mostly;
67static unsigned event_array_pages __read_mostly;
68
69#define BM(w) ((unsigned long *)(w))
70
71static inline event_word_t *event_word_from_port(unsigned port)
72{
73 unsigned i = port / EVENT_WORDS_PER_PAGE;
74
75 return event_array[i] + port % EVENT_WORDS_PER_PAGE;
76}
77
78static unsigned evtchn_fifo_max_channels(void)
79{
80 return EVTCHN_FIFO_NR_CHANNELS;
81}
82
83static unsigned evtchn_fifo_nr_channels(void)
84{
85 return event_array_pages * EVENT_WORDS_PER_PAGE;
86}
87
88static void free_unused_array_pages(void)
89{
90 unsigned i;
91
92 for (i = event_array_pages; i < MAX_EVENT_ARRAY_PAGES; i++) {
93 if (!event_array[i])
94 break;
95 free_page((unsigned long)event_array[i]);
96 event_array[i] = NULL;
97 }
98}
99
100static void init_array_page(event_word_t *array_page)
101{
102 unsigned i;
103
104 for (i = 0; i < EVENT_WORDS_PER_PAGE; i++)
105 array_page[i] = 1 << EVTCHN_FIFO_MASKED;
106}
107
108static int evtchn_fifo_setup(struct irq_info *info)
109{
110 unsigned port = info->evtchn;
111 unsigned new_array_pages;
112 int ret = -ENOMEM;
113
114 new_array_pages = port / EVENT_WORDS_PER_PAGE + 1;
115
116 if (new_array_pages > MAX_EVENT_ARRAY_PAGES)
117 return -EINVAL;
118
119 while (event_array_pages < new_array_pages) {
120 void *array_page;
121 struct evtchn_expand_array expand_array;
122
123 /* Might already have a page if we've resumed. */
124 array_page = event_array[event_array_pages];
125 if (!array_page) {
126 array_page = (void *)__get_free_page(GFP_KERNEL);
127 if (array_page == NULL)
128 goto error;
129 event_array[event_array_pages] = array_page;
130 }
131
132 /* Mask all events in this page before adding it. */
133 init_array_page(array_page);
134
135 expand_array.array_gfn = virt_to_mfn(array_page);
136
137 ret = HYPERVISOR_event_channel_op(EVTCHNOP_expand_array, &expand_array);
138 if (ret < 0)
139 goto error;
140
141 event_array_pages++;
142 }
143 return 0;
144
145 error:
146 if (event_array_pages == 0)
147 panic("xen: unable to expand event array with initial page (%d)\n", ret);
148 else
149 pr_err("unable to expand event array (%d)\n", ret);
150 free_unused_array_pages();
151 return ret;
152}
153
154static void evtchn_fifo_bind_to_cpu(struct irq_info *info, unsigned cpu)
155{
156 /* no-op */
157}
158
159static void evtchn_fifo_clear_pending(unsigned port)
160{
161 event_word_t *word = event_word_from_port(port);
162 sync_clear_bit(EVTCHN_FIFO_PENDING, BM(word));
163}
164
165static void evtchn_fifo_set_pending(unsigned port)
166{
167 event_word_t *word = event_word_from_port(port);
168 sync_set_bit(EVTCHN_FIFO_PENDING, BM(word));
169}
170
171static bool evtchn_fifo_is_pending(unsigned port)
172{
173 event_word_t *word = event_word_from_port(port);
174 return sync_test_bit(EVTCHN_FIFO_PENDING, BM(word));
175}
176
177static bool evtchn_fifo_test_and_set_mask(unsigned port)
178{
179 event_word_t *word = event_word_from_port(port);
180 return sync_test_and_set_bit(EVTCHN_FIFO_MASKED, BM(word));
181}
182
183static void evtchn_fifo_mask(unsigned port)
184{
185 event_word_t *word = event_word_from_port(port);
186 sync_set_bit(EVTCHN_FIFO_MASKED, BM(word));
187}
188
189/*
190 * Clear MASKED, spinning if BUSY is set.
191 */
192static void clear_masked(volatile event_word_t *word)
193{
194 event_word_t new, old, w;
195
196 w = *word;
197
198 do {
199 old = w & ~(1 << EVTCHN_FIFO_BUSY);
200 new = old & ~(1 << EVTCHN_FIFO_MASKED);
201 w = sync_cmpxchg(word, old, new);
202 } while (w != old);
203}
204
205static void evtchn_fifo_unmask(unsigned port)
206{
207 event_word_t *word = event_word_from_port(port);
208
209 BUG_ON(!irqs_disabled());
210
211 clear_masked(word);
212 if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))) {
213 struct evtchn_unmask unmask = { .port = port };
214 (void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask);
215 }
216}
217
218static uint32_t clear_linked(volatile event_word_t *word)
219{
220 event_word_t new, old, w;
221
222 w = *word;
223
224 do {
225 old = w;
226 new = (w & ~((1 << EVTCHN_FIFO_LINKED)
227 | EVTCHN_FIFO_LINK_MASK));
228 } while ((w = sync_cmpxchg(word, old, new)) != old);
229
230 return w & EVTCHN_FIFO_LINK_MASK;
231}
232
233static void handle_irq_for_port(unsigned port)
234{
235 int irq;
236 struct irq_desc *desc;
237
238 irq = get_evtchn_to_irq(port);
239 if (irq != -1) {
240 desc = irq_to_desc(irq);
241 if (desc)
242 generic_handle_irq_desc(irq, desc);
243 }
244}
245
246static void consume_one_event(unsigned cpu,
247 struct evtchn_fifo_control_block *control_block,
248 unsigned priority, uint32_t *ready)
249{
250 struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
251 uint32_t head;
252 unsigned port;
253 event_word_t *word;
254
255 head = q->head[priority];
256
257 /*
258 * Reached the tail last time? Read the new HEAD from the
259 * control block.
260 */
261 if (head == 0) {
262 rmb(); /* Ensure word is up-to-date before reading head. */
263 head = control_block->head[priority];
264 }
265
266 port = head;
267 word = event_word_from_port(port);
268 head = clear_linked(word);
269
270 /*
271 * If the link is non-zero, there are more events in the
272 * queue, otherwise the queue is empty.
273 *
274 * If the queue is empty, clear this priority from our local
275 * copy of the ready word.
276 */
277 if (head == 0)
278 clear_bit(priority, BM(ready));
279
280 if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))
281 && !sync_test_bit(EVTCHN_FIFO_MASKED, BM(word)))
282 handle_irq_for_port(port);
283
284 q->head[priority] = head;
285}
286
287static void evtchn_fifo_handle_events(unsigned cpu)
288{
289 struct evtchn_fifo_control_block *control_block;
290 uint32_t ready;
291 unsigned q;
292
293 control_block = per_cpu(cpu_control_block, cpu);
294
295 ready = xchg(&control_block->ready, 0);
296
297 while (ready) {
298 q = find_first_bit(BM(&ready), EVTCHN_FIFO_MAX_QUEUES);
299 consume_one_event(cpu, control_block, q, &ready);
300 ready |= xchg(&control_block->ready, 0);
301 }
302}
303
304static void evtchn_fifo_resume(void)
305{
306 unsigned cpu;
307
308 for_each_possible_cpu(cpu) {
309 void *control_block = per_cpu(cpu_control_block, cpu);
310 struct evtchn_init_control init_control;
311 int ret;
312
313 if (!control_block)
314 continue;
315
316 /*
317 * If this CPU is offline, take the opportunity to
318 * free the control block while it is not being
319 * used.
320 */
321 if (!cpu_online(cpu)) {
322 free_page((unsigned long)control_block);
323 per_cpu(cpu_control_block, cpu) = NULL;
324 continue;
325 }
326
327 init_control.control_gfn = virt_to_mfn(control_block);
328 init_control.offset = 0;
329 init_control.vcpu = cpu;
330
331 ret = HYPERVISOR_event_channel_op(EVTCHNOP_init_control,
332 &init_control);
333 if (ret < 0)
334 BUG();
335 }
336
337 /*
338 * The event array starts out as empty again and is extended
339 * as normal when events are bound. The existing pages will
340 * be reused.
341 */
342 event_array_pages = 0;
343}
344
345static const struct evtchn_ops evtchn_ops_fifo = {
346 .max_channels = evtchn_fifo_max_channels,
347 .nr_channels = evtchn_fifo_nr_channels,
348 .setup = evtchn_fifo_setup,
349 .bind_to_cpu = evtchn_fifo_bind_to_cpu,
350 .clear_pending = evtchn_fifo_clear_pending,
351 .set_pending = evtchn_fifo_set_pending,
352 .is_pending = evtchn_fifo_is_pending,
353 .test_and_set_mask = evtchn_fifo_test_and_set_mask,
354 .mask = evtchn_fifo_mask,
355 .unmask = evtchn_fifo_unmask,
356 .handle_events = evtchn_fifo_handle_events,
357 .resume = evtchn_fifo_resume,
358};
359
360static int __cpuinit evtchn_fifo_init_control_block(unsigned cpu)
361{
362 struct page *control_block = NULL;
363 struct evtchn_init_control init_control;
364 int ret = -ENOMEM;
365
366 control_block = alloc_page(GFP_KERNEL|__GFP_ZERO);
367 if (control_block == NULL)
368 goto error;
369
370 init_control.control_gfn = virt_to_mfn(page_address(control_block));
371 init_control.offset = 0;
372 init_control.vcpu = cpu;
373
374 ret = HYPERVISOR_event_channel_op(EVTCHNOP_init_control, &init_control);
375 if (ret < 0)
376 goto error;
377
378 per_cpu(cpu_control_block, cpu) = page_address(control_block);
379
380 return 0;
381
382 error:
383 __free_page(control_block);
384 return ret;
385}
386
387static int __cpuinit evtchn_fifo_cpu_notification(struct notifier_block *self,
388 unsigned long action,
389 void *hcpu)
390{
391 int cpu = (long)hcpu;
392 int ret = 0;
393
394 switch (action) {
395 case CPU_UP_PREPARE:
396 if (!per_cpu(cpu_control_block, cpu))
397 ret = evtchn_fifo_init_control_block(cpu);
398 break;
399 default:
400 break;
401 }
402 return ret < 0 ? NOTIFY_BAD : NOTIFY_OK;
403}
404
405static struct notifier_block evtchn_fifo_cpu_notifier __cpuinitdata = {
406 .notifier_call = evtchn_fifo_cpu_notification,
407};
408
409int __init xen_evtchn_fifo_init(void)
410{
411 int cpu = get_cpu();
412 int ret;
413
414 ret = evtchn_fifo_init_control_block(cpu);
415 if (ret < 0)
416 goto out;
417
418 pr_info("Using FIFO-based ABI\n");
419
420 evtchn_ops = &evtchn_ops_fifo;
421
422 register_cpu_notifier(&evtchn_fifo_cpu_notifier);
423out:
424 put_cpu();
425 return ret;
426}