summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/um/Kconfig12
-rw-r--r--arch/um/include/shared/timer-internal.h46
-rw-r--r--arch/um/kernel/process.c42
-rw-r--r--arch/um/kernel/skas/syscall.c11
-rw-r--r--arch/um/kernel/time.c129
5 files changed, 233 insertions, 7 deletions
diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 6b6eb938fcc1..3c3adfc486f2 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -184,6 +184,18 @@ config SECCOMP
184 184
185 If unsure, say Y. 185 If unsure, say Y.
186 186
187config UML_TIME_TRAVEL_SUPPORT
188 bool
189 prompt "Support time-travel mode (e.g. for test execution)"
190 help
191 Enable this option to support time travel inside the UML instance.
192
193 After enabling this option, two modes are accessible at runtime
194 (selected by the kernel command line), see the kernel's command-
195 line help for more details.
196
197 It is safe to say Y, but you probably don't need this.
198
187endmenu 199endmenu
188 200
189source "arch/um/drivers/Kconfig" 201source "arch/um/drivers/Kconfig"
diff --git a/arch/um/include/shared/timer-internal.h b/arch/um/include/shared/timer-internal.h
index 03e6f217f807..5ca74f415d52 100644
--- a/arch/um/include/shared/timer-internal.h
+++ b/arch/um/include/shared/timer-internal.h
@@ -10,4 +10,50 @@
10#define TIMER_MULTIPLIER 256 10#define TIMER_MULTIPLIER 256
11#define TIMER_MIN_DELTA 500 11#define TIMER_MIN_DELTA 500
12 12
13enum time_travel_mode {
14 TT_MODE_OFF,
15 TT_MODE_BASIC,
16 TT_MODE_INFCPU,
17};
18
19enum time_travel_timer_mode {
20 TT_TMR_DISABLED,
21 TT_TMR_ONESHOT,
22 TT_TMR_PERIODIC,
23};
24
25#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
26extern enum time_travel_mode time_travel_mode;
27extern unsigned long long time_travel_time;
28extern enum time_travel_timer_mode time_travel_timer_mode;
29extern unsigned long long time_travel_timer_expiry;
30extern unsigned long long time_travel_timer_interval;
31
32static inline void time_travel_set_time(unsigned long long ns)
33{
34 time_travel_time = ns;
35}
36
37static inline void time_travel_set_timer(enum time_travel_timer_mode mode,
38 unsigned long long expiry)
39{
40 time_travel_timer_mode = mode;
41 time_travel_timer_expiry = expiry;
42}
43#else
44#define time_travel_mode TT_MODE_OFF
45#define time_travel_time 0
46#define time_travel_timer_expiry 0
47#define time_travel_timer_interval 0
48
49static inline void time_travel_set_time(unsigned long long ns)
50{
51}
52
53static inline void time_travel_set_timer(enum time_travel_timer_mode mode,
54 unsigned long long expiry)
55{
56}
57#endif
58
13#endif 59#endif
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index 691b83b10649..def2091697ca 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -203,10 +203,50 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
203 kmalloc_ok = save_kmalloc_ok; 203 kmalloc_ok = save_kmalloc_ok;
204} 204}
205 205
206static void time_travel_sleep(unsigned long long duration)
207{
208 unsigned long long next = time_travel_time + duration;
209
210 if (time_travel_mode != TT_MODE_INFCPU)
211 os_timer_disable();
212
213 if (time_travel_timer_mode != TT_TMR_DISABLED ||
214 time_travel_timer_expiry < next) {
215 if (time_travel_timer_mode == TT_TMR_ONESHOT)
216 time_travel_timer_mode = TT_TMR_DISABLED;
217 /*
218 * time_travel_time will be adjusted in the timer
219 * IRQ handler so it works even when the signal
220 * comes from the OS timer
221 */
222 deliver_alarm();
223 } else {
224 time_travel_set_time(next);
225 }
226
227 if (time_travel_mode != TT_MODE_INFCPU) {
228 if (time_travel_timer_mode == TT_TMR_PERIODIC)
229 os_timer_set_interval(time_travel_timer_interval);
230 else if (time_travel_timer_mode == TT_TMR_ONESHOT)
231 os_timer_one_shot(time_travel_timer_expiry - next);
232 }
233}
234
235static void um_idle_sleep(void)
236{
237 unsigned long long duration = UM_NSEC_PER_SEC;
238
239 if (time_travel_mode != TT_MODE_OFF) {
240 time_travel_sleep(duration);
241 } else {
242 os_idle_sleep(duration);
243 }
244}
245
206void arch_cpu_idle(void) 246void arch_cpu_idle(void)
207{ 247{
208 cpu_tasks[current_thread_info()->cpu].pid = os_getpid(); 248 cpu_tasks[current_thread_info()->cpu].pid = os_getpid();
209 os_idle_sleep(UM_NSEC_PER_SEC); 249 um_idle_sleep();
210 local_irq_enable(); 250 local_irq_enable();
211} 251}
212 252
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index b783ac87d98a..44bb10785075 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -10,12 +10,23 @@
10#include <sysdep/ptrace.h> 10#include <sysdep/ptrace.h>
11#include <sysdep/ptrace_user.h> 11#include <sysdep/ptrace_user.h>
12#include <sysdep/syscalls.h> 12#include <sysdep/syscalls.h>
13#include <shared/timer-internal.h>
13 14
14void handle_syscall(struct uml_pt_regs *r) 15void handle_syscall(struct uml_pt_regs *r)
15{ 16{
16 struct pt_regs *regs = container_of(r, struct pt_regs, regs); 17 struct pt_regs *regs = container_of(r, struct pt_regs, regs);
17 int syscall; 18 int syscall;
18 19
20 /*
21 * If we have infinite CPU resources, then make every syscall also a
22 * preemption point, since we don't have any other preemption in this
23 * case, and kernel threads would basically never run until userspace
24 * went to sleep, even if said userspace interacts with the kernel in
25 * various ways.
26 */
27 if (time_travel_mode == TT_MODE_INFCPU)
28 schedule();
29
19 /* Initialize the syscall number and default return value. */ 30 /* Initialize the syscall number and default return value. */
20 UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp); 31 UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
21 PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS); 32 PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index 3a2fe54bef65..6a051b078359 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -19,11 +19,29 @@
19#include <kern_util.h> 19#include <kern_util.h>
20#include <os.h> 20#include <os.h>
21#include <timer-internal.h> 21#include <timer-internal.h>
22#include <shared/init.h>
23
24#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
25enum time_travel_mode time_travel_mode;
26unsigned long long time_travel_time;
27enum time_travel_timer_mode time_travel_timer_mode;
28unsigned long long time_travel_timer_expiry;
29unsigned long long time_travel_timer_interval;
30
31static bool time_travel_start_set;
32static unsigned long long time_travel_start;
33#else
34#define time_travel_start_set 0
35#define time_travel_start 0
36#endif
22 37
23void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) 38void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
24{ 39{
25 unsigned long flags; 40 unsigned long flags;
26 41
42 if (time_travel_mode != TT_MODE_OFF)
43 time_travel_set_time(time_travel_timer_expiry);
44
27 local_irq_save(flags); 45 local_irq_save(flags);
28 do_IRQ(TIMER_IRQ, regs); 46 do_IRQ(TIMER_IRQ, regs);
29 local_irq_restore(flags); 47 local_irq_restore(flags);
@@ -31,26 +49,47 @@ void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
31 49
32static int itimer_shutdown(struct clock_event_device *evt) 50static int itimer_shutdown(struct clock_event_device *evt)
33{ 51{
34 os_timer_disable(); 52 if (time_travel_mode != TT_MODE_OFF)
53 time_travel_set_timer(TT_TMR_DISABLED, 0);
54
55 if (time_travel_mode != TT_MODE_INFCPU)
56 os_timer_disable();
57
35 return 0; 58 return 0;
36} 59}
37 60
38static int itimer_set_periodic(struct clock_event_device *evt) 61static int itimer_set_periodic(struct clock_event_device *evt)
39{ 62{
40 os_timer_set_interval(NSEC_PER_SEC / HZ); 63 unsigned long long interval = NSEC_PER_SEC / HZ;
64
65 if (time_travel_mode != TT_MODE_OFF)
66 time_travel_set_timer(TT_TMR_PERIODIC,
67 time_travel_time + interval);
68
69 if (time_travel_mode != TT_MODE_INFCPU)
70 os_timer_set_interval(interval);
71
41 return 0; 72 return 0;
42} 73}
43 74
44static int itimer_next_event(unsigned long delta, 75static int itimer_next_event(unsigned long delta,
45 struct clock_event_device *evt) 76 struct clock_event_device *evt)
46{ 77{
47 return os_timer_one_shot(delta + 1); 78 delta += 1;
79
80 if (time_travel_mode != TT_MODE_OFF)
81 time_travel_set_timer(TT_TMR_ONESHOT,
82 time_travel_time + delta);
83
84 if (time_travel_mode != TT_MODE_INFCPU)
85 return os_timer_one_shot(delta);
86
87 return 0;
48} 88}
49 89
50static int itimer_one_shot(struct clock_event_device *evt) 90static int itimer_one_shot(struct clock_event_device *evt)
51{ 91{
52 os_timer_one_shot(1); 92 return itimer_next_event(0, evt);
53 return 0;
54} 93}
55 94
56static struct clock_event_device timer_clockevent = { 95static struct clock_event_device timer_clockevent = {
@@ -87,6 +126,17 @@ static irqreturn_t um_timer(int irq, void *dev)
87 126
88static u64 timer_read(struct clocksource *cs) 127static u64 timer_read(struct clocksource *cs)
89{ 128{
129 if (time_travel_mode != TT_MODE_OFF) {
130 /*
131 * We make reading the timer cost a bit so that we don't get
132 * stuck in loops that expect time to move more than the
133 * exact requested sleep amount, e.g. python's socket server,
134 * see https://bugs.python.org/issue37026.
135 */
136 time_travel_set_time(time_travel_time + TIMER_MULTIPLIER);
137 return time_travel_time / TIMER_MULTIPLIER;
138 }
139
90 return os_nsecs() / TIMER_MULTIPLIER; 140 return os_nsecs() / TIMER_MULTIPLIER;
91} 141}
92 142
@@ -123,7 +173,12 @@ static void __init um_timer_setup(void)
123 173
124void read_persistent_clock64(struct timespec64 *ts) 174void read_persistent_clock64(struct timespec64 *ts)
125{ 175{
126 long long nsecs = os_persistent_clock_emulation(); 176 long long nsecs;
177
178 if (time_travel_start_set)
179 nsecs = time_travel_start + time_travel_time;
180 else
181 nsecs = os_persistent_clock_emulation();
127 182
128 set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC, 183 set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC,
129 nsecs % NSEC_PER_SEC); 184 nsecs % NSEC_PER_SEC);
@@ -134,3 +189,65 @@ void __init time_init(void)
134 timer_set_signal_handler(); 189 timer_set_signal_handler();
135 late_time_init = um_timer_setup; 190 late_time_init = um_timer_setup;
136} 191}
192
193#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
194unsigned long calibrate_delay_is_known(void)
195{
196 if (time_travel_mode == TT_MODE_INFCPU)
197 return 1;
198 return 0;
199}
200
201int setup_time_travel(char *str)
202{
203 if (strcmp(str, "=inf-cpu") == 0) {
204 time_travel_mode = TT_MODE_INFCPU;
205 timer_clockevent.name = "time-travel-timer-infcpu";
206 timer_clocksource.name = "time-travel-clock";
207 return 1;
208 }
209
210 if (!*str) {
211 time_travel_mode = TT_MODE_BASIC;
212 timer_clockevent.name = "time-travel-timer";
213 timer_clocksource.name = "time-travel-clock";
214 return 1;
215 }
216
217 return -EINVAL;
218}
219
220__setup("time-travel", setup_time_travel);
221__uml_help(setup_time_travel,
222"time-travel\n"
223"This option just enables basic time travel mode, in which the clock/timers\n"
224"inside the UML instance skip forward when there's nothing to do, rather than\n"
225"waiting for real time to elapse. However, instance CPU speed is limited by\n"
226"the real CPU speed, so e.g. a 10ms timer will always fire after ~10ms wall\n"
227"clock (but quicker when there's nothing to do).\n"
228"\n"
229"time-travel=inf-cpu\n"
230"This enables time travel mode with infinite processing power, in which there\n"
231"are no wall clock timers, and any CPU processing happens - as seen from the\n"
232"guest - instantly. This can be useful for accurate simulation regardless of\n"
233"debug overhead, physical CPU speed, etc. but is somewhat dangerous as it can\n"
234"easily lead to getting stuck (e.g. if anything in the system busy loops).\n");
235
236int setup_time_travel_start(char *str)
237{
238 int err;
239
240 err = kstrtoull(str, 0, &time_travel_start);
241 if (err)
242 return err;
243
244 time_travel_start_set = 1;
245 return 1;
246}
247
248__setup("time-travel-start", setup_time_travel_start);
249__uml_help(setup_time_travel_start,
250"time-travel-start=<seconds>\n"
251"Configure the UML instance's wall clock to start at this value rather than\n"
252"the host's wall clock at the time of UML boot.\n");
253#endif