root/virt/lib/irqbypass.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __connect
  2. __disconnect
  3. irq_bypass_register_producer
  4. irq_bypass_unregister_producer
  5. irq_bypass_register_consumer
  6. irq_bypass_unregister_consumer

   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                 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 */
  58 static 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 producer
  79  * @producer: pointer to producer structure
  80  *
  81  * Add the provided IRQ producer to the list of producers and connect
  82  * with any matching token found on the IRQ consumers list.
  83  */
  84 int irq_bypass_register_producer(struct irq_bypass_producer *producer)
  85 {
  86         struct irq_bypass_producer *tmp;
  87         struct irq_bypass_consumer *consumer;
  88 
  89         if (!producer->token)
  90                 return -EINVAL;
  91 
  92         might_sleep();
  93 
  94         if (!try_module_get(THIS_MODULE))
  95                 return -ENODEV;
  96 
  97         mutex_lock(&lock);
  98 
  99         list_for_each_entry(tmp, &producers, node) {
 100                 if (tmp->token == producer->token) {
 101                         mutex_unlock(&lock);
 102                         module_put(THIS_MODULE);
 103                         return -EBUSY;
 104                 }
 105         }
 106 
 107         list_for_each_entry(consumer, &consumers, node) {
 108                 if (consumer->token == producer->token) {
 109                         int ret = __connect(producer, consumer);
 110                         if (ret) {
 111                                 mutex_unlock(&lock);
 112                                 module_put(THIS_MODULE);
 113                                 return ret;
 114                         }
 115                         break;
 116                 }
 117         }
 118 
 119         list_add(&producer->node, &producers);
 120 
 121         mutex_unlock(&lock);
 122 
 123         return 0;
 124 }
 125 EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
 126 
 127 /**
 128  * irq_bypass_unregister_producer - unregister IRQ bypass producer
 129  * @producer: pointer to producer structure
 130  *
 131  * Remove a previously registered IRQ producer from the list of producers
 132  * and disconnect it from any connected IRQ consumer.
 133  */
 134 void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
 135 {
 136         struct irq_bypass_producer *tmp;
 137         struct irq_bypass_consumer *consumer;
 138 
 139         if (!producer->token)
 140                 return;
 141 
 142         might_sleep();
 143 
 144         if (!try_module_get(THIS_MODULE))
 145                 return; /* nothing in the list anyway */
 146 
 147         mutex_lock(&lock);
 148 
 149         list_for_each_entry(tmp, &producers, node) {
 150                 if (tmp->token != producer->token)
 151                         continue;
 152 
 153                 list_for_each_entry(consumer, &consumers, node) {
 154                         if (consumer->token == producer->token) {
 155                                 __disconnect(producer, consumer);
 156                                 break;
 157                         }
 158                 }
 159 
 160                 list_del(&producer->node);
 161                 module_put(THIS_MODULE);
 162                 break;
 163         }
 164 
 165         mutex_unlock(&lock);
 166 
 167         module_put(THIS_MODULE);
 168 }
 169 EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
 170 
 171 /**
 172  * irq_bypass_register_consumer - register IRQ bypass consumer
 173  * @consumer: pointer to consumer structure
 174  *
 175  * Add the provided IRQ consumer to the list of consumers and connect
 176  * with any matching token found on the IRQ producer list.
 177  */
 178 int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
 179 {
 180         struct irq_bypass_consumer *tmp;
 181         struct irq_bypass_producer *producer;
 182 
 183         if (!consumer->token ||
 184             !consumer->add_producer || !consumer->del_producer)
 185                 return -EINVAL;
 186 
 187         might_sleep();
 188 
 189         if (!try_module_get(THIS_MODULE))
 190                 return -ENODEV;
 191 
 192         mutex_lock(&lock);
 193 
 194         list_for_each_entry(tmp, &consumers, node) {
 195                 if (tmp->token == consumer->token || tmp == consumer) {
 196                         mutex_unlock(&lock);
 197                         module_put(THIS_MODULE);
 198                         return -EBUSY;
 199                 }
 200         }
 201 
 202         list_for_each_entry(producer, &producers, node) {
 203                 if (producer->token == consumer->token) {
 204                         int ret = __connect(producer, consumer);
 205                         if (ret) {
 206                                 mutex_unlock(&lock);
 207                                 module_put(THIS_MODULE);
 208                                 return ret;
 209                         }
 210                         break;
 211                 }
 212         }
 213 
 214         list_add(&consumer->node, &consumers);
 215 
 216         mutex_unlock(&lock);
 217 
 218         return 0;
 219 }
 220 EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
 221 
 222 /**
 223  * irq_bypass_unregister_consumer - unregister IRQ bypass consumer
 224  * @consumer: pointer to consumer structure
 225  *
 226  * Remove a previously registered IRQ consumer from the list of consumers
 227  * and disconnect it from any connected IRQ producer.
 228  */
 229 void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
 230 {
 231         struct irq_bypass_consumer *tmp;
 232         struct irq_bypass_producer *producer;
 233 
 234         if (!consumer->token)
 235                 return;
 236 
 237         might_sleep();
 238 
 239         if (!try_module_get(THIS_MODULE))
 240                 return; /* nothing in the list anyway */
 241 
 242         mutex_lock(&lock);
 243 
 244         list_for_each_entry(tmp, &consumers, node) {
 245                 if (tmp != consumer)
 246                         continue;
 247 
 248                 list_for_each_entry(producer, &producers, node) {
 249                         if (producer->token == consumer->token) {
 250                                 __disconnect(producer, consumer);
 251                                 break;
 252                         }
 253                 }
 254 
 255                 list_del(&consumer->node);
 256                 module_put(THIS_MODULE);
 257                 break;
 258         }
 259 
 260         mutex_unlock(&lock);
 261 
 262         module_put(THIS_MODULE);
 263 }
 264 EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);

/* [<][>][^][v][top][bottom][index][help] */