Commit c140a5bd authored by Benjamin Berg's avatar Benjamin Berg Committed by Johannes Berg

um: irqs: process outstanding IRQs when unblocking signals

When in time-travel mode, the eventfd events are read even when signals
are blocked as SIGIO still needs to be processed. In this case, the
event is cleared on the eventfd but the IRQ still needs to be fired
later.

We did already ensure that the SIGIO handler is run again. However, the
FDs are configured to be level triggered, so that eventfd will not
notify again. As such, add some logic to mark the IRQ as pending and
process it at the next opportunity.

To avoid duplication, reuse the logic used for the suspend/resume case.
This does not really change anything except for delaying running the
IRQs with timetravel_handler at a slightly later point in time (and
possibly running non-timetravel IRQs that shouldn't happen earlier).
While at it, move marking as pending into irq_event_handler as that is
the more logical place for it to happen.
Signed-off-by: default avatarBenjamin Berg <benjamin.berg@intel.com>
Link: https://patch.msgid.link/20231018123643.1255813-1-benjamin@sipsolutions.netSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 22a40d14
...@@ -37,7 +37,7 @@ struct irq_reg { ...@@ -37,7 +37,7 @@ struct irq_reg {
bool pending; bool pending;
bool wakeup; bool wakeup;
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
bool pending_on_resume; bool pending_event;
void (*timetravel_handler)(int, int, void *, void (*timetravel_handler)(int, int, void *,
struct time_travel_event *); struct time_travel_event *);
struct time_travel_event event; struct time_travel_event event;
...@@ -56,6 +56,9 @@ static DEFINE_SPINLOCK(irq_lock); ...@@ -56,6 +56,9 @@ static DEFINE_SPINLOCK(irq_lock);
static LIST_HEAD(active_fds); static LIST_HEAD(active_fds);
static DECLARE_BITMAP(irqs_allocated, UM_LAST_SIGNAL_IRQ); static DECLARE_BITMAP(irqs_allocated, UM_LAST_SIGNAL_IRQ);
static bool irqs_suspended; static bool irqs_suspended;
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
static bool irqs_pending;
#endif
static void irq_io_loop(struct irq_reg *irq, struct uml_pt_regs *regs) static void irq_io_loop(struct irq_reg *irq, struct uml_pt_regs *regs)
{ {
...@@ -84,9 +87,12 @@ static void irq_event_handler(struct time_travel_event *ev) ...@@ -84,9 +87,12 @@ static void irq_event_handler(struct time_travel_event *ev)
{ {
struct irq_reg *reg = container_of(ev, struct irq_reg, event); struct irq_reg *reg = container_of(ev, struct irq_reg, event);
/* do nothing if suspended - just to cause a wakeup */ /* do nothing if suspended; just cause a wakeup and mark as pending */
if (irqs_suspended) if (irqs_suspended) {
irqs_pending = true;
reg->pending_event = true;
return; return;
}
generic_handle_irq(reg->irq); generic_handle_irq(reg->irq);
} }
...@@ -110,16 +116,47 @@ static bool irq_do_timetravel_handler(struct irq_entry *entry, ...@@ -110,16 +116,47 @@ static bool irq_do_timetravel_handler(struct irq_entry *entry,
if (!reg->event.pending) if (!reg->event.pending)
return false; return false;
if (irqs_suspended)
reg->pending_on_resume = true;
return true; return true;
} }
static void irq_do_pending_events(bool timetravel_handlers_only)
{
struct irq_entry *entry;
if (!irqs_pending || timetravel_handlers_only)
return;
irqs_pending = false;
list_for_each_entry(entry, &active_fds, list) {
enum um_irq_type t;
for (t = 0; t < NUM_IRQ_TYPES; t++) {
struct irq_reg *reg = &entry->reg[t];
/*
* Any timetravel_handler was invoked already, just
* directly run the IRQ.
*/
if (reg->pending_event) {
irq_enter();
generic_handle_irq(reg->irq);
irq_exit();
reg->pending_event = false;
}
}
}
}
#else #else
static bool irq_do_timetravel_handler(struct irq_entry *entry, static bool irq_do_timetravel_handler(struct irq_entry *entry,
enum um_irq_type t) enum um_irq_type t)
{ {
return false; return false;
} }
static void irq_do_pending_events(bool timetravel_handlers_only)
{
}
#endif #endif
static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type t, static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type t,
...@@ -145,6 +182,8 @@ static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type ...@@ -145,6 +182,8 @@ static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type
*/ */
if (timetravel_handlers_only) { if (timetravel_handlers_only) {
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
reg->pending_event = true;
irqs_pending = true;
mark_sigio_pending(); mark_sigio_pending();
#endif #endif
return; return;
...@@ -162,6 +201,10 @@ static void _sigio_handler(struct uml_pt_regs *regs, ...@@ -162,6 +201,10 @@ static void _sigio_handler(struct uml_pt_regs *regs,
if (timetravel_handlers_only && !um_irq_timetravel_handler_used()) if (timetravel_handlers_only && !um_irq_timetravel_handler_used())
return; return;
/* Flush out pending events that were ignored due to time-travel. */
if (!irqs_suspended)
irq_do_pending_events(timetravel_handlers_only);
while (1) { while (1) {
/* This is now lockless - epoll keeps back-referencesto the irqs /* This is now lockless - epoll keeps back-referencesto the irqs
* which have trigger it so there is no need to walk the irq * which have trigger it so there is no need to walk the irq
...@@ -543,30 +586,7 @@ void um_irqs_resume(void) ...@@ -543,30 +586,7 @@ void um_irqs_resume(void)
unsigned long flags; unsigned long flags;
local_irq_save(flags); spin_lock_irqsave(&irq_lock, flags);
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
/*
* We don't need to lock anything here since we're in resume
* and nothing else is running, but have disabled IRQs so we
* don't try anything else with the interrupt list from there.
*/
list_for_each_entry(entry, &active_fds, list) {
enum um_irq_type t;
for (t = 0; t < NUM_IRQ_TYPES; t++) {
struct irq_reg *reg = &entry->reg[t];
if (reg->pending_on_resume) {
irq_enter();
generic_handle_irq(reg->irq);
irq_exit();
reg->pending_on_resume = false;
}
}
}
#endif
spin_lock(&irq_lock);
list_for_each_entry(entry, &active_fds, list) { list_for_each_entry(entry, &active_fds, list) {
if (entry->suspended) { if (entry->suspended) {
int err = os_set_fd_async(entry->fd); int err = os_set_fd_async(entry->fd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment