xref: /openbmc/linux/arch/um/kernel/time.c (revision 78beef62)
1 /*
2  * Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
3  * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
4  * Copyright (C) 2012-2014 Cisco Systems
5  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
6  * Licensed under the GPL
7  */
8 
9 #include <linux/clockchips.h>
10 #include <linux/init.h>
11 #include <linux/interrupt.h>
12 #include <linux/jiffies.h>
13 #include <linux/mm.h>
14 #include <linux/sched.h>
15 #include <linux/spinlock.h>
16 #include <linux/threads.h>
17 #include <asm/irq.h>
18 #include <asm/param.h>
19 #include <kern_util.h>
20 #include <os.h>
21 #include <timer-internal.h>
22 #include <shared/init.h>
23 
24 #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
25 enum time_travel_mode time_travel_mode;
26 unsigned long long time_travel_time;
27 enum time_travel_timer_mode time_travel_timer_mode;
28 unsigned long long time_travel_timer_expiry;
29 unsigned long long time_travel_timer_interval;
30 
31 static bool time_travel_start_set;
32 static unsigned long long time_travel_start;
33 #else
34 #define time_travel_start_set 0
35 #define time_travel_start 0
36 #endif
37 
38 void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
39 {
40 	unsigned long flags;
41 
42 	if (time_travel_mode != TT_MODE_OFF)
43 		time_travel_set_time(time_travel_timer_expiry);
44 
45 	local_irq_save(flags);
46 	do_IRQ(TIMER_IRQ, regs);
47 	local_irq_restore(flags);
48 }
49 
50 static int itimer_shutdown(struct clock_event_device *evt)
51 {
52 	if (time_travel_mode != TT_MODE_OFF)
53 		time_travel_set_timer_mode(TT_TMR_DISABLED);
54 
55 	if (time_travel_mode != TT_MODE_INFCPU)
56 		os_timer_disable();
57 
58 	return 0;
59 }
60 
61 static int itimer_set_periodic(struct clock_event_device *evt)
62 {
63 	unsigned long long interval = NSEC_PER_SEC / HZ;
64 
65 	if (time_travel_mode != TT_MODE_OFF) {
66 		time_travel_set_timer_mode(TT_TMR_PERIODIC);
67 		time_travel_set_timer_expiry(time_travel_time + interval);
68 	}
69 
70 	if (time_travel_mode != TT_MODE_INFCPU)
71 		os_timer_set_interval(interval);
72 
73 	return 0;
74 }
75 
76 static int itimer_next_event(unsigned long delta,
77 			     struct clock_event_device *evt)
78 {
79 	delta += 1;
80 
81 	if (time_travel_mode != TT_MODE_OFF) {
82 		time_travel_set_timer_mode(TT_TMR_ONESHOT);
83 		time_travel_set_timer_expiry(time_travel_time + delta);
84 	}
85 
86 	if (time_travel_mode != TT_MODE_INFCPU)
87 		return os_timer_one_shot(delta);
88 
89 	return 0;
90 }
91 
92 static int itimer_one_shot(struct clock_event_device *evt)
93 {
94 	return itimer_next_event(0, evt);
95 }
96 
97 static struct clock_event_device timer_clockevent = {
98 	.name			= "posix-timer",
99 	.rating			= 250,
100 	.cpumask		= cpu_possible_mask,
101 	.features		= CLOCK_EVT_FEAT_PERIODIC |
102 				  CLOCK_EVT_FEAT_ONESHOT,
103 	.set_state_shutdown	= itimer_shutdown,
104 	.set_state_periodic	= itimer_set_periodic,
105 	.set_state_oneshot	= itimer_one_shot,
106 	.set_next_event		= itimer_next_event,
107 	.shift			= 0,
108 	.max_delta_ns		= 0xffffffff,
109 	.max_delta_ticks	= 0xffffffff,
110 	.min_delta_ns		= TIMER_MIN_DELTA,
111 	.min_delta_ticks	= TIMER_MIN_DELTA, // microsecond resolution should be enough for anyone, same as 640K RAM
112 	.irq			= 0,
113 	.mult			= 1,
114 };
115 
116 static irqreturn_t um_timer(int irq, void *dev)
117 {
118 	if (get_current()->mm != NULL)
119 	{
120         /* userspace - relay signal, results in correct userspace timers */
121 		os_alarm_process(get_current()->mm->context.id.u.pid);
122 	}
123 
124 	(*timer_clockevent.event_handler)(&timer_clockevent);
125 
126 	return IRQ_HANDLED;
127 }
128 
129 static u64 timer_read(struct clocksource *cs)
130 {
131 	if (time_travel_mode != TT_MODE_OFF) {
132 		/*
133 		 * We make reading the timer cost a bit so that we don't get
134 		 * stuck in loops that expect time to move more than the
135 		 * exact requested sleep amount, e.g. python's socket server,
136 		 * see https://bugs.python.org/issue37026.
137 		 */
138 		time_travel_set_time(time_travel_time + TIMER_MULTIPLIER);
139 		return time_travel_time / TIMER_MULTIPLIER;
140 	}
141 
142 	return os_nsecs() / TIMER_MULTIPLIER;
143 }
144 
145 static struct clocksource timer_clocksource = {
146 	.name		= "timer",
147 	.rating		= 300,
148 	.read		= timer_read,
149 	.mask		= CLOCKSOURCE_MASK(64),
150 	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
151 };
152 
153 static void __init um_timer_setup(void)
154 {
155 	int err;
156 
157 	err = request_irq(TIMER_IRQ, um_timer, IRQF_TIMER, "hr timer", NULL);
158 	if (err != 0)
159 		printk(KERN_ERR "register_timer : request_irq failed - "
160 		       "errno = %d\n", -err);
161 
162 	err = os_timer_create();
163 	if (err != 0) {
164 		printk(KERN_ERR "creation of timer failed - errno = %d\n", -err);
165 		return;
166 	}
167 
168 	err = clocksource_register_hz(&timer_clocksource, NSEC_PER_SEC/TIMER_MULTIPLIER);
169 	if (err) {
170 		printk(KERN_ERR "clocksource_register_hz returned %d\n", err);
171 		return;
172 	}
173 	clockevents_register_device(&timer_clockevent);
174 }
175 
176 void read_persistent_clock64(struct timespec64 *ts)
177 {
178 	long long nsecs;
179 
180 	if (time_travel_start_set)
181 		nsecs = time_travel_start + time_travel_time;
182 	else
183 		nsecs = os_persistent_clock_emulation();
184 
185 	set_normalized_timespec64(ts, nsecs / NSEC_PER_SEC,
186 				  nsecs % NSEC_PER_SEC);
187 }
188 
189 void __init time_init(void)
190 {
191 	timer_set_signal_handler();
192 	late_time_init = um_timer_setup;
193 }
194 
195 #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
196 unsigned long calibrate_delay_is_known(void)
197 {
198 	if (time_travel_mode == TT_MODE_INFCPU)
199 		return 1;
200 	return 0;
201 }
202 
203 int setup_time_travel(char *str)
204 {
205 	if (strcmp(str, "=inf-cpu") == 0) {
206 		time_travel_mode = TT_MODE_INFCPU;
207 		timer_clockevent.name = "time-travel-timer-infcpu";
208 		timer_clocksource.name = "time-travel-clock";
209 		return 1;
210 	}
211 
212 	if (!*str) {
213 		time_travel_mode = TT_MODE_BASIC;
214 		timer_clockevent.name = "time-travel-timer";
215 		timer_clocksource.name = "time-travel-clock";
216 		return 1;
217 	}
218 
219 	return -EINVAL;
220 }
221 
222 __setup("time-travel", setup_time_travel);
223 __uml_help(setup_time_travel,
224 "time-travel\n"
225 "This option just enables basic time travel mode, in which the clock/timers\n"
226 "inside the UML instance skip forward when there's nothing to do, rather than\n"
227 "waiting for real time to elapse. However, instance CPU speed is limited by\n"
228 "the real CPU speed, so e.g. a 10ms timer will always fire after ~10ms wall\n"
229 "clock (but quicker when there's nothing to do).\n"
230 "\n"
231 "time-travel=inf-cpu\n"
232 "This enables time travel mode with infinite processing power, in which there\n"
233 "are no wall clock timers, and any CPU processing happens - as seen from the\n"
234 "guest - instantly. This can be useful for accurate simulation regardless of\n"
235 "debug overhead, physical CPU speed, etc. but is somewhat dangerous as it can\n"
236 "easily lead to getting stuck (e.g. if anything in the system busy loops).\n");
237 
238 int setup_time_travel_start(char *str)
239 {
240 	int err;
241 
242 	err = kstrtoull(str, 0, &time_travel_start);
243 	if (err)
244 		return err;
245 
246 	time_travel_start_set = 1;
247 	return 1;
248 }
249 
250 __setup("time-travel-start", setup_time_travel_start);
251 __uml_help(setup_time_travel_start,
252 "time-travel-start=<seconds>\n"
253 "Configure the UML instance's wall clock to start at this value rather than\n"
254 "the host's wall clock at the time of UML boot.\n");
255 #endif
256