xref: /linux/arch/um/os-Linux/time.c (revision 399ead3a6d76cbdd29a716660db5c84a314dab70)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
4  * Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
5  * Copyright (C) 2012-2014 Cisco Systems
6  * Copyright (C) 2000 - 2007 Jeff Dike (jdike{addtoit,linux.intel}.com)
7  */
8 
9 #include <stddef.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <signal.h>
13 #include <time.h>
14 #include <sys/signalfd.h>
15 #include <sys/time.h>
16 #include <kern_util.h>
17 #include <os.h>
18 #include <smp.h>
19 #include <string.h>
20 #include "internal.h"
21 
22 static timer_t event_high_res_timer[CONFIG_NR_CPUS] = { 0 };
23 
timespec_to_ns(const struct timespec * ts)24 static inline long long timespec_to_ns(const struct timespec *ts)
25 {
26 	return ((long long) ts->tv_sec * UM_NSEC_PER_SEC) + ts->tv_nsec;
27 }
28 
os_persistent_clock_emulation(void)29 long long os_persistent_clock_emulation(void)
30 {
31 	struct timespec realtime_tp;
32 
33 	clock_gettime(CLOCK_REALTIME, &realtime_tp);
34 	return timespec_to_ns(&realtime_tp);
35 }
36 
37 #ifndef sigev_notify_thread_id
38 #define sigev_notify_thread_id _sigev_un._tid
39 #endif
40 
41 /**
42  * os_timer_create() - create an new posix (interval) timer
43  */
os_timer_create(void)44 int os_timer_create(void)
45 {
46 	int cpu = uml_curr_cpu();
47 	timer_t *t = &event_high_res_timer[cpu];
48 	struct sigevent sev = {
49 		.sigev_notify = SIGEV_THREAD_ID,
50 		.sigev_signo = SIGALRM,
51 		.sigev_value.sival_ptr = t,
52 		.sigev_notify_thread_id = gettid(),
53 	};
54 
55 	if (timer_create(CLOCK_MONOTONIC, &sev, t) == -1)
56 		return -1;
57 
58 	return 0;
59 }
60 
os_timer_set_interval(int cpu,unsigned long long nsecs)61 int os_timer_set_interval(int cpu, unsigned long long nsecs)
62 {
63 	struct itimerspec its;
64 
65 	its.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC;
66 	its.it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC;
67 
68 	its.it_interval.tv_sec = nsecs / UM_NSEC_PER_SEC;
69 	its.it_interval.tv_nsec = nsecs % UM_NSEC_PER_SEC;
70 
71 	if (timer_settime(event_high_res_timer[cpu], 0, &its, NULL) == -1)
72 		return -errno;
73 
74 	return 0;
75 }
76 
os_timer_one_shot(int cpu,unsigned long long nsecs)77 int os_timer_one_shot(int cpu, unsigned long long nsecs)
78 {
79 	struct itimerspec its = {
80 		.it_value.tv_sec = nsecs / UM_NSEC_PER_SEC,
81 		.it_value.tv_nsec = nsecs % UM_NSEC_PER_SEC,
82 
83 		.it_interval.tv_sec = 0,
84 		.it_interval.tv_nsec = 0, // we cheat here
85 	};
86 
87 	timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
88 	return 0;
89 }
90 
91 /**
92  * os_timer_disable() - disable the posix (interval) timer
93  * @cpu: the CPU for which the timer is to be disabled
94  */
os_timer_disable(int cpu)95 void os_timer_disable(int cpu)
96 {
97 	struct itimerspec its;
98 
99 	memset(&its, 0, sizeof(struct itimerspec));
100 	timer_settime(event_high_res_timer[cpu], 0, &its, NULL);
101 }
102 
os_nsecs(void)103 long long os_nsecs(void)
104 {
105 	struct timespec ts;
106 
107 	clock_gettime(CLOCK_MONOTONIC,&ts);
108 	return timespec_to_ns(&ts);
109 }
110 
111 static __thread int wake_signals;
112 
os_idle_prepare(void)113 void os_idle_prepare(void)
114 {
115 	sigset_t set;
116 
117 	sigemptyset(&set);
118 	sigaddset(&set, SIGALRM);
119 	sigaddset(&set, IPI_SIGNAL);
120 
121 	/*
122 	 * We need to use signalfd rather than sigsuspend in idle sleep
123 	 * because the IPI signal is a real-time signal that carries data,
124 	 * and unlike handling SIGALRM, we cannot simply flag it in
125 	 * signals_pending.
126 	 */
127 	wake_signals = signalfd(-1, &set, SFD_CLOEXEC);
128 	if (wake_signals < 0)
129 		panic("Failed to create signal FD, errno = %d", errno);
130 }
131 
132 /**
133  * os_idle_sleep() - sleep until interrupted
134  */
os_idle_sleep(void)135 void os_idle_sleep(void)
136 {
137 	sigset_t set;
138 
139 	/*
140 	 * Block SIGALRM while performing the need_resched check.
141 	 * Note that, because IRQs are disabled, the IPI signal is
142 	 * already blocked.
143 	 */
144 	sigemptyset(&set);
145 	sigaddset(&set, SIGALRM);
146 	sigprocmask(SIG_BLOCK, &set, NULL);
147 
148 	/*
149 	 * Because disabling IRQs does not block SIGALRM, it is also
150 	 * necessary to check for any pending timer alarms.
151 	 */
152 	if (!uml_need_resched() && !timer_alarm_pending())
153 		os_poll(1, &wake_signals);
154 
155 	/* Restore the signal mask. */
156 	sigprocmask(SIG_UNBLOCK, &set, NULL);
157 }
158