Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 22MODULE_LICENSE("GPL v2");
 23MODULE_DESCRIPTION("IRQ bypass manager utility module");
 24
 25static LIST_HEAD(producers);
 26static LIST_HEAD(consumers);
 27static DEFINE_MUTEX(lock);
 28
 29/* @lock must be held when calling connect */
 30static 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);
 54err_add_producer:
 55	if (prod->del_consumer)
 56		prod->del_consumer(prod, cons);
 57err_add_consumer:
 58	return ret;
 59}
 60
 61/* @lock must be held when calling disconnect */
 62static 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 */
 88int 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;
125out_err:
126	mutex_unlock(&lock);
127	module_put(THIS_MODULE);
128	return ret;
129}
130EXPORT_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 */
139void 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}
174EXPORT_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 */
183int 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;
221out_err:
222	mutex_unlock(&lock);
223	module_put(THIS_MODULE);
224	return ret;
225}
226EXPORT_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 */
235void 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}
270EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);