brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · 28fda42 Raw
267 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * IRQ offload/bypass manager4 *5 * Copyright (C) 2015 Red Hat, Inc.6 * Copyright (c) 2015 Linaro Ltd.7 *8 * Various virtualization hardware acceleration techniques allow bypassing or9 * offloading interrupts received from devices around the host kernel.  Posted10 * Interrupts on Intel VT-d systems can allow interrupts to be received11 * directly by a virtual machine.  ARM IRQ Forwarding allows forwarded physical12 * interrupts to be directly deactivated by the guest.  This manager allows13 * interrupt producers and consumers to find each other to enable this sort of14 * 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		ret = cons->add_producer(cons, prod);45		if (ret && prod->del_consumer)46			prod->del_consumer(prod, cons);47	}48 49	if (cons->start)50		cons->start(cons);51	if (prod->start)52		prod->start(prod);53 54	return ret;55}56 57/* @lock must be held when calling disconnect */58static void __disconnect(struct irq_bypass_producer *prod,59			 struct irq_bypass_consumer *cons)60{61	if (prod->stop)62		prod->stop(prod);63	if (cons->stop)64		cons->stop(cons);65 66	cons->del_producer(cons, prod);67 68	if (prod->del_consumer)69		prod->del_consumer(prod, cons);70 71	if (cons->start)72		cons->start(cons);73	if (prod->start)74		prod->start(prod);75}76 77/**78 * irq_bypass_register_producer - register IRQ bypass producer79 * @producer: pointer to producer structure80 *81 * Add the provided IRQ producer to the list of producers and connect82 * with any matching token found on the IRQ consumers list.83 */84int irq_bypass_register_producer(struct irq_bypass_producer *producer)85{86	struct irq_bypass_producer *tmp;87	struct irq_bypass_consumer *consumer;88	int ret;89 90	if (!producer->token)91		return -EINVAL;92 93	might_sleep();94 95	if (!try_module_get(THIS_MODULE))96		return -ENODEV;97 98	mutex_lock(&lock);99 100	list_for_each_entry(tmp, &producers, node) {101		if (tmp->token == producer->token) {102			ret = -EBUSY;103			goto out_err;104		}105	}106 107	list_for_each_entry(consumer, &consumers, node) {108		if (consumer->token == producer->token) {109			ret = __connect(producer, consumer);110			if (ret)111				goto out_err;112			break;113		}114	}115 116	list_add(&producer->node, &producers);117 118	mutex_unlock(&lock);119 120	return 0;121out_err:122	mutex_unlock(&lock);123	module_put(THIS_MODULE);124	return ret;125}126EXPORT_SYMBOL_GPL(irq_bypass_register_producer);127 128/**129 * irq_bypass_unregister_producer - unregister IRQ bypass producer130 * @producer: pointer to producer structure131 *132 * Remove a previously registered IRQ producer from the list of producers133 * and disconnect it from any connected IRQ consumer.134 */135void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)136{137	struct irq_bypass_producer *tmp;138	struct irq_bypass_consumer *consumer;139 140	if (!producer->token)141		return;142 143	might_sleep();144 145	if (!try_module_get(THIS_MODULE))146		return; /* nothing in the list anyway */147 148	mutex_lock(&lock);149 150	list_for_each_entry(tmp, &producers, node) {151		if (tmp->token != producer->token)152			continue;153 154		list_for_each_entry(consumer, &consumers, node) {155			if (consumer->token == producer->token) {156				__disconnect(producer, consumer);157				break;158			}159		}160 161		list_del(&producer->node);162		module_put(THIS_MODULE);163		break;164	}165 166	mutex_unlock(&lock);167 168	module_put(THIS_MODULE);169}170EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);171 172/**173 * irq_bypass_register_consumer - register IRQ bypass consumer174 * @consumer: pointer to consumer structure175 *176 * Add the provided IRQ consumer to the list of consumers and connect177 * with any matching token found on the IRQ producer list.178 */179int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)180{181	struct irq_bypass_consumer *tmp;182	struct irq_bypass_producer *producer;183	int ret;184 185	if (!consumer->token ||186	    !consumer->add_producer || !consumer->del_producer)187		return -EINVAL;188 189	might_sleep();190 191	if (!try_module_get(THIS_MODULE))192		return -ENODEV;193 194	mutex_lock(&lock);195 196	list_for_each_entry(tmp, &consumers, node) {197		if (tmp->token == consumer->token || tmp == consumer) {198			ret = -EBUSY;199			goto out_err;200		}201	}202 203	list_for_each_entry(producer, &producers, node) {204		if (producer->token == consumer->token) {205			ret = __connect(producer, consumer);206			if (ret)207				goto out_err;208			break;209		}210	}211 212	list_add(&consumer->node, &consumers);213 214	mutex_unlock(&lock);215 216	return 0;217out_err:218	mutex_unlock(&lock);219	module_put(THIS_MODULE);220	return ret;221}222EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);223 224/**225 * irq_bypass_unregister_consumer - unregister IRQ bypass consumer226 * @consumer: pointer to consumer structure227 *228 * Remove a previously registered IRQ consumer from the list of consumers229 * and disconnect it from any connected IRQ producer.230 */231void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)232{233	struct irq_bypass_consumer *tmp;234	struct irq_bypass_producer *producer;235 236	if (!consumer->token)237		return;238 239	might_sleep();240 241	if (!try_module_get(THIS_MODULE))242		return; /* nothing in the list anyway */243 244	mutex_lock(&lock);245 246	list_for_each_entry(tmp, &consumers, node) {247		if (tmp != consumer)248			continue;249 250		list_for_each_entry(producer, &producers, node) {251			if (producer->token == consumer->token) {252				__disconnect(producer, consumer);253				break;254			}255		}256 257		list_del(&consumer->node);258		module_put(THIS_MODULE);259		break;260	}261 262	mutex_unlock(&lock);263 264	module_put(THIS_MODULE);265}266EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);267