um: time-travel: Correct time event IRQ delivery

Lockdep (on 5.10-rc) points out that we're delivering IRQs while IRQs
are not even enabled, which clearly shouldn't happen. Defer the time
event IRQ delivery until they actually are enabled.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
This commit is contained in:
Johannes Berg 2020-12-13 22:18:18 +01:00 committed by Richard Weinberger
parent cae20ba0a1
commit 11385539c0
4 changed files with 47 additions and 0 deletions

View File

@ -40,3 +40,6 @@ DEFINE(UML_CONFIG_UML_X86, CONFIG_UML_X86);
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT); DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT);
#endif #endif
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
DEFINE(UML_CONFIG_UML_TIME_TRAVEL_SUPPORT, CONFIG_UML_TIME_TRAVEL_SUPPORT);
#endif

View File

@ -342,4 +342,7 @@ extern void unblock_signals_trace(void);
extern void um_trace_signals_on(void); extern void um_trace_signals_on(void);
extern void um_trace_signals_off(void); extern void um_trace_signals_off(void);
/* time-travel */
extern void deliver_time_travel_irqs(void);
#endif #endif

View File

@ -31,6 +31,7 @@ static bool time_travel_start_set;
static unsigned long long time_travel_start; static unsigned long long time_travel_start;
static unsigned long long time_travel_time; static unsigned long long time_travel_time;
static LIST_HEAD(time_travel_events); static LIST_HEAD(time_travel_events);
static LIST_HEAD(time_travel_irqs);
static unsigned long long time_travel_timer_interval; static unsigned long long time_travel_timer_interval;
static unsigned long long time_travel_next_event; static unsigned long long time_travel_next_event;
static struct time_travel_event time_travel_timer_event; static struct time_travel_event time_travel_timer_event;
@ -324,6 +325,35 @@ void time_travel_periodic_timer(struct time_travel_event *e)
deliver_alarm(); deliver_alarm();
} }
void deliver_time_travel_irqs(void)
{
struct time_travel_event *e;
unsigned long flags;
/*
* Don't do anything for most cases. Note that because here we have
* to disable IRQs (and re-enable later) we'll actually recurse at
* the end of the function, so this is strictly necessary.
*/
if (likely(list_empty(&time_travel_irqs)))
return;
local_irq_save(flags);
irq_enter();
while ((e = list_first_entry_or_null(&time_travel_irqs,
struct time_travel_event,
list))) {
WARN(e->time != time_travel_time,
"time moved from %lld to %lld before IRQ delivery\n",
time_travel_time, e->time);
list_del(&e->list);
e->pending = false;
e->fn(e);
}
irq_exit();
local_irq_restore(flags);
}
static void time_travel_deliver_event(struct time_travel_event *e) static void time_travel_deliver_event(struct time_travel_event *e)
{ {
if (e == &time_travel_timer_event) { if (e == &time_travel_timer_event) {
@ -332,6 +362,14 @@ static void time_travel_deliver_event(struct time_travel_event *e)
* by itself, so must handle it specially here * by itself, so must handle it specially here
*/ */
e->fn(e); e->fn(e);
} else if (irqs_disabled()) {
list_add_tail(&e->list, &time_travel_irqs);
/*
* set pending again, it was set to false when the
* event was deleted from the original list, but
* now it's still pending until we deliver the IRQ.
*/
e->pending = true;
} else { } else {
unsigned long flags; unsigned long flags;

View File

@ -271,6 +271,9 @@ void unblock_signals(void)
return; return;
signals_enabled = 1; signals_enabled = 1;
#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT
deliver_time_travel_irqs();
#endif
/* /*
* We loop because the IRQ handler returns with interrupts off. So, * We loop because the IRQ handler returns with interrupts off. So,