* [PATCH] um: time-travel: account for coalesced timer expirations
@ 2026-10-20 12:00 Hajime Tazaki
0 siblings, 0 replies; only message in thread
From: Hajime Tazaki @ 2026-10-20 12:00 UTC (permalink / raw)
To: linux-um; +Cc: Hajime Tazaki
BASIC time-travel mode uses a host POSIX timer to limit guest CPU
execution to real CPU speed. Each SIGALRM advances the time-travel clock
before the timer IRQ is delivered. Since SIGALRM is a standard signal,
multiple expirations are not queued. If signal handling is delayed, the
timer handler accounts for only one expiration and guest CLOCK_MONOTONIC
falls behind host time. Time-based tests can then exceed their requested
runtime or hit an external timeout.
LTP's inotify09 triggers this with two synchronized loops: verify_inotify()
repeatedly adds and removes an inotify watch while the write_seek() worker
writes to and rewinds the same file. The fuzzy-sync loops keep the guest
busy during the test's 150-second runtime, delaying timer handling and
allowing expirations to coalesce.
This commit fixes BASIC mode's guest clock falling behind when host timer
expirations are coalesced with timer_getoverrun() and the periodic timer
event's expiry. The cached variable time_travel_next_event holds the
earliest generic event, which may differ from the expiry associated with
SIGALRM.
Clamp the result to the current guest time to prevent it from moving
backwards.
Without the clamp, a delayed SIGALRM could move guest time backwards.
During NOMMU TT boot, mkdir triggered this panic:
Kernel panic - not syncing: time-travel: time goes backwards 275440000454 -> 275440000443
CPU: 0 UID: 0 PID: 31 Comm: mkdir Not tainted 7.3.0-rc4-g8fbb61d9e37d-dirty #1 VOLUNTARY
Stack:
00000000 00000000 00000001 607d92a1
60030d6d 706d7960 706d72b0 6003cd40
60846af0 00000000 6097b2e0 706d7338
Call Trace:
[<60030d6d>] ? freeze_kernel_threads+0x9d/0x9d
[<60040229>] show_stack+0x10e/0x11a
[<60030d6d>] ? freeze_kernel_threads+0x9d/0x9d
[<6003cd40>] dump_stack_lvl+0x63/0x7e
[<6003cd79>] dump_stack+0x1e/0x20
[<6002f62a>] vpanic+0x123/0x33f
[<6002f88d>] panic+0x47/0x47
[<6002f846>] ? vpanic+0x33f/0x33f
[<60040287>] time_travel_set_time+0x35/0x61
[<600416a8>] timer_handler+0x43/0xc1
[<60052fe6>] timer_real_alarm_handler+0x53/0x5e
[<6003ff6e>] ? um_trace_signals_on+0x5/0x5
[<600530cd>] unblock_signals.part.0+0xdc/0x12a
[<603a4250>] ? blkg_destroy+0xe5/0xe5
[<603a44d4>] ? bio_associate_blkg+0x84/0x8e
[<6043cfeb>] ? sbitmap_find_bit+0xb8/0x147
[<60040790>] ? arch_local_irq_save+0x26/0x2d
[<60040fef>] ? time_travel_del_event+0x54/0x5c
Link: https://github.com/linux-test-project/ltp/blob/master/testcases/kernel/syscalls/inotify/inotify09.c
Fixes: 4b786e24ca80 ("um: time-travel: Rewrite as an event scheduler")
Signed-off-by: Hajime Tazaki <thehajime@gmail.com>
---
arch/um/include/shared/os.h | 1 +
arch/um/kernel/time.c | 21 ++++++++++++++++++---
arch/um/os-Linux/time.c | 5 +++++
3 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index 0067c5208c4c..638dfd59c7d0 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -279,6 +279,7 @@ extern void os_idle_sleep(void);
extern int os_timer_create(void);
extern int os_timer_set_interval(int cpu, unsigned long long nsecs);
extern int os_timer_one_shot(int cpu, unsigned long long nsecs);
+extern int os_timer_get_overrun(int cpu);
extern void os_timer_disable(int cpu);
extern long long os_persistent_clock_emulation(void);
extern long long os_nsecs(void);
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index d620b6d6310a..046e21713e06 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -38,7 +38,6 @@ static unsigned long long time_travel_shm_offset;
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;
static int time_travel_ext_fd = -1;
static unsigned int time_travel_ext_waiting;
@@ -432,7 +431,6 @@ static void __time_travel_add_event(struct time_travel_event *e,
tmp = time_travel_first_event();
time_travel_ext_update_request(tmp->time);
- time_travel_next_event = tmp->time;
local_irq_restore(flags);
}
@@ -649,7 +647,24 @@ void time_travel_sleep(void)
static void time_travel_handle_real_alarm(void)
{
- time_travel_set_time(time_travel_next_event);
+ unsigned long long event_time = time_travel_timer_event.time;
+ int overrun = 0;
+
+ if (time_travel_timer_event.fn == time_travel_periodic_timer)
+ overrun = os_timer_get_overrun(raw_smp_processor_id());
+
+ /* SIGALRM is not queued, so account for coalesced timer expirations. */
+ if (overrun > 0)
+ event_time += overrun * time_travel_timer_interval;
+
+ /*
+ * A clock read may have advanced BASIC mode beyond the event while the
+ * signal was blocked. Never move the time-travel clock backwards.
+ */
+ if (event_time < time_travel_time)
+ event_time = time_travel_time;
+
+ time_travel_set_time(event_time);
time_travel_del_event(&time_travel_timer_event);
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c
index 13ebc86918d4..ecd6e5e183fe 100644
--- a/arch/um/os-Linux/time.c
+++ b/arch/um/os-Linux/time.c
@@ -88,6 +88,11 @@ int os_timer_one_shot(int cpu, unsigned long long nsecs)
return 0;
}
+int os_timer_get_overrun(int cpu)
+{
+ return timer_getoverrun(event_high_res_timer[cpu]);
+}
+
/**
* os_timer_disable() - disable the posix (interval) timer
* @cpu: the CPU for which the timer is to be disabled
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-20 12:00 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-20 12:00 [PATCH] um: time-travel: account for coalesced timer expirations Hajime Tazaki
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox