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