irqbypass.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. #include <linux/irqbypass.h>
  17. #include <linux/list.h>
  18. #include <linux/module.h>
  19. #include <linux/mutex.h>
  20. MODULE_LICENSE("GPL v2");
  21. MODULE_DESCRIPTION("IRQ bypass manager utility module");
  22. static DEFINE_XARRAY(producers);
  23. static DEFINE_XARRAY(consumers);
  24. static DEFINE_MUTEX(lock);
  25. /* @lock must be held when calling connect */
  26. static int __connect(struct irq_bypass_producer *prod,
  27. struct irq_bypass_consumer *cons)
  28. {
  29. int ret = 0;
  30. if (prod->stop)
  31. prod->stop(prod);
  32. if (cons->stop)
  33. cons->stop(cons);
  34. if (prod->add_consumer)
  35. ret = prod->add_consumer(prod, cons);
  36. if (!ret) {
  37. ret = cons->add_producer(cons, prod);
  38. if (ret && prod->del_consumer)
  39. prod->del_consumer(prod, cons);
  40. }
  41. if (cons->start)
  42. cons->start(cons);
  43. if (prod->start)
  44. prod->start(prod);
  45. if (!ret) {
  46. prod->consumer = cons;
  47. cons->producer = prod;
  48. }
  49. return ret;
  50. }
  51. /* @lock must be held when calling disconnect */
  52. static void __disconnect(struct irq_bypass_producer *prod,
  53. struct irq_bypass_consumer *cons)
  54. {
  55. if (prod->stop)
  56. prod->stop(prod);
  57. if (cons->stop)
  58. cons->stop(cons);
  59. cons->del_producer(cons, prod);
  60. if (prod->del_consumer)
  61. prod->del_consumer(prod, cons);
  62. if (cons->start)
  63. cons->start(cons);
  64. if (prod->start)
  65. prod->start(prod);
  66. prod->consumer = NULL;
  67. cons->producer = NULL;
  68. }
  69. /**
  70. * irq_bypass_register_producer - register IRQ bypass producer
  71. * @producer: pointer to producer structure
  72. * @eventfd: pointer to the eventfd context associated with the producer
  73. * @irq: Linux IRQ number of the underlying producer device
  74. *
  75. * Add the provided IRQ producer to the set of producers and connect with the
  76. * consumer with a matching eventfd, if one exists.
  77. */
  78. int irq_bypass_register_producer(struct irq_bypass_producer *producer,
  79. struct eventfd_ctx *eventfd, int irq)
  80. {
  81. unsigned long index = (unsigned long)eventfd;
  82. struct irq_bypass_consumer *consumer;
  83. int ret;
  84. if (WARN_ON_ONCE(producer->eventfd))
  85. return -EINVAL;
  86. producer->irq = irq;
  87. guard(mutex)(&lock);
  88. ret = xa_insert(&producers, index, producer, GFP_KERNEL);
  89. if (ret)
  90. return ret;
  91. consumer = xa_load(&consumers, index);
  92. if (consumer) {
  93. ret = __connect(producer, consumer);
  94. if (ret) {
  95. WARN_ON_ONCE(xa_erase(&producers, index) != producer);
  96. return ret;
  97. }
  98. }
  99. producer->eventfd = eventfd;
  100. return 0;
  101. }
  102. EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
  103. /**
  104. * irq_bypass_unregister_producer - unregister IRQ bypass producer
  105. * @producer: pointer to producer structure
  106. *
  107. * Remove a previously registered IRQ producer (note, it's safe to call this
  108. * even if registration was unsuccessful). Disconnect from the associated
  109. * consumer, if one exists.
  110. */
  111. void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
  112. {
  113. unsigned long index = (unsigned long)producer->eventfd;
  114. if (!producer->eventfd)
  115. return;
  116. guard(mutex)(&lock);
  117. if (producer->consumer)
  118. __disconnect(producer, producer->consumer);
  119. WARN_ON_ONCE(xa_erase(&producers, index) != producer);
  120. producer->eventfd = NULL;
  121. }
  122. EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
  123. /**
  124. * irq_bypass_register_consumer - register IRQ bypass consumer
  125. * @consumer: pointer to consumer structure
  126. * @eventfd: pointer to the eventfd context associated with the consumer
  127. *
  128. * Add the provided IRQ consumer to the set of consumers and connect with the
  129. * producer with a matching eventfd, if one exists.
  130. */
  131. int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer,
  132. struct eventfd_ctx *eventfd)
  133. {
  134. unsigned long index = (unsigned long)eventfd;
  135. struct irq_bypass_producer *producer;
  136. int ret;
  137. if (WARN_ON_ONCE(consumer->eventfd))
  138. return -EINVAL;
  139. if (!consumer->add_producer || !consumer->del_producer)
  140. return -EINVAL;
  141. guard(mutex)(&lock);
  142. ret = xa_insert(&consumers, index, consumer, GFP_KERNEL);
  143. if (ret)
  144. return ret;
  145. producer = xa_load(&producers, index);
  146. if (producer) {
  147. ret = __connect(producer, consumer);
  148. if (ret) {
  149. WARN_ON_ONCE(xa_erase(&consumers, index) != consumer);
  150. return ret;
  151. }
  152. }
  153. consumer->eventfd = eventfd;
  154. return 0;
  155. }
  156. EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
  157. /**
  158. * irq_bypass_unregister_consumer - unregister IRQ bypass consumer
  159. * @consumer: pointer to consumer structure
  160. *
  161. * Remove a previously registered IRQ consumer (note, it's safe to call this
  162. * even if registration was unsuccessful). Disconnect from the associated
  163. * producer, if one exists.
  164. */
  165. void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
  166. {
  167. unsigned long index = (unsigned long)consumer->eventfd;
  168. if (!consumer->eventfd)
  169. return;
  170. guard(mutex)(&lock);
  171. if (consumer->producer)
  172. __disconnect(consumer->producer, consumer);
  173. WARN_ON_ONCE(xa_erase(&consumers, index) != consumer);
  174. consumer->eventfd = NULL;
  175. }
  176. EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);