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