xref: /OK3568_Linux_fs/kernel/virt/lib/irqbypass.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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