Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * IRQ offload/bypass manager
0004  *
0005  * Copyright (C) 2015 Red Hat, Inc.
0006  * Copyright (c) 2015 Linaro Ltd.
0007  *
0008  * Various virtualization hardware acceleration techniques allow bypassing or
0009  * offloading interrupts received from devices around the host kernel.  Posted
0010  * Interrupts on Intel VT-d systems can allow interrupts to be received
0011  * directly by a virtual machine.  ARM IRQ Forwarding allows forwarded physical
0012  * interrupts to be directly deactivated by the guest.  This manager allows
0013  * interrupt producers and consumers to find each other to enable this sort of
0014  * bypass.
0015  */
0016 
0017 #include <linux/irqbypass.h>
0018 #include <linux/list.h>
0019 #include <linux/module.h>
0020 #include <linux/mutex.h>
0021 
0022 MODULE_LICENSE("GPL v2");
0023 MODULE_DESCRIPTION("IRQ bypass manager utility module");
0024 
0025 static LIST_HEAD(producers);
0026 static LIST_HEAD(consumers);
0027 static DEFINE_MUTEX(lock);
0028 
0029 /* @lock must be held when calling connect */
0030 static int __connect(struct irq_bypass_producer *prod,
0031              struct irq_bypass_consumer *cons)
0032 {
0033     int ret = 0;
0034 
0035     if (prod->stop)
0036         prod->stop(prod);
0037     if (cons->stop)
0038         cons->stop(cons);
0039 
0040     if (prod->add_consumer)
0041         ret = prod->add_consumer(prod, cons);
0042 
0043     if (!ret) {
0044         ret = cons->add_producer(cons, prod);
0045         if (ret && prod->del_consumer)
0046             prod->del_consumer(prod, cons);
0047     }
0048 
0049     if (cons->start)
0050         cons->start(cons);
0051     if (prod->start)
0052         prod->start(prod);
0053 
0054     return ret;
0055 }
0056 
0057 /* @lock must be held when calling disconnect */
0058 static void __disconnect(struct irq_bypass_producer *prod,
0059              struct irq_bypass_consumer *cons)
0060 {
0061     if (prod->stop)
0062         prod->stop(prod);
0063     if (cons->stop)
0064         cons->stop(cons);
0065 
0066     cons->del_producer(cons, prod);
0067 
0068     if (prod->del_consumer)
0069         prod->del_consumer(prod, cons);
0070 
0071     if (cons->start)
0072         cons->start(cons);
0073     if (prod->start)
0074         prod->start(prod);
0075 }
0076 
0077 /**
0078  * irq_bypass_register_producer - register IRQ bypass producer
0079  * @producer: pointer to producer structure
0080  *
0081  * Add the provided IRQ producer to the list of producers and connect
0082  * with any matching token found on the IRQ consumers list.
0083  */
0084 int irq_bypass_register_producer(struct irq_bypass_producer *producer)
0085 {
0086     struct irq_bypass_producer *tmp;
0087     struct irq_bypass_consumer *consumer;
0088     int ret;
0089 
0090     if (!producer->token)
0091         return -EINVAL;
0092 
0093     might_sleep();
0094 
0095     if (!try_module_get(THIS_MODULE))
0096         return -ENODEV;
0097 
0098     mutex_lock(&lock);
0099 
0100     list_for_each_entry(tmp, &producers, node) {
0101         if (tmp->token == producer->token) {
0102             ret = -EBUSY;
0103             goto out_err;
0104         }
0105     }
0106 
0107     list_for_each_entry(consumer, &consumers, node) {
0108         if (consumer->token == producer->token) {
0109             ret = __connect(producer, consumer);
0110             if (ret)
0111                 goto out_err;
0112             break;
0113         }
0114     }
0115 
0116     list_add(&producer->node, &producers);
0117 
0118     mutex_unlock(&lock);
0119 
0120     return 0;
0121 out_err:
0122     mutex_unlock(&lock);
0123     module_put(THIS_MODULE);
0124     return ret;
0125 }
0126 EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
0127 
0128 /**
0129  * irq_bypass_unregister_producer - unregister IRQ bypass producer
0130  * @producer: pointer to producer structure
0131  *
0132  * Remove a previously registered IRQ producer from the list of producers
0133  * and disconnect it from any connected IRQ consumer.
0134  */
0135 void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
0136 {
0137     struct irq_bypass_producer *tmp;
0138     struct irq_bypass_consumer *consumer;
0139 
0140     if (!producer->token)
0141         return;
0142 
0143     might_sleep();
0144 
0145     if (!try_module_get(THIS_MODULE))
0146         return; /* nothing in the list anyway */
0147 
0148     mutex_lock(&lock);
0149 
0150     list_for_each_entry(tmp, &producers, node) {
0151         if (tmp->token != producer->token)
0152             continue;
0153 
0154         list_for_each_entry(consumer, &consumers, node) {
0155             if (consumer->token == producer->token) {
0156                 __disconnect(producer, consumer);
0157                 break;
0158             }
0159         }
0160 
0161         list_del(&producer->node);
0162         module_put(THIS_MODULE);
0163         break;
0164     }
0165 
0166     mutex_unlock(&lock);
0167 
0168     module_put(THIS_MODULE);
0169 }
0170 EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
0171 
0172 /**
0173  * irq_bypass_register_consumer - register IRQ bypass consumer
0174  * @consumer: pointer to consumer structure
0175  *
0176  * Add the provided IRQ consumer to the list of consumers and connect
0177  * with any matching token found on the IRQ producer list.
0178  */
0179 int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
0180 {
0181     struct irq_bypass_consumer *tmp;
0182     struct irq_bypass_producer *producer;
0183     int ret;
0184 
0185     if (!consumer->token ||
0186         !consumer->add_producer || !consumer->del_producer)
0187         return -EINVAL;
0188 
0189     might_sleep();
0190 
0191     if (!try_module_get(THIS_MODULE))
0192         return -ENODEV;
0193 
0194     mutex_lock(&lock);
0195 
0196     list_for_each_entry(tmp, &consumers, node) {
0197         if (tmp->token == consumer->token || tmp == consumer) {
0198             ret = -EBUSY;
0199             goto out_err;
0200         }
0201     }
0202 
0203     list_for_each_entry(producer, &producers, node) {
0204         if (producer->token == consumer->token) {
0205             ret = __connect(producer, consumer);
0206             if (ret)
0207                 goto out_err;
0208             break;
0209         }
0210     }
0211 
0212     list_add(&consumer->node, &consumers);
0213 
0214     mutex_unlock(&lock);
0215 
0216     return 0;
0217 out_err:
0218     mutex_unlock(&lock);
0219     module_put(THIS_MODULE);
0220     return ret;
0221 }
0222 EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
0223 
0224 /**
0225  * irq_bypass_unregister_consumer - unregister IRQ bypass consumer
0226  * @consumer: pointer to consumer structure
0227  *
0228  * Remove a previously registered IRQ consumer from the list of consumers
0229  * and disconnect it from any connected IRQ producer.
0230  */
0231 void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
0232 {
0233     struct irq_bypass_consumer *tmp;
0234     struct irq_bypass_producer *producer;
0235 
0236     if (!consumer->token)
0237         return;
0238 
0239     might_sleep();
0240 
0241     if (!try_module_get(THIS_MODULE))
0242         return; /* nothing in the list anyway */
0243 
0244     mutex_lock(&lock);
0245 
0246     list_for_each_entry(tmp, &consumers, node) {
0247         if (tmp != consumer)
0248             continue;
0249 
0250         list_for_each_entry(producer, &producers, node) {
0251             if (producer->token == consumer->token) {
0252                 __disconnect(producer, consumer);
0253                 break;
0254             }
0255         }
0256 
0257         list_del(&consumer->node);
0258         module_put(THIS_MODULE);
0259         break;
0260     }
0261 
0262     mutex_unlock(&lock);
0263 
0264     module_put(THIS_MODULE);
0265 }
0266 EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);