Skip to content

Commit

Permalink
um: time-travel: Rewrite as an event scheduler
Browse files Browse the repository at this point in the history
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.

This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)

While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.

Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.

Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
  • Loading branch information
jmberg-intel authored and richardweinberger committed Mar 29, 2020
1 parent f185063 commit 4b786e2
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 95 deletions.
1 change: 1 addition & 0 deletions arch/um/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ config UML_TIME_TRAVEL_SUPPORT
prompt "Support time-travel mode (e.g. for test execution)"
# inf-cpu mode is incompatible with the benchmarking
depends on !RAID6_PQ_BENCHMARK
depends on !SMP
help
Enable this option to support time travel inside the UML instance.

Expand Down
65 changes: 20 additions & 45 deletions arch/um/include/linux/time-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#ifndef __TIMER_INTERNAL_H__
#define __TIMER_INTERNAL_H__
#include <linux/list.h>

#define TIMER_MULTIPLIER 256
#define TIMER_MIN_DELTA 500
Expand All @@ -16,61 +17,35 @@ enum time_travel_mode {
TT_MODE_INFCPU,
};

enum time_travel_timer_mode {
TT_TMR_DISABLED,
TT_TMR_ONESHOT,
TT_TMR_PERIODIC,
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
struct time_travel_event {
unsigned long long time;
void (*fn)(struct time_travel_event *d);
struct list_head list;
bool pending, onstack;
};

#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
extern enum time_travel_mode time_travel_mode;
extern unsigned long long time_travel_time;
extern enum time_travel_timer_mode time_travel_timer_mode;
extern unsigned long long time_travel_timer_expiry;
extern unsigned long long time_travel_timer_interval;

static inline void time_travel_set_time(unsigned long long ns)
{
time_travel_time = ns;
}
void time_travel_sleep(unsigned long long duration);

static inline void time_travel_set_timer_mode(enum time_travel_timer_mode mode)
static inline void
time_travel_set_event_fn(struct time_travel_event *e,
void (*fn)(struct time_travel_event *d))
{
time_travel_timer_mode = mode;
}

static inline void time_travel_set_timer_expiry(unsigned long long expiry)
{
time_travel_timer_expiry = expiry;
}

static inline void time_travel_set_timer_interval(unsigned long long interval)
{
time_travel_timer_interval = interval;
e->fn = fn;
}
#else
#define time_travel_mode TT_MODE_OFF
#define time_travel_time 0
#define time_travel_timer_expiry 0
#define time_travel_timer_interval 0

static inline void time_travel_set_time(unsigned long long ns)
{
}

static inline void time_travel_set_timer_mode(enum time_travel_timer_mode mode)
{
}
struct time_travel_event {
};

static inline void time_travel_set_timer_expiry(unsigned long long expiry)
{
}
#define time_travel_mode TT_MODE_OFF

static inline void time_travel_set_timer_interval(unsigned long long interval)
static inline void time_travel_sleep(unsigned long long duration)
{
}

#define time_travel_timer_mode TT_TMR_DISABLED
#endif

#endif
/* this is a macro so the event/function need not exist */
#define time_travel_set_event_fn(e, fn) do {} while (0)
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
#endif /* __TIMER_INTERNAL_H__ */
37 changes: 0 additions & 37 deletions arch/um/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,43 +203,6 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
kmalloc_ok = save_kmalloc_ok;
}

static void time_travel_sleep(unsigned long long duration)
{
unsigned long long next = time_travel_time + duration;

if (time_travel_mode != TT_MODE_INFCPU)
os_timer_disable();

while (time_travel_timer_mode == TT_TMR_PERIODIC &&
time_travel_timer_expiry < time_travel_time)
time_travel_set_timer_expiry(time_travel_timer_expiry +
time_travel_timer_interval);

if (time_travel_timer_mode != TT_TMR_DISABLED &&
time_travel_timer_expiry < next) {
if (time_travel_timer_mode == TT_TMR_ONESHOT)
time_travel_set_timer_mode(TT_TMR_DISABLED);
/*
* In basic mode, time_travel_time will be adjusted in
* the timer IRQ handler so it works even when the signal
* comes from the OS timer, see there.
*/
if (time_travel_mode != TT_MODE_BASIC)
time_travel_set_time(time_travel_timer_expiry);

deliver_alarm();
} else {
time_travel_set_time(next);
}

if (time_travel_mode != TT_MODE_INFCPU) {
if (time_travel_timer_mode == TT_TMR_PERIODIC)
os_timer_set_interval(time_travel_timer_interval);
else if (time_travel_timer_mode == TT_TMR_ONESHOT)
os_timer_one_shot(time_travel_timer_expiry - next);
}
}

static void um_idle_sleep(void)
{
unsigned long long duration = UM_NSEC_PER_SEC;
Expand Down
Loading

0 comments on commit 4b786e2

Please sign in to comment.