Skip to content

Commit f73f817

Browse files
Alex Williamsonbonzini
authored andcommitted
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>
1 parent 18cd52c commit f73f817

File tree

5 files changed

+357
-0
lines changed

5 files changed

+357
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11177,6 +11177,13 @@ L: netdev@vger.kernel.org
1117711177
S: Maintained
1117811178
F: drivers/net/ethernet/via/via-velocity.*
1117911179

11180+
VIRT LIB
11181+
M: Alex Williamson <alex.williamson@redhat.com>
11182+
M: Paolo Bonzini <pbonzini@redhat.com>
11183+
L: kvm@vger.kernel.org
11184+
S: Supported
11185+
F: virt/lib/
11186+
1118011187
VIVID VIRTUAL VIDEO DRIVER
1118111188
M: Hans Verkuil <hverkuil@xs4all.nl>
1118211189
L: linux-media@vger.kernel.org

include/linux/irqbypass.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
16+
struct 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+
*/
48+
struct 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+
*/
74+
struct 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+
85+
int irq_bypass_register_producer(struct irq_bypass_producer *);
86+
void irq_bypass_unregister_producer(struct irq_bypass_producer *);
87+
int irq_bypass_register_consumer(struct irq_bypass_consumer *);
88+
void irq_bypass_unregister_consumer(struct irq_bypass_consumer *);
89+
90+
#endif /* IRQBYPASS_H */

virt/lib/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
config IRQ_BYPASS_MANAGER
2+
tristate

virt/lib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
obj-$(CONFIG_IRQ_BYPASS_MANAGER) += irqbypass.o

virt/lib/irqbypass.c

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
25+
MODULE_LICENSE("GPL v2");
26+
MODULE_DESCRIPTION("IRQ bypass manager utility module");
27+
28+
static LIST_HEAD(producers);
29+
static LIST_HEAD(consumers);
30+
static DEFINE_MUTEX(lock);
31+
32+
/* @lock must be held when calling connect */
33+
static 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 */
61+
static 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+
*/
87+
int 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+
}
125+
EXPORT_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+
*/
134+
void 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+
}
166+
EXPORT_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+
*/
175+
int 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+
}
216+
EXPORT_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+
*/
225+
void 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+
}
257+
EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);

0 commit comments

Comments
 (0)