summaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2015-09-18 10:29:39 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2015-10-01 09:06:43 -0400
commitf73f8173126ba68eb1c42bd9a234a51d78576ca6 (patch)
tree53384c5b359400ffab53dbd44e4467f62ef3300f /virt
parent18cd52c4d9bfbd081fb643021ba8d048475cd82a (diff)
virt: IRQ bypass manager
When a physical I/O device is assigned to a virtual machine through facilities like VFIO and KVM, the interrupt for the device generally bounces through the host system before being injected into the VM. However, hardware technologies exist that often allow the host to be bypassed for some of these scenarios. Intel Posted Interrupts allow the specified physical edge interrupts to be directly injected into a guest when delivered to a physical processor while the vCPU is running. ARM IRQ Forwarding allows forwarded physical interrupts to be directly deactivated by the guest. The IRQ bypass manager here is meant to provide the shim to connect interrupt producers, generally the host physical device driver, with interrupt consumers, generally the hypervisor, in order to configure these bypass mechanism. To do this, we base the connection on a shared, opaque token. For KVM-VFIO this is expected to be an eventfd_ctx since this is the connection we already use to connect an eventfd to an irqfd on the in-kernel path. When a producer and consumer with matching tokens is found, callbacks via both registered participants allow the bypass facilities to be automatically enabled. Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Reviewed-by: Eric Auger <eric.auger@linaro.org> Tested-by: Eric Auger <eric.auger@linaro.org> Tested-by: Feng Wu <feng.wu@intel.com> Signed-off-by: Feng Wu <feng.wu@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'virt')
-rw-r--r--virt/lib/Kconfig2
-rw-r--r--virt/lib/Makefile1
-rw-r--r--virt/lib/irqbypass.c257
3 files changed, 260 insertions, 0 deletions
diff --git a/virt/lib/Kconfig b/virt/lib/Kconfig
new file mode 100644
index 000000000000..89a414f815d2
--- /dev/null
+++ b/virt/lib/Kconfig
@@ -0,0 +1,2 @@
1config IRQ_BYPASS_MANAGER
2 tristate
diff --git a/virt/lib/Makefile b/virt/lib/Makefile
new file mode 100644
index 000000000000..901228d1ffbc
--- /dev/null
+++ b/virt/lib/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_IRQ_BYPASS_MANAGER) += irqbypass.o
diff --git a/virt/lib/irqbypass.c b/virt/lib/irqbypass.c
new file mode 100644
index 000000000000..09a03b5a21ff
--- /dev/null
+++ b/virt/lib/irqbypass.c
@@ -0,0 +1,257 @@
1/*
2 * IRQ offload/bypass manager
3 *
4 * Copyright (C) 2015 Red Hat, Inc.
5 * Copyright (c) 2015 Linaro Ltd.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Various virtualization hardware acceleration techniques allow bypassing or
12 * offloading interrupts received from devices around the host kernel. Posted
13 * Interrupts on Intel VT-d systems can allow interrupts to be received
14 * directly by a virtual machine. ARM IRQ Forwarding allows forwarded physical
15 * interrupts to be directly deactivated by the guest. This manager allows
16 * interrupt producers and consumers to find each other to enable this sort of
17 * bypass.
18 */
19
20#include <linux/irqbypass.h>
21#include <linux/list.h>
22#include <linux/module.h>
23#include <linux/mutex.h>
24
25MODULE_LICENSE("GPL v2");
26MODULE_DESCRIPTION("IRQ bypass manager utility module");
27
28static LIST_HEAD(producers);
29static LIST_HEAD(consumers);
30static DEFINE_MUTEX(lock);
31
32/* @lock must be held when calling connect */
33static int __connect(struct irq_bypass_producer *prod,
34 struct irq_bypass_consumer *cons)
35{
36 int ret = 0;
37
38 if (prod->stop)
39 prod->stop(prod);
40 if (cons->stop)
41 cons->stop(cons);
42
43 if (prod->add_consumer)
44 ret = prod->add_consumer(prod, cons);
45
46 if (!ret) {
47 ret = cons->add_producer(cons, prod);
48 if (ret && prod->del_consumer)
49 prod->del_consumer(prod, cons);
50 }
51
52 if (cons->start)
53 cons->start(cons);
54 if (prod->start)
55 prod->start(prod);
56
57 return ret;
58}
59
60/* @lock must be held when calling disconnect */
61static void __disconnect(struct irq_bypass_producer *prod,
62 struct irq_bypass_consumer *cons)
63{
64 if (prod->stop)
65 prod->stop(prod);
66 if (cons->stop)
67 cons->stop(cons);
68
69 cons->del_producer(cons, prod);
70
71 if (prod->del_consumer)
72 prod->del_consumer(prod, cons);
73
74 if (cons->start)
75 cons->start(cons);
76 if (prod->start)
77 prod->start(prod);
78}
79
80/**
81 * irq_bypass_register_producer - register IRQ bypass producer
82 * @producer: pointer to producer structure
83 *
84 * Add the provided IRQ producer to the list of producers and connect
85 * with any matching token found on the IRQ consumers list.
86 */
87int irq_bypass_register_producer(struct irq_bypass_producer *producer)
88{
89 struct irq_bypass_producer *tmp;
90 struct irq_bypass_consumer *consumer;
91
92 might_sleep();
93
94 if (!try_module_get(THIS_MODULE))
95 return -ENODEV;
96
97 mutex_lock(&lock);
98
99 list_for_each_entry(tmp, &producers, node) {
100 if (tmp->token == producer->token) {
101 mutex_unlock(&lock);
102 module_put(THIS_MODULE);
103 return -EBUSY;
104 }
105 }
106
107 list_for_each_entry(consumer, &consumers, node) {
108 if (consumer->token == producer->token) {
109 int ret = __connect(producer, consumer);
110 if (ret) {
111 mutex_unlock(&lock);
112 module_put(THIS_MODULE);
113 return ret;
114 }
115 break;
116 }
117 }
118
119 list_add(&producer->node, &producers);
120
121 mutex_unlock(&lock);
122
123 return 0;
124}
125EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
126
127/**
128 * irq_bypass_unregister_producer - unregister IRQ bypass producer
129 * @producer: pointer to producer structure
130 *
131 * Remove a previously registered IRQ producer from the list of producers
132 * and disconnect it from any connected IRQ consumer.
133 */
134void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
135{
136 struct irq_bypass_producer *tmp;
137 struct irq_bypass_consumer *consumer;
138
139 might_sleep();
140
141 if (!try_module_get(THIS_MODULE))
142 return; /* nothing in the list anyway */
143
144 mutex_lock(&lock);
145
146 list_for_each_entry(tmp, &producers, node) {
147 if (tmp->token != producer->token)
148 continue;
149
150 list_for_each_entry(consumer, &consumers, node) {
151 if (consumer->token == producer->token) {
152 __disconnect(producer, consumer);
153 break;
154 }
155 }
156
157 list_del(&producer->node);
158 module_put(THIS_MODULE);
159 break;
160 }
161
162 mutex_unlock(&lock);
163
164 module_put(THIS_MODULE);
165}
166EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
167
168/**
169 * irq_bypass_register_consumer - register IRQ bypass consumer
170 * @consumer: pointer to consumer structure
171 *
172 * Add the provided IRQ consumer to the list of consumers and connect
173 * with any matching token found on the IRQ producer list.
174 */
175int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
176{
177 struct irq_bypass_consumer *tmp;
178 struct irq_bypass_producer *producer;
179
180 if (!consumer->add_producer || !consumer->del_producer)
181 return -EINVAL;
182
183 might_sleep();
184
185 if (!try_module_get(THIS_MODULE))
186 return -ENODEV;
187
188 mutex_lock(&lock);
189
190 list_for_each_entry(tmp, &consumers, node) {
191 if (tmp->token == consumer->token) {
192 mutex_unlock(&lock);
193 module_put(THIS_MODULE);
194 return -EBUSY;
195 }
196 }
197
198 list_for_each_entry(producer, &producers, node) {
199 if (producer->token == consumer->token) {
200 int ret = __connect(producer, consumer);
201 if (ret) {
202 mutex_unlock(&lock);
203 module_put(THIS_MODULE);
204 return ret;
205 }
206 break;
207 }
208 }
209
210 list_add(&consumer->node, &consumers);
211
212 mutex_unlock(&lock);
213
214 return 0;
215}
216EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
217
218/**
219 * irq_bypass_unregister_consumer - unregister IRQ bypass consumer
220 * @consumer: pointer to consumer structure
221 *
222 * Remove a previously registered IRQ consumer from the list of consumers
223 * and disconnect it from any connected IRQ producer.
224 */
225void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
226{
227 struct irq_bypass_consumer *tmp;
228 struct irq_bypass_producer *producer;
229
230 might_sleep();
231
232 if (!try_module_get(THIS_MODULE))
233 return; /* nothing in the list anyway */
234
235 mutex_lock(&lock);
236
237 list_for_each_entry(tmp, &consumers, node) {
238 if (tmp->token != consumer->token)
239 continue;
240
241 list_for_each_entry(producer, &producers, node) {
242 if (producer->token == consumer->token) {
243 __disconnect(producer, consumer);
244 break;
245 }
246 }
247
248 list_del(&consumer->node);
249 module_put(THIS_MODULE);
250 break;
251 }
252
253 mutex_unlock(&lock);
254
255 module_put(THIS_MODULE);
256}
257EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);