1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * IRQ offload/bypass manager
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2015 Red Hat, Inc.
6*4882a593Smuzhiyun * Copyright (c) 2015 Linaro Ltd.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Various virtualization hardware acceleration techniques allow bypassing or
9*4882a593Smuzhiyun * offloading interrupts received from devices around the host kernel. Posted
10*4882a593Smuzhiyun * Interrupts on Intel VT-d systems can allow interrupts to be received
11*4882a593Smuzhiyun * directly by a virtual machine. ARM IRQ Forwarding allows forwarded physical
12*4882a593Smuzhiyun * interrupts to be directly deactivated by the guest. This manager allows
13*4882a593Smuzhiyun * interrupt producers and consumers to find each other to enable this sort of
14*4882a593Smuzhiyun * bypass.
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <linux/irqbypass.h>
18*4882a593Smuzhiyun #include <linux/list.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/mutex.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
23*4882a593Smuzhiyun MODULE_DESCRIPTION("IRQ bypass manager utility module");
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun static LIST_HEAD(producers);
26*4882a593Smuzhiyun static LIST_HEAD(consumers);
27*4882a593Smuzhiyun static DEFINE_MUTEX(lock);
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /* @lock must be held when calling connect */
__connect(struct irq_bypass_producer * prod,struct irq_bypass_consumer * cons)30*4882a593Smuzhiyun static int __connect(struct irq_bypass_producer *prod,
31*4882a593Smuzhiyun struct irq_bypass_consumer *cons)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun int ret = 0;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun if (prod->stop)
36*4882a593Smuzhiyun prod->stop(prod);
37*4882a593Smuzhiyun if (cons->stop)
38*4882a593Smuzhiyun cons->stop(cons);
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun if (prod->add_consumer)
41*4882a593Smuzhiyun ret = prod->add_consumer(prod, cons);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun if (!ret) {
44*4882a593Smuzhiyun ret = cons->add_producer(cons, prod);
45*4882a593Smuzhiyun if (ret && prod->del_consumer)
46*4882a593Smuzhiyun prod->del_consumer(prod, cons);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun if (cons->start)
50*4882a593Smuzhiyun cons->start(cons);
51*4882a593Smuzhiyun if (prod->start)
52*4882a593Smuzhiyun prod->start(prod);
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun return ret;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /* @lock must be held when calling disconnect */
__disconnect(struct irq_bypass_producer * prod,struct irq_bypass_consumer * cons)58*4882a593Smuzhiyun static void __disconnect(struct irq_bypass_producer *prod,
59*4882a593Smuzhiyun struct irq_bypass_consumer *cons)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun if (prod->stop)
62*4882a593Smuzhiyun prod->stop(prod);
63*4882a593Smuzhiyun if (cons->stop)
64*4882a593Smuzhiyun cons->stop(cons);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun cons->del_producer(cons, prod);
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun if (prod->del_consumer)
69*4882a593Smuzhiyun prod->del_consumer(prod, cons);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun if (cons->start)
72*4882a593Smuzhiyun cons->start(cons);
73*4882a593Smuzhiyun if (prod->start)
74*4882a593Smuzhiyun prod->start(prod);
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /**
78*4882a593Smuzhiyun * irq_bypass_register_producer - register IRQ bypass producer
79*4882a593Smuzhiyun * @producer: pointer to producer structure
80*4882a593Smuzhiyun *
81*4882a593Smuzhiyun * Add the provided IRQ producer to the list of producers and connect
82*4882a593Smuzhiyun * with any matching token found on the IRQ consumers list.
83*4882a593Smuzhiyun */
irq_bypass_register_producer(struct irq_bypass_producer * producer)84*4882a593Smuzhiyun int irq_bypass_register_producer(struct irq_bypass_producer *producer)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun struct irq_bypass_producer *tmp;
87*4882a593Smuzhiyun struct irq_bypass_consumer *consumer;
88*4882a593Smuzhiyun int ret;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun if (!producer->token)
91*4882a593Smuzhiyun return -EINVAL;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun might_sleep();
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun if (!try_module_get(THIS_MODULE))
96*4882a593Smuzhiyun return -ENODEV;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun mutex_lock(&lock);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun list_for_each_entry(tmp, &producers, node) {
101*4882a593Smuzhiyun if (tmp->token == producer->token) {
102*4882a593Smuzhiyun ret = -EBUSY;
103*4882a593Smuzhiyun goto out_err;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun list_for_each_entry(consumer, &consumers, node) {
108*4882a593Smuzhiyun if (consumer->token == producer->token) {
109*4882a593Smuzhiyun ret = __connect(producer, consumer);
110*4882a593Smuzhiyun if (ret)
111*4882a593Smuzhiyun goto out_err;
112*4882a593Smuzhiyun break;
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun list_add(&producer->node, &producers);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun mutex_unlock(&lock);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun return 0;
121*4882a593Smuzhiyun out_err:
122*4882a593Smuzhiyun mutex_unlock(&lock);
123*4882a593Smuzhiyun module_put(THIS_MODULE);
124*4882a593Smuzhiyun return ret;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /**
129*4882a593Smuzhiyun * irq_bypass_unregister_producer - unregister IRQ bypass producer
130*4882a593Smuzhiyun * @producer: pointer to producer structure
131*4882a593Smuzhiyun *
132*4882a593Smuzhiyun * Remove a previously registered IRQ producer from the list of producers
133*4882a593Smuzhiyun * and disconnect it from any connected IRQ consumer.
134*4882a593Smuzhiyun */
irq_bypass_unregister_producer(struct irq_bypass_producer * producer)135*4882a593Smuzhiyun void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct irq_bypass_producer *tmp;
138*4882a593Smuzhiyun struct irq_bypass_consumer *consumer;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun if (!producer->token)
141*4882a593Smuzhiyun return;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun might_sleep();
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (!try_module_get(THIS_MODULE))
146*4882a593Smuzhiyun return; /* nothing in the list anyway */
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun mutex_lock(&lock);
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun list_for_each_entry(tmp, &producers, node) {
151*4882a593Smuzhiyun if (tmp->token != producer->token)
152*4882a593Smuzhiyun continue;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun list_for_each_entry(consumer, &consumers, node) {
155*4882a593Smuzhiyun if (consumer->token == producer->token) {
156*4882a593Smuzhiyun __disconnect(producer, consumer);
157*4882a593Smuzhiyun break;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun list_del(&producer->node);
162*4882a593Smuzhiyun module_put(THIS_MODULE);
163*4882a593Smuzhiyun break;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun mutex_unlock(&lock);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun module_put(THIS_MODULE);
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun /**
173*4882a593Smuzhiyun * irq_bypass_register_consumer - register IRQ bypass consumer
174*4882a593Smuzhiyun * @consumer: pointer to consumer structure
175*4882a593Smuzhiyun *
176*4882a593Smuzhiyun * Add the provided IRQ consumer to the list of consumers and connect
177*4882a593Smuzhiyun * with any matching token found on the IRQ producer list.
178*4882a593Smuzhiyun */
irq_bypass_register_consumer(struct irq_bypass_consumer * consumer)179*4882a593Smuzhiyun int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun struct irq_bypass_consumer *tmp;
182*4882a593Smuzhiyun struct irq_bypass_producer *producer;
183*4882a593Smuzhiyun int ret;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun if (!consumer->token ||
186*4882a593Smuzhiyun !consumer->add_producer || !consumer->del_producer)
187*4882a593Smuzhiyun return -EINVAL;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun might_sleep();
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun if (!try_module_get(THIS_MODULE))
192*4882a593Smuzhiyun return -ENODEV;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun mutex_lock(&lock);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun list_for_each_entry(tmp, &consumers, node) {
197*4882a593Smuzhiyun if (tmp->token == consumer->token || tmp == consumer) {
198*4882a593Smuzhiyun ret = -EBUSY;
199*4882a593Smuzhiyun goto out_err;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun list_for_each_entry(producer, &producers, node) {
204*4882a593Smuzhiyun if (producer->token == consumer->token) {
205*4882a593Smuzhiyun ret = __connect(producer, consumer);
206*4882a593Smuzhiyun if (ret)
207*4882a593Smuzhiyun goto out_err;
208*4882a593Smuzhiyun break;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun list_add(&consumer->node, &consumers);
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun mutex_unlock(&lock);
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun return 0;
217*4882a593Smuzhiyun out_err:
218*4882a593Smuzhiyun mutex_unlock(&lock);
219*4882a593Smuzhiyun module_put(THIS_MODULE);
220*4882a593Smuzhiyun return ret;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun /**
225*4882a593Smuzhiyun * irq_bypass_unregister_consumer - unregister IRQ bypass consumer
226*4882a593Smuzhiyun * @consumer: pointer to consumer structure
227*4882a593Smuzhiyun *
228*4882a593Smuzhiyun * Remove a previously registered IRQ consumer from the list of consumers
229*4882a593Smuzhiyun * and disconnect it from any connected IRQ producer.
230*4882a593Smuzhiyun */
irq_bypass_unregister_consumer(struct irq_bypass_consumer * consumer)231*4882a593Smuzhiyun void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun struct irq_bypass_consumer *tmp;
234*4882a593Smuzhiyun struct irq_bypass_producer *producer;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun if (!consumer->token)
237*4882a593Smuzhiyun return;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun might_sleep();
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun if (!try_module_get(THIS_MODULE))
242*4882a593Smuzhiyun return; /* nothing in the list anyway */
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun mutex_lock(&lock);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun list_for_each_entry(tmp, &consumers, node) {
247*4882a593Smuzhiyun if (tmp != consumer)
248*4882a593Smuzhiyun continue;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun list_for_each_entry(producer, &producers, node) {
251*4882a593Smuzhiyun if (producer->token == consumer->token) {
252*4882a593Smuzhiyun __disconnect(producer, consumer);
253*4882a593Smuzhiyun break;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun list_del(&consumer->node);
258*4882a593Smuzhiyun module_put(THIS_MODULE);
259*4882a593Smuzhiyun break;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun mutex_unlock(&lock);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun module_put(THIS_MODULE);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);
267