aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--MAINTAINERS7
-rw-r--r--include/linux/irqbypass.h90
-rw-r--r--virt/lib/Kconfig2
-rw-r--r--virt/lib/Makefile1
-rw-r--r--virt/lib/irqbypass.c257
5 files changed, 357 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 797236befd27..3b738cb8bdeb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11177,6 +11177,13 @@ L: netdev@vger.kernel.org
11177S: Maintained 11177S: Maintained
11178F: drivers/net/ethernet/via/via-velocity.* 11178F: drivers/net/ethernet/via/via-velocity.*
11179 11179
11180VIRT LIB
11181M: Alex Williamson <alex.williamson@redhat.com>
11182M: Paolo Bonzini <pbonzini@redhat.com>
11183L: kvm@vger.kernel.org
11184S: Supported
11185F: virt/lib/
11186
11180VIVID VIRTUAL VIDEO DRIVER 11187VIVID VIRTUAL VIDEO DRIVER
11181M: Hans Verkuil <hverkuil@xs4all.nl> 11188M: Hans Verkuil <hverkuil@xs4all.nl>
11182L: linux-media@vger.kernel.org 11189L: linux-media@vger.kernel.org
diff --git a/include/linux/irqbypass.h b/include/linux/irqbypass.h
new file mode 100644
index 000000000000..1551b5b2f4c2
--- /dev/null
+++ b/include/linux/irqbypass.h
@@ -0,0 +1,90 @@
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#ifndef IRQBYPASS_H
12#define IRQBYPASS_H
13
14#include <linux/list.h>
15
16struct irq_bypass_consumer;
17
18/*
19 * Theory of operation
20 *
21 * The IRQ bypass manager is a simple set of lists and callbacks that allows
22 * IRQ producers (ex. physical interrupt sources) to be matched to IRQ
23 * consumers (ex. virtualization hardware that allows IRQ bypass or offload)
24 * via a shared token (ex. eventfd_ctx). Producers and consumers register
25 * independently. When a token match is found, the optional @stop callback
26 * will be called for each participant. The pair will then be connected via
27 * the @add_* callbacks, and finally the optional @start callback will allow
28 * any final coordination. When either participant is unregistered, the
29 * process is repeated using the @del_* callbacks in place of the @add_*
30 * callbacks. Match tokens must be unique per producer/consumer, 1:N pairings
31 * are not supported.
32 */
33
34/**
35 * struct irq_bypass_producer - IRQ bypass producer definition
36 * @node: IRQ bypass manager private list management
37 * @token: opaque token to match between producer and consumer
38 * @irq: Linux IRQ number for the producer device
39 * @add_consumer: Connect the IRQ producer to an IRQ consumer (optional)
40 * @del_consumer: Disconnect the IRQ producer from an IRQ consumer (optional)
41 * @stop: Perform any quiesce operations necessary prior to add/del (optional)
42 * @start: Perform any startup operations necessary after add/del (optional)
43 *
44 * The IRQ bypass producer structure represents an interrupt source for
45 * participation in possible host bypass, for instance an interrupt vector
46 * for a physical device assigned to a VM.
47 */
48struct irq_bypass_producer {
49 struct list_head node;
50 void *token;
51 int irq;
52 int (*add_consumer)(struct irq_bypass_producer *,
53 struct irq_bypass_consumer *);
54 void (*del_consumer)(struct irq_bypass_producer *,
55 struct irq_bypass_consumer *);
56 void (*stop)(struct irq_bypass_producer *);
57 void (*start)(struct irq_bypass_producer *);
58};
59
60/**
61 * struct irq_bypass_consumer - IRQ bypass consumer definition
62 * @node: IRQ bypass manager private list management
63 * @token: opaque token to match between producer and consumer
64 * @add_producer: Connect the IRQ consumer to an IRQ producer
65 * @del_producer: Disconnect the IRQ consumer from an IRQ producer
66 * @stop: Perform any quiesce operations necessary prior to add/del (optional)
67 * @start: Perform any startup operations necessary after add/del (optional)
68 *
69 * The IRQ bypass consumer structure represents an interrupt sink for
70 * participation in possible host bypass, for instance a hypervisor may
71 * support offloads to allow bypassing the host entirely or offload
72 * portions of the interrupt handling to the VM.
73 */
74struct irq_bypass_consumer {
75 struct list_head node;
76 void *token;
77 int (*add_producer)(struct irq_bypass_consumer *,
78 struct irq_bypass_producer *);
79 void (*del_producer)(struct irq_bypass_consumer *,
80 struct irq_bypass_producer *);
81 void (*stop)(struct irq_bypass_consumer *);
82 void (*start)(struct irq_bypass_consumer *);
83};
84
85int irq_bypass_register_producer(struct irq_bypass_producer *);
86void irq_bypass_unregister_producer(struct irq_bypass_producer *);
87int irq_bypass_register_consumer(struct irq_bypass_consumer *);
88void irq_bypass_unregister_consumer(struct irq_bypass_consumer *);
89
90#endif /* IRQBYPASS_H */
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);