From 11385539c024b6071dce538123a2043a8f52c9a1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 13 Dec 2020 22:18:18 +0100 Subject: [PATCH] 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 Signed-off-by: Richard Weinberger --- arch/um/include/shared/common-offsets.h | 3 ++ arch/um/include/shared/os.h | 3 ++ arch/um/kernel/time.c | 38 +++++++++++++++++++++++++ arch/um/os-Linux/signal.c | 3 ++ 4 files changed, 47 insertions(+) diff --git a/arch/um/include/shared/common-offsets.h b/arch/um/include/shared/common-offsets.h index 4e99fe05576a..16a51a8c800f 100644 --- a/arch/um/include/shared/common-offsets.h +++ b/arch/um/include/shared/common-offsets.h @@ -40,3 +40,6 @@ DEFINE(UML_CONFIG_UML_X86, CONFIG_UML_X86); #ifdef CONFIG_64BIT DEFINE(UML_CONFIG_64BIT, CONFIG_64BIT); #endif +#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT +DEFINE(UML_CONFIG_UML_TIME_TRAVEL_SUPPORT, CONFIG_UML_TIME_TRAVEL_SUPPORT); +#endif diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index bc25c960f7e0..13d86f94cf0f 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -342,4 +342,7 @@ extern void unblock_signals_trace(void); extern void um_trace_signals_on(void); extern void um_trace_signals_off(void); +/* time-travel */ +extern void deliver_time_travel_irqs(void); + #endif diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index e48282308126..f4db89b5b5a6 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -31,6 +31,7 @@ static bool time_travel_start_set; static unsigned long long time_travel_start; static unsigned long long time_travel_time; 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_next_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(); } +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) { 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 */ 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 { unsigned long flags; diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 510e956b4320..96f511d1aabe 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -271,6 +271,9 @@ void unblock_signals(void) return; 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, -- 2.39.2