um: time-travel: Correct time event IRQ delivery
authorJohannes Berg <johannes.berg@intel.com>
Sun, 13 Dec 2020 21:18:18 +0000 (22:18 +0100)
committerRichard Weinberger <richard@nod.at>
Sun, 13 Dec 2020 21:42:06 +0000 (22:42 +0100)
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>
arch/um/include/shared/common-offsets.h
arch/um/include/shared/os.h
arch/um/kernel/time.c
arch/um/os-Linux/signal.c

index 4e99fe0..16a51a8 100644 (file)
@@ -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
index bc25c96..13d86f9 100644 (file)
@@ -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
index e482823..f4db89b 100644 (file)
@@ -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;
 
index 510e956..96f511d 100644 (file)
@@ -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,