Skip to content

Commit 8394b32

Browse files
committed
irqbypass: Use xarray to track producers and consumers
Track IRQ bypass producers and consumers using an xarray to avoid the O(2n) insertion time associated with walking a list to check for duplicate entries, and to search for an partner. At low (tens or few hundreds) total producer/consumer counts, using a list is faster due to the need to allocate backing storage for xarray. But as count creeps into the thousands, xarray wins easily, and can provide several orders of magnitude better latency at high counts. E.g. hundreds of nanoseconds vs. hundreds of milliseconds. Cc: Oliver Upton <oliver.upton@linux.dev> Cc: David Matlack <dmatlack@google.com> Cc: Like Xu <like.xu.linux@gmail.com> Cc: Binbin Wu <binbin.wu@linux.intel.com> Reported-by: Yong He <alexyonghe@tencent.com> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217379 Link: https://lore.kernel.org/all/20230801115646.33990-1-likexu@tencent.com Reviewed-by: Kevin Tian <kevin.tian@intel.com> Acked-by: Michael S. Tsirkin <mst@redhat.com> Reviewed-by: Alex Williamson <alex.williamson@redhat.com> Link: https://lore.kernel.org/r/20250516230734.2564775-8-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 46a4bfd commit 8394b32

File tree

2 files changed

+38
-40
lines changed

2 files changed

+38
-40
lines changed

include/linux/irqbypass.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ struct irq_bypass_consumer;
3333

