mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
irq: add per-IRQ observer to fix qemu_irq_intercept_in leak
qemu_irq_intercept_in() saves original IRQ handlers by allocating new QOM objects, which are never freed. On a PC machine, this leaks IRQ objects (one per IOAPIC pin) on every qtest run. Rather than tracking allocations to free later, avoid them: add an "observer" field to IRQState, called by qemu_set_irq() after the real handler. Interception sets the observer instead of rewriting handlers, so there's nothing to save and nothing to leak. Fix qemu_notirq() to route through qemu_set_irq() so inverted IRQs trigger observers too. Drop the LSan suppression. Reviewed-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260623-b4-ui-v4-3-4656aec3398d@redhat.com>
This commit is contained in:
@@ -32,6 +32,9 @@ void qemu_set_irq(qemu_irq irq, int level)
|
||||
return;
|
||||
|
||||
irq->handler(irq->opaque, irq->n, level);
|
||||
if (unlikely(irq->observer)) {
|
||||
irq->observer(irq->opaque, irq->n, level);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_irq_fields(IRQState *irq, qemu_irq_handler handler,
|
||||
@@ -111,7 +114,7 @@ static void qemu_notirq(void *opaque, int line, int level)
|
||||
{
|
||||
IRQState *irq = opaque;
|
||||
|
||||
irq->handler(irq->opaque, irq->n, !level);
|
||||
qemu_set_irq(irq, !level);
|
||||
}
|
||||
|
||||
qemu_irq qemu_irq_invert(qemu_irq irq)
|
||||
@@ -121,14 +124,11 @@ qemu_irq qemu_irq_invert(qemu_irq irq)
|
||||
return qemu_allocate_irq(qemu_notirq, irq, 0);
|
||||
}
|
||||
|
||||
void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
|
||||
void qemu_irq_set_observer(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
|
||||
{
|
||||
int i;
|
||||
qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
*old_irqs[i] = *gpio_in[i];
|
||||
gpio_in[i]->handler = handler;
|
||||
gpio_in[i]->opaque = &old_irqs[i];
|
||||
gpio_in[i]->observer = handler;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ struct IRQState {
|
||||
qemu_irq_handler handler;
|
||||
void *opaque;
|
||||
int n;
|
||||
qemu_irq_handler observer;
|
||||
};
|
||||
|
||||
void qemu_set_irq(qemu_irq irq, int level);
|
||||
@@ -96,9 +97,8 @@ void qemu_free_irq(qemu_irq irq);
|
||||
/* Returns a new IRQ with opposite polarity. */
|
||||
qemu_irq qemu_irq_invert(qemu_irq irq);
|
||||
|
||||
/* For internal use in qtest. Similar to qemu_irq_split, but operating
|
||||
on an existing vector of qemu_irq. */
|
||||
void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n);
|
||||
/* For internal use in qtest. */
|
||||
void qemu_irq_set_observer(qemu_irq *gpio_in, qemu_irq_handler handler, int n);
|
||||
|
||||
/**
|
||||
* qemu_irq_is_connected: Return true if IRQ line is wired up
|
||||
|
||||
@@ -16,11 +16,3 @@ leak:/lib64/libxkbcommon.so.0
|
||||
# https://github.com/GNOME/glib/blob/main/tools/glib.supp
|
||||
# This avoids false positive leak reports for the qga-ssh-test.
|
||||
leak:g_set_user_dirs
|
||||
|
||||
# qemu_irq_intercept_in is only used by the qtest harness, and
|
||||
# its API inherently involves a leak.
|
||||
# While we could keep track of the old IRQ data structure
|
||||
# in order to free it, it doesn't seem very important to fix
|
||||
# since it is only used by the qtest test harness.
|
||||
# Just ignore the leak, at least for the moment.
|
||||
leak:qemu_irq_intercept_in
|
||||
|
||||
@@ -326,9 +326,6 @@ void qtest_sendf(CharFrontend *chr, const char *fmt, ...)
|
||||
|
||||
static void qtest_irq_handler(void *opaque, int n, int level)
|
||||
{
|
||||
qemu_irq old_irq = *(qemu_irq *)opaque;
|
||||
qemu_set_irq(old_irq, level);
|
||||
|
||||
if (irq_levels[n] != level) {
|
||||
CharFrontend *chr = &qtest->qtest_chr;
|
||||
irq_levels[n] = level;
|
||||
@@ -421,7 +418,7 @@ static void qtest_process_command(CharFrontend *chr, gchar **words)
|
||||
interception_succeeded = true;
|
||||
}
|
||||
} else {
|
||||
qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
|
||||
qemu_irq_set_observer(ngl->in, qtest_irq_handler,
|
||||
ngl->num_in);
|
||||
interception_succeeded = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user