Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2018 Intel Corporation
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : #include <zephyr/kernel.h>
8 : : #include <zephyr/spinlock.h>
9 : : #include <ksched.h>
10 : : #include <timeout_q.h>
11 : : #include <zephyr/internal/syscall_handler.h>
12 : : #include <zephyr/drivers/timer/system_timer.h>
13 : : #include <zephyr/sys_clock.h>
14 : : #include <zephyr/llext/symbol.h>
15 : :
16 : : static uint64_t curr_tick;
17 : :
18 : : static sys_dlist_t timeout_list = SYS_DLIST_STATIC_INIT(&timeout_list);
19 : :
20 : : /*
21 : : * The timeout code shall take no locks other than its own (timeout_lock), nor
22 : : * shall it call any other subsystem while holding this lock.
23 : : */
24 : : static struct k_spinlock timeout_lock;
25 : :
26 : : /* Ticks left to process in the currently-executing sys_clock_announce() */
27 : : static int announce_remaining;
28 : :
29 : 273 : static struct _timeout *first(void)
30 : : {
31 : 273 : sys_dnode_t *t = sys_dlist_peek_head(&timeout_list);
32 : :
33 : 273 : return (t == NULL) ? NULL : CONTAINER_OF(t, struct _timeout, node);
34 : : }
35 : :
36 : 54 : static struct _timeout *next(struct _timeout *t)
37 : : {
38 : 54 : sys_dnode_t *n = sys_dlist_peek_next(&timeout_list, &t->node);
39 : :
40 : 54 : return (n == NULL) ? NULL : CONTAINER_OF(n, struct _timeout, node);
41 : : }
42 : :
43 : 54 : static void remove_timeout(struct _timeout *t)
44 : : {
45 [ - + ]: 54 : if (next(t) != NULL) {
46 : 0 : next(t)->dticks += t->dticks;
47 : : }
48 : :
49 : 54 : sys_dlist_remove(&t->node);
50 : 54 : }
51 : :
52 : 109 : static int32_t elapsed(void)
53 : : {
54 : : /* While sys_clock_announce() is executing, new relative timeouts will be
55 : : * scheduled relatively to the currently firing timeout's original tick
56 : : * value (=curr_tick) rather than relative to the current
57 : : * sys_clock_elapsed().
58 : : *
59 : : * This means that timeouts being scheduled from within timeout callbacks
60 : : * will be scheduled at well-defined offsets from the currently firing
61 : : * timeout.
62 : : *
63 : : * As a side effect, the same will happen if an ISR with higher priority
64 : : * preempts a timeout callback and schedules a timeout.
65 : : *
66 : : * The distinction is implemented by looking at announce_remaining which
67 : : * will be non-zero while sys_clock_announce() is executing and zero
68 : : * otherwise.
69 : : */
70 [ + - ]: 109 : return announce_remaining == 0 ? sys_clock_elapsed() : 0U;
71 : : }
72 : :
73 : 109 : static int32_t next_timeout(int32_t ticks_elapsed)
74 : : {
75 : 109 : struct _timeout *to = first();
76 : 109 : int32_t ret;
77 : :
78 [ + + ]: 109 : if ((to == NULL) ||
79 [ + - ]: 55 : ((int64_t)(to->dticks - ticks_elapsed) > (int64_t)INT_MAX)) {
80 : : ret = SYS_CLOCK_MAX_WAIT;
81 : : } else {
82 : 55 : ret = max(0, to->dticks - ticks_elapsed);
83 : : }
84 : :
85 : 109 : return ret;
86 : : }
87 : :
88 : 55 : k_ticks_t z_add_timeout(struct _timeout *to, _timeout_func_t fn, k_timeout_t timeout)
89 : : {
90 : 55 : k_ticks_t ticks = 0;
91 : :
92 [ + - ]: 55 : if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
93 : : return 0;
94 : : }
95 : :
96 : : #ifdef CONFIG_KERNEL_COHERENCE
97 : : __ASSERT_NO_MSG(sys_cache_is_mem_coherent(to));
98 : : #endif /* CONFIG_KERNEL_COHERENCE */
99 : :
100 [ - + ]: 55 : __ASSERT(!sys_dnode_is_linked(&to->node), "");
101 : 55 : to->fn = fn;
102 : :
103 [ + + ]: 110 : K_SPINLOCK(&timeout_lock) {
104 : 55 : struct _timeout *t;
105 : 55 : int32_t ticks_elapsed;
106 : 55 : bool has_elapsed = false;
107 : :
108 [ + - ]: 55 : if (Z_IS_TIMEOUT_RELATIVE(timeout)) {
109 : 55 : ticks_elapsed = elapsed();
110 : 55 : has_elapsed = true;
111 : 55 : to->dticks = timeout.ticks + 1 + ticks_elapsed;
112 : 55 : ticks = curr_tick + to->dticks;
113 : : } else {
114 : 0 : k_ticks_t dticks = Z_TICK_ABS(timeout.ticks) - curr_tick;
115 : :
116 : 0 : to->dticks = max(1, dticks);
117 : 0 : ticks = timeout.ticks;
118 : : }
119 : :
120 [ - + ]: 55 : for (t = first(); t != NULL; t = next(t)) {
121 [ # # ]: 0 : if (t->dticks > to->dticks) {
122 : 0 : t->dticks -= to->dticks;
123 : 0 : sys_dlist_insert(&t->node, &to->node);
124 : 0 : break;
125 : : }
126 : 0 : to->dticks -= t->dticks;
127 : : }
128 : :
129 : 0 : if (t == NULL) {
130 : 55 : sys_dlist_append(&timeout_list, &to->node);
131 : : }
132 : :
133 [ + - + - ]: 55 : if (to == first() && announce_remaining == 0) {
134 [ - + ]: 55 : if (!has_elapsed) {
135 : : /* In case of absolute timeout that is first to expire
136 : : * elapsed need to be read from the system clock.
137 : : */
138 : 0 : ticks_elapsed = elapsed();
139 : : }
140 : 55 : sys_clock_set_timeout(next_timeout(ticks_elapsed), false);
141 : : }
142 : : }
143 : :
144 : 55 : return ticks;
145 : : }
146 : :
147 : 207 : int z_abort_timeout(struct _timeout *to)
148 : : {
149 : 207 : int ret = -EINVAL;
150 : :
151 [ + + ]: 414 : K_SPINLOCK(&timeout_lock) {
152 [ + + ]: 207 : if (sys_dnode_is_linked(&to->node)) {
153 : 54 : bool is_first = (to == first());
154 : :
155 : 54 : remove_timeout(to);
156 : 54 : to->dticks = TIMEOUT_DTICKS_ABORTED;
157 : 54 : ret = 0;
158 [ - + ]: 54 : if (is_first) {
159 : 54 : sys_clock_set_timeout(next_timeout(elapsed()), false);
160 : : }
161 : : }
162 : : }
163 : :
164 : 207 : return ret;
165 : : }
166 : :
167 : : /* must be locked */
168 : 0 : static k_ticks_t timeout_rem(const struct _timeout *timeout)
169 : : {
170 : 0 : k_ticks_t ticks = 0;
171 : :
172 [ # # ]: 0 : for (struct _timeout *t = first(); t != NULL; t = next(t)) {
173 : 0 : ticks += t->dticks;
174 [ # # ]: 0 : if (timeout == t) {
175 : : break;
176 : : }
177 : : }
178 : :
179 : 0 : return ticks;
180 : : }
181 : :
182 : 0 : k_ticks_t z_timeout_remaining(const struct _timeout *timeout)
183 : : {
184 : 0 : k_ticks_t ticks = 0;
185 : :
186 [ # # ]: 0 : K_SPINLOCK(&timeout_lock) {
187 [ # # ]: 0 : if (!z_is_inactive_timeout(timeout)) {
188 : 0 : ticks = timeout_rem(timeout) - elapsed();
189 : : }
190 : : }
191 : :
192 : 0 : return ticks;
193 : : }
194 : : EXPORT_SYMBOL(z_timeout_remaining);
195 : :
196 : 0 : k_ticks_t z_timeout_expires(const struct _timeout *timeout)
197 : : {
198 : 0 : k_ticks_t ticks = 0;
199 : :
200 [ # # ]: 0 : K_SPINLOCK(&timeout_lock) {
201 : 0 : ticks = curr_tick;
202 [ # # ]: 0 : if (!z_is_inactive_timeout(timeout)) {
203 : 0 : ticks += timeout_rem(timeout);
204 : : }
205 : : }
206 : :
207 : 0 : return ticks;
208 : : }
209 : : EXPORT_SYMBOL(z_timeout_expires);
210 : :
211 : 0 : int32_t z_get_next_timeout_expiry(void)
212 : : {
213 : 0 : int32_t ret = (int32_t) K_TICKS_FOREVER;
214 : :
215 [ # # ]: 0 : K_SPINLOCK(&timeout_lock) {
216 : 0 : ret = next_timeout(elapsed());
217 : : }
218 : 0 : return ret;
219 : : }
220 : :
221 : 0 : void sys_clock_announce(int32_t ticks)
222 : : {
223 : 0 : k_spinlock_key_t key = k_spin_lock(&timeout_lock);
224 : :
225 : : /* We release the lock around the callbacks below, so on SMP
226 : : * systems someone might be already running the loop. Don't
227 : : * race (which will cause parallel execution of "sequential"
228 : : * timeouts and confuse apps), just increment the tick count
229 : : * and return.
230 : : */
231 : 0 : if (IS_ENABLED(CONFIG_SMP) && (announce_remaining != 0)) {
232 : : announce_remaining += ticks;
233 : : k_spin_unlock(&timeout_lock, key);
234 : : return;
235 : : }
236 : :
237 : 0 : announce_remaining = ticks;
238 : :
239 : 0 : struct _timeout *t;
240 : :
241 : 0 : for (t = first();
242 [ # # # # ]: 0 : (t != NULL) && (t->dticks <= announce_remaining);
243 : 0 : t = first()) {
244 : 0 : int dt = t->dticks;
245 : :
246 : 0 : curr_tick += dt;
247 : 0 : t->dticks = 0;
248 : 0 : remove_timeout(t);
249 : :
250 : 0 : k_spin_unlock(&timeout_lock, key);
251 : 0 : t->fn(t);
252 : 0 : key = k_spin_lock(&timeout_lock);
253 : 0 : announce_remaining -= dt;
254 : : }
255 : :
256 [ # # ]: 0 : if (t != NULL) {
257 : 0 : t->dticks -= announce_remaining;
258 : : }
259 : :
260 : 0 : curr_tick += announce_remaining;
261 : 0 : announce_remaining = 0;
262 : :
263 : 0 : sys_clock_set_timeout(next_timeout(0), false);
264 : :
265 : 0 : k_spin_unlock(&timeout_lock, key);
266 : :
267 : : #ifdef CONFIG_TIMESLICING
268 : 0 : z_time_slice();
269 : : #endif /* CONFIG_TIMESLICING */
270 : : }
271 : :
272 : 0 : int64_t sys_clock_tick_get(void)
273 : : {
274 : 0 : uint64_t t = 0U;
275 : :
276 [ # # ]: 0 : K_SPINLOCK(&timeout_lock) {
277 : 0 : t = curr_tick + elapsed();
278 : : }
279 : 0 : return t;
280 : : }
281 : :
282 : 0 : uint32_t sys_clock_tick_get_32(void)
283 : : {
284 : : #ifdef CONFIG_TICKLESS_KERNEL
285 : 0 : return (uint32_t)sys_clock_tick_get();
286 : : #else
287 : : return (uint32_t)curr_tick;
288 : : #endif /* CONFIG_TICKLESS_KERNEL */
289 : : }
290 : :
291 : 0 : int64_t z_impl_k_uptime_ticks(void)
292 : : {
293 : 0 : return sys_clock_tick_get();
294 : : }
295 : :
296 : : #ifdef CONFIG_USERSPACE
297 : : static inline int64_t z_vrfy_k_uptime_ticks(void)
298 : : {
299 : : return z_impl_k_uptime_ticks();
300 : : }
301 : : #include <zephyr/syscalls/k_uptime_ticks_mrsh.c>
302 : : #endif /* CONFIG_USERSPACE */
303 : :
304 : 0 : k_timepoint_t sys_timepoint_calc(k_timeout_t timeout)
305 : : {
306 : 0 : k_timepoint_t timepoint;
307 : :
308 [ # # ]: 0 : if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
309 : : timepoint.tick = UINT64_MAX;
310 [ # # ]: 0 : } else if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
311 : : timepoint.tick = 0;
312 : : } else {
313 : 0 : k_ticks_t dt = timeout.ticks;
314 : :
315 [ # # ]: 0 : if (Z_IS_TIMEOUT_RELATIVE(timeout)) {
316 : 0 : timepoint.tick = sys_clock_tick_get() + max(1, dt);
317 : : } else {
318 : 0 : timepoint.tick = Z_TICK_ABS(dt);
319 : : }
320 : : }
321 : :
322 : 0 : return timepoint;
323 : : }
324 : :
325 : 0 : k_timeout_t sys_timepoint_timeout(k_timepoint_t timepoint)
326 : : {
327 : 0 : uint64_t now, remaining;
328 : :
329 [ # # ]: 0 : if (timepoint.tick == UINT64_MAX) {
330 : 0 : return K_FOREVER;
331 : : }
332 [ # # ]: 0 : if (timepoint.tick == 0) {
333 : 0 : return K_NO_WAIT;
334 : : }
335 : :
336 : 0 : now = sys_clock_tick_get();
337 [ # # ]: 0 : remaining = (timepoint.tick > now) ? (timepoint.tick - now) : 0;
338 : 0 : return K_TICKS(remaining);
339 : : }
340 : :
341 : : #ifdef CONFIG_ZTEST
342 : 0 : void z_impl_sys_clock_tick_set(uint64_t tick)
343 : : {
344 : 0 : curr_tick = tick;
345 : 0 : }
346 : :
347 : 0 : void z_vrfy_sys_clock_tick_set(uint64_t tick)
348 : : {
349 : 0 : z_impl_sys_clock_tick_set(tick);
350 : 0 : }
351 : : #endif /* CONFIG_ZTEST */
|