Commit 11385539 authored by Johannes Berg's avatar Johannes Berg Committed by Richard Weinberger
Browse files

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: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent cae20ba0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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
+38 −0
Original line number Diff line number Diff line
@@ -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;

+3 −0
Original line number Diff line number Diff line
@@ -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,