3434
/**
3535
* struct irq_bypass_producer - IRQ bypass producer definition
36-
* @node: IRQ bypass manager private list management
3736
* @eventfd: eventfd context used to match producers and consumers
3837
* @consumer: The connected consumer (NULL if no connection)
3938
* @irq: Linux IRQ number for the producer device
@@ -47,7 +46,6 @@ struct irq_bypass_consumer;
4746
* for a physical device assigned to a VM.
4847
*/
4948
struct irq_bypass_producer {
50-
struct list_head node;
5149
struct eventfd_ctx *eventfd;
5250
struct irq_bypass_consumer *consumer;
5351
int irq;
@@ -61,7 +59,6 @@ struct irq_bypass_producer {
6159

6260
/**
6361
* struct irq_bypass_consumer - IRQ bypass consumer definition
64-
* @node: IRQ bypass manager private list management
6562
* @eventfd: eventfd context used to match producers and consumers
6663
* @producer: The connected producer (NULL if no connection)
6764
* @add_producer: Connect the IRQ consumer to an IRQ producer
@@ -75,7 +72,6 @@ struct irq_bypass_producer {
7572
* portions of the interrupt handling to the VM.
7673
*/
7774
struct irq_bypass_consumer {
78-
struct list_head node;
7975
struct eventfd_ctx *eventfd;
8076
struct irq_bypass_producer *producer;
8177

virt/lib/irqbypass.c

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
MODULE_LICENSE("GPL v2");
2323
MODULE_DESCRIPTION("IRQ bypass manager utility module");
2424

25-
static LIST_HEAD(producers);
26-
static LIST_HEAD(consumers);
25+
static DEFINE_XARRAY(producers);
26+
static DEFINE_XARRAY(consumers);
2727
static DEFINE_MUTEX(lock);
2828

2929
/* @lock must be held when calling connect */
@@ -86,13 +86,13 @@ static void __disconnect(struct irq_bypass_producer *prod,
8686
* @producer: pointer to producer structure
8787
* @eventfd: pointer to the eventfd context associated with the producer
8888
*
89-
* Add the provided IRQ producer to the list of producers and connect
90-
* with any matching eventfd found on the IRQ consumers list.
89+
* Add the provided IRQ producer to the set of producers and connect with the
90+
* consumer with a matching eventfd, if one exists.
9191
*/
9292
int irq_bypass_register_producer(struct irq_bypass_producer *producer,
9393
struct eventfd_ctx *eventfd)
9494
{
95-
struct irq_bypass_producer *tmp;
95+
unsigned long index = (unsigned long)eventfd;
9696
struct irq_bypass_consumer *consumer;
9797
int ret;
9898

@@ -101,22 +101,20 @@ int irq_bypass_register_producer(struct irq_bypass_producer *producer,
101101

102102
guard(mutex)(&lock);
103103

104-
list_for_each_entry(tmp, &producers, node) {
105-
if (tmp->eventfd == eventfd)
106-
return -EBUSY;
107-
}
104+
ret = xa_insert(&producers, index, producer, GFP_KERNEL);
105+
if (ret)
106+
return ret;
108107

109-
list_for_each_entry(consumer, &consumers, node) {
110-
if (consumer->eventfd == eventfd) {
111-
ret = __connect(producer, consumer);
112-
if (ret)
113-
return ret;
114-
break;
108+
consumer = xa_load(&consumers, index);
109+
if (consumer) {
110+
ret = __connect(producer, consumer);
111+
if (ret) {
112+
WARN_ON_ONCE(xa_erase(&producers, index) != producer);
113+
return ret;
115114
}
116115
}
117116

118117
producer->eventfd = eventfd;
119-
list_add(&producer->node, &producers);
120118
return 0;
121119
}
122120
EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
@@ -125,11 +123,14 @@ EXPORT_SYMBOL_GPL(irq_bypass_register_producer);
125123
* irq_bypass_unregister_producer - unregister IRQ bypass producer
126124
* @producer: pointer to producer structure
127125
*
128-
* Remove a previously registered IRQ producer from the list of producers
129-
* and disconnect it from any connected IRQ consumer.
126+
* Remove a previously registered IRQ producer (note, it's safe to call this
127+
* even if registration was unsuccessful). Disconnect from the associated
128+
* consumer, if one exists.
130129
*/
131130
void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
132131
{
132+
unsigned long index = (unsigned long)producer->eventfd;
133+
133134
if (!producer->eventfd)
134135
return;
135136

@@ -138,8 +139,8 @@ void irq_bypass_unregister_producer(struct irq_bypass_producer *producer)
138139
if (producer->consumer)
139140
__disconnect(producer, producer->consumer);
140141

142+
WARN_ON_ONCE(xa_erase(&producers, index) != producer);
141143
producer->eventfd = NULL;
142-
list_del(&producer->node);
143144
}
144145
EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
145146

@@ -148,13 +149,13 @@ EXPORT_SYMBOL_GPL(irq_bypass_unregister_producer);
148149
* @consumer: pointer to consumer structure
149150
* @eventfd: pointer to the eventfd context associated with the consumer
150151
*
151-
* Add the provided IRQ consumer to the list of consumers and connect
152-
* with any matching eventfd found on the IRQ producer list.
152+
* Add the provided IRQ consumer to the set of consumers and connect with the
153+
* producer with a matching eventfd, if one exists.
153154
*/
154155
int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer,
155156
struct eventfd_ctx *eventfd)
156157
{
157-
struct irq_bypass_consumer *tmp;
158+
unsigned long index = (unsigned long)eventfd;
158159
struct irq_bypass_producer *producer;
159160
int ret;
160161

@@ -166,22 +167,20 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer,
166167

167168
guard(mutex)(&lock);
168169

169-
list_for_each_entry(tmp, &consumers, node) {
170-
if (tmp->eventfd == eventfd)
171-
return -EBUSY;
172-
}
170+
ret = xa_insert(&consumers, index, consumer, GFP_KERNEL);
171+
if (ret)
172+
return ret;
173173

174-
list_for_each_entry(producer, &producers, node) {
175-
if (producer->eventfd == eventfd) {
176-
ret = __connect(producer, consumer);
177-
if (ret)
178-
return ret;
179-
break;
174+
producer = xa_load(&producers, index);
175+
if (producer) {
176+
ret = __connect(producer, consumer);
177+
if (ret) {
178+
WARN_ON_ONCE(xa_erase(&consumers, index) != consumer);
179+
return ret;
180180
}
181181
}
182182

183183
consumer->eventfd = eventfd;
184-
list_add(&consumer->node, &consumers);
185184
return 0;
186185
}
187186
EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
@@ -190,11 +189,14 @@ EXPORT_SYMBOL_GPL(irq_bypass_register_consumer);
190189
* irq_bypass_unregister_consumer - unregister IRQ bypass consumer
191190
* @consumer: pointer to consumer structure
192191
*
193-
* Remove a previously registered IRQ consumer from the list of consumers
194-
* and disconnect it from any connected IRQ producer.
192+
* Remove a previously registered IRQ consumer (note, it's safe to call this
193+
* even if registration was unsuccessful). Disconnect from the associated
194+
* producer, if one exists.
195195
*/
196196
void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
197197
{
198+
unsigned long index = (unsigned long)consumer->eventfd;
199+
198200
if (!consumer->eventfd)
199201
return;
200202

@@ -203,7 +205,7 @@ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
203205
if (consumer->producer)
204206
__disconnect(consumer->producer, consumer);
205207

208+
WARN_ON_ONCE(xa_erase(&consumers, index) != consumer);
206209
consumer->eventfd = NULL;
207-
list_del(&consumer->node);
208210
}
209211
EXPORT_SYMBOL_GPL(irq_bypass_unregister_consumer);

0 commit comments

Comments
 (0)