Skip to content

Commit 8ab3bc6

Browse files
lib: posix: clock: Refactor to use sys_realtime for realtime clock
The posix clock implementation uses k_uptime as its only source of truth for time, tying clock monotonic 1-1 to k_uptime, and clock realtime to (k_uptime + a reference/offset). sys_realtime provides the zephyr version of a realtime clock, which is managed by the kernel. This commit refactors the posix clock implementation to offload the realtime clock to sys_realtime, converting between a unix timestamp in ms (sys_realtime) to a unix timestamp in struct timespec (posix time). A small adjustment to the posix common clock test suite to set the realtime clock to the same time as clock monotonic was required since the test suite can not support a clock which is not tied 1-1 to k_uptime. Signed-off-by: Bjarki Arge Andreasen <[email protected]>
1 parent 41e1aae commit 8ab3bc6

File tree

4 files changed

+143
-151
lines changed

4 files changed

+143
-151
lines changed

lib/posix/options/CMakeLists.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
set(GEN_DIR ${ZEPHYR_BINARY_DIR}/include/generated)
44

5-
zephyr_syscall_header(
6-
posix_clock.h
7-
)
8-
95
if(CONFIG_POSIX_API)
106
zephyr_include_directories(${ZEPHYR_BASE}/include/zephyr/posix)
117
endif()

lib/posix/options/Kconfig.timer

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
menuconfig POSIX_TIMERS
77
bool "POSIX timers, clocks, and sleep functions"
8+
select SYS_REALTIME
89
help
910
Select 'y' here and Zephyr will provide implementations of clock_getres(), clock_gettime(),
1011
clock_settime(), nanosleep(), timer_create(), timer_delete(), timer_getoverrun(),

lib/posix/options/clock.c

Lines changed: 142 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -5,97 +5,157 @@
55
* SPDX-License-Identifier: Apache-2.0
66
*/
77

8-
#include "posix_clock.h"
9-
108
#include <zephyr/kernel.h>
119
#include <errno.h>
1210
#include <zephyr/posix/time.h>
1311
#include <zephyr/posix/sys/time.h>
1412
#include <zephyr/posix/unistd.h>
15-
#include <zephyr/internal/syscall_handler.h>
16-
#include <zephyr/spinlock.h>
13+
#include <zephyr/sys/realtime.h>
14+
15+
static bool __posix_clock_validate_timespec(const struct timespec *ts)
16+
{
17+
return ts->tv_sec >= 0 && ts->tv_nsec >= 0 && ts->tv_nsec < NSEC_PER_SEC;
18+
}
19+
20+
static void __posix_clock_k_ticks_to_timespec(struct timespec *ts, int64_t ticks)
21+
{
22+
uint64_t elapsed_secs = ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
23+
uint64_t nremainder = ticks - elapsed_secs * CONFIG_SYS_CLOCK_TICKS_PER_SEC;
24+
25+
ts->tv_sec = (time_t) elapsed_secs;
26+
/* For ns 32 bit conversion can be used since its smaller than 1sec. */
27+
ts->tv_nsec = (int32_t) k_ticks_to_ns_floor32(nremainder);
28+
}
29+
30+
static void __posix_clock_get_monotonic(struct timespec *ts)
31+
{
32+
__posix_clock_k_ticks_to_timespec(ts, k_uptime_ticks());
33+
}
34+
35+
static void __posix_clock_msec_to_timespec(struct timespec *ts, const int64_t *ms)
36+
{
37+
ts->tv_sec = *ms / MSEC_PER_SEC;
38+
ts->tv_nsec = (*ms % MSEC_PER_SEC) * NSEC_PER_MSEC;
39+
}
40+
41+
static uint64_t __posix_clock_timespec_to_usec(const struct timespec *ts)
42+
{
43+
uint64_t usec;
44+
45+
usec = ts->tv_sec;
46+
usec *= USEC_PER_SEC;
47+
usec += DIV_ROUND_UP(ts->tv_nsec, NSEC_PER_USEC);
48+
return usec;
49+
}
50+
51+
static uint64_t __posix_clock_timespec_to_msec(const struct timespec *ts)
52+
{
53+
uint64_t msec;
54+
55+
msec = ts->tv_sec;
56+
msec *= MSEC_PER_SEC;
57+
msec += DIV_ROUND_UP(ts->tv_nsec, NSEC_PER_MSEC);
58+
return msec;
59+
}
60+
61+
/* Check if a_ts is less than b_ts (a_ts < b_ts) */
62+
static bool __posix_clock_timespec_less_than(const struct timespec *a_ts,
63+
const struct timespec *b_ts)
64+
{
65+
return (a_ts->tv_sec < b_ts->tv_sec) ||
66+
(a_ts->tv_sec == b_ts->tv_sec && a_ts->tv_nsec < b_ts->tv_nsec);
67+
}
1768

1869
/*
19-
* `k_uptime_get` returns a timestamp based on an always increasing
20-
* value from the system start. To support the `CLOCK_REALTIME`
21-
* clock, this `rt_clock_base` records the time that the system was
22-
* started. This can either be set via 'clock_settime', or could be
23-
* set from a real time clock, if such hardware is present.
70+
* Subtract b_ts from a_ts placing result in res_ts (ret_ts = a_ts - b_ts)
71+
* Presumes a_ts >= b_ts
2472
*/
25-
static struct timespec rt_clock_base;
26-
static struct k_spinlock rt_clock_base_lock;
73+
static void __posix_clock_timespec_subtract(struct timespec *res_ts,
74+
const struct timespec *a_ts,
75+
const struct timespec *b_ts)
76+
{
77+
res_ts->tv_sec = a_ts->tv_sec - b_ts->tv_sec;
2778

28-
/**
29-
* @brief Get clock time specified by clock_id.
30-
*
31-
* See IEEE 1003.1
32-
*/
33-
int z_impl___posix_clock_get_base(clockid_t clock_id, struct timespec *base)
79+
if (b_ts->tv_nsec <= a_ts->tv_nsec) {
80+
res_ts->tv_nsec = a_ts->tv_nsec - b_ts->tv_nsec;
81+
} else {
82+
res_ts->tv_sec--;
83+
res_ts->tv_nsec = a_ts->tv_nsec + NSEC_PER_SEC - b_ts->tv_nsec;
84+
}
85+
}
86+
87+
/* Add b_ts to a_ts placing result in res_ts (ret_ts = a_ts + b_ts) */
88+
static void __posix_clock_timespec_add(struct timespec *res_ts,
89+
const struct timespec *a_ts,
90+
const struct timespec *b_ts)
3491
{
35-
switch (clock_id) {
36-
case CLOCK_MONOTONIC:
37-
base->tv_sec = 0;
38-
base->tv_nsec = 0;
39-
break;
92+
res_ts->tv_sec = a_ts->tv_sec + b_ts->tv_sec;
93+
res_ts->tv_nsec = a_ts->tv_nsec + b_ts->tv_nsec;
4094

41-
case CLOCK_REALTIME:
42-
K_SPINLOCK(&rt_clock_base_lock) {
43-
*base = rt_clock_base;
44-
}
45-
break;
95+
if (res_ts->tv_nsec >= NSEC_PER_SEC) {
96+
res_ts->tv_sec++;
97+
res_ts->tv_nsec -= NSEC_PER_SEC;
98+
}
99+
}
46100

47-
default:
48-
errno = EINVAL;
49-
return -1;
101+
static void __posix_clock_timespec_copy(struct timespec *des_ts, const struct timespec *src_ts)
102+
{
103+
des_ts->tv_sec = src_ts->tv_sec;
104+
des_ts->tv_nsec = src_ts->tv_nsec;
105+
}
106+
107+
static void __posix_clock_get_realtime(struct timespec *ts)
108+
{
109+
int res;
110+
int64_t timestamp_ms;
111+
112+
res = sys_realtime_get_timestamp(&timestamp_ms);
113+
if (timestamp_ms < 0) {
114+
/* timespec can't be negative */
115+
ts->tv_sec = 0;
116+
ts->tv_nsec = 0;
50117
}
51118

52-
return 0;
119+
__posix_clock_msec_to_timespec(ts, &timestamp_ms);
53120
}
54121

55-
#ifdef CONFIG_USERSPACE
56-
int z_vrfy___posix_clock_get_base(clockid_t clock_id, struct timespec *ts)
122+
static int __posix_clock_set_realtime(const struct timespec *ts)
57123
{
58-
K_OOPS(K_SYSCALL_MEMORY_WRITE(ts, sizeof(*ts)));
59-
return z_impl___posix_clock_get_base(clock_id, ts);
124+
int64_t timestamp_ms;
125+
int res;
126+
127+
timestamp_ms = (int64_t)__posix_clock_timespec_to_msec(ts);
128+
129+
res = sys_realtime_set_timestamp(&timestamp_ms);
130+
if (res < 0) {
131+
errno = EINVAL;
132+
return -1;
133+
}
134+
135+
return 0;
60136
}
61-
#include <zephyr/syscalls/__posix_clock_get_base_mrsh.c>
62-
#endif
63137

64138
int clock_gettime(clockid_t clock_id, struct timespec *ts)
65139
{
66-
struct timespec base;
140+
int res;
67141

68142
switch (clock_id) {
69143
case CLOCK_MONOTONIC:
70-
base.tv_sec = 0;
71-
base.tv_nsec = 0;
144+
__posix_clock_get_monotonic(ts);
145+
res = 0;
72146
break;
73147

74148
case CLOCK_REALTIME:
75-
(void)__posix_clock_get_base(clock_id, &base);
149+
__posix_clock_get_realtime(ts);
150+
res = 0;
76151
break;
77152

78153
default:
79154
errno = EINVAL;
80-
return -1;
155+
res = -1;
81156
}
82157

83-
uint64_t ticks = k_uptime_ticks();
84-
uint64_t elapsed_secs = ticks / CONFIG_SYS_CLOCK_TICKS_PER_SEC;
85-
uint64_t nremainder = ticks - elapsed_secs * CONFIG_SYS_CLOCK_TICKS_PER_SEC;
86-
87-
ts->tv_sec = (time_t) elapsed_secs;
88-
/* For ns 32 bit conversion can be used since its smaller than 1sec. */
89-
ts->tv_nsec = (int32_t) k_ticks_to_ns_floor32(nremainder);
90-
91-
ts->tv_sec += base.tv_sec;
92-
ts->tv_nsec += base.tv_nsec;
93-
if (ts->tv_nsec >= NSEC_PER_SEC) {
94-
ts->tv_sec++;
95-
ts->tv_nsec -= NSEC_PER_SEC;
96-
}
97-
98-
return 0;
158+
return res;
99159
}
100160

101161
int clock_getres(clockid_t clock_id, struct timespec *res)
@@ -130,31 +190,12 @@ int clock_getres(clockid_t clock_id, struct timespec *res)
130190
*/
131191
int clock_settime(clockid_t clock_id, const struct timespec *tp)
132192
{
133-
struct timespec base;
134-
k_spinlock_key_t key;
135-
136-
if (clock_id != CLOCK_REALTIME) {
193+
if (clock_id != CLOCK_REALTIME || !__posix_clock_validate_timespec(tp)) {
137194
errno = EINVAL;
138195
return -1;
139196
}
140197

141-
if (tp->tv_nsec < 0 || tp->tv_nsec >= NSEC_PER_SEC) {
142-
errno = EINVAL;
143-
return -1;
144-
}
145-
146-
uint64_t elapsed_nsecs = k_ticks_to_ns_floor64(k_uptime_ticks());
147-
int64_t delta = (int64_t)NSEC_PER_SEC * tp->tv_sec + tp->tv_nsec
148-
- elapsed_nsecs;
149-
150-
base.tv_sec = delta / NSEC_PER_SEC;
151-
base.tv_nsec = delta % NSEC_PER_SEC;
152-
153-
key = k_spin_lock(&rt_clock_base_lock);
154-
rt_clock_base = base;
155-
k_spin_unlock(&rt_clock_base_lock, key);
156-
157-
return 0;
198+
return __posix_clock_set_realtime(tp);
158199
}
159200

160201
/*
@@ -195,10 +236,10 @@ int usleep(useconds_t useconds)
195236
static int __z_clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp,
196237
struct timespec *rmtp)
197238
{
198-
uint64_t ns;
199-
uint64_t us;
200-
uint64_t uptime_ns;
201-
k_spinlock_key_t key;
239+
volatile uint64_t usec;
240+
struct timespec clock_ts;
241+
struct timespec rel_ts;
242+
struct timespec abs_ts;
202243
const bool update_rmtp = rmtp != NULL;
203244

204245
if (!(clock_id == CLOCK_REALTIME || clock_id == CLOCK_MONOTONIC)) {
@@ -211,42 +252,35 @@ static int __z_clock_nanosleep(clockid_t clock_id, int flags, const struct times
211252
return -1;
212253
}
213254

214-
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= NSEC_PER_SEC) {
255+
if (!__posix_clock_validate_timespec(rqtp)) {
215256
errno = EINVAL;
216257
return -1;
217258
}
218259

219-
if ((flags & TIMER_ABSTIME) == 0 &&
220-
unlikely(rqtp->tv_sec >= ULLONG_MAX / NSEC_PER_SEC)) {
221-
222-
ns = rqtp->tv_nsec + NSEC_PER_SEC
223-
+ k_sleep(K_SECONDS(rqtp->tv_sec - 1)) * NSEC_PER_MSEC;
224-
} else {
225-
ns = rqtp->tv_sec * NSEC_PER_SEC + rqtp->tv_nsec;
226-
}
227-
228-
uptime_ns = k_cyc_to_ns_ceil64(k_cycle_get_32());
260+
if ((flags & TIMER_ABSTIME) && clock_id == CLOCK_REALTIME) {
261+
__posix_clock_get_realtime(&clock_ts);
229262

230-
if (flags & TIMER_ABSTIME && clock_id == CLOCK_REALTIME) {
231-
key = k_spin_lock(&rt_clock_base_lock);
232-
ns -= rt_clock_base.tv_sec * NSEC_PER_SEC + rt_clock_base.tv_nsec;
233-
k_spin_unlock(&rt_clock_base_lock, key);
234-
}
263+
if (__posix_clock_timespec_less_than(rqtp, &clock_ts)) {
264+
goto post_sleep;
265+
}
235266

236-
if ((flags & TIMER_ABSTIME) == 0) {
237-
ns += uptime_ns;
267+
__posix_clock_timespec_subtract(&rel_ts, rqtp, &clock_ts);
268+
__posix_clock_get_monotonic(&clock_ts);
269+
__posix_clock_timespec_add(&abs_ts, &rel_ts, &clock_ts);
270+
} else if (flags & TIMER_ABSTIME) {
271+
__posix_clock_timespec_copy(&abs_ts, rqtp);
272+
} else {
273+
__posix_clock_get_monotonic(&clock_ts);
274+
__posix_clock_timespec_add(&abs_ts, rqtp, &clock_ts);
238275
}
239276

240-
if (ns <= uptime_ns) {
241-
goto do_rmtp_update;
242-
}
277+
usec = __posix_clock_timespec_to_usec(&abs_ts);
243278

244-
us = DIV_ROUND_UP(ns, NSEC_PER_USEC);
245279
do {
246-
us = k_sleep(K_TIMEOUT_ABS_US(us)) * 1000;
247-
} while (us != 0);
280+
usec = k_sleep(K_TIMEOUT_ABS_US(usec)) * USEC_PER_MSEC;
281+
} while (usec != 0);
248282

249-
do_rmtp_update:
283+
post_sleep:
250284
if (update_rmtp) {
251285
rmtp->tv_sec = 0;
252286
rmtp->tv_nsec = 0;
@@ -298,23 +332,3 @@ int clock_getcpuclockid(pid_t pid, clockid_t *clock_id)
298332

299333
return 0;
300334
}
301-
302-
#ifdef CONFIG_ZTEST
303-
#include <zephyr/ztest.h>
304-
static void reset_clock_base(void)
305-
{
306-
K_SPINLOCK(&rt_clock_base_lock) {
307-
rt_clock_base = (struct timespec){0};
308-
}
309-
}
310-
311-
static void clock_base_reset_rule_after(const struct ztest_unit_test *test, void *data)
312-
{
313-
ARG_UNUSED(test);
314-
ARG_UNUSED(data);
315-
316-
reset_clock_base();
317-
}
318-
319-
ZTEST_RULE(clock_base_reset_rule, NULL, clock_base_reset_rule_after);
320-
#endif /* CONFIG_ZTEST */

lib/posix/options/posix_clock.h

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)