1#include <cmdline.h>
2#include <enforce.h>
3#include <string.h>
4#include <sync/seqlock.h>
5#include <time/clock.h>
6#include <time/time.h>
7#include <time/timekeeper.h>
8#include <time/tsc.h>
9
10#include "internal.h"
11
12struct timekeeper {
13 struct seqlock lock;
14 struct clock *clock;
15 uint64_t raw_base_cycles;
16 uint64_t base_ns;
17};
18
19static struct timekeeper timekeeper = {
20 .lock = SEQLOCK_INIT,
21 .clock = NULL,
22 .raw_base_cycles = 0,
23 .base_ns = 0,
24};
25
26CMDLINE_DEFINE_STATIC(timekeeper, .flags = CMDLINE_ENTRY_SYMBOLIC,
27 .desc = "Timekeeper subsystem parent node");
28
29static char *clock_to_use = NULL;
30CMDLINE_CHILDREN_DEFINE(timekeeper,
31 CMDLINE_INNER_STRING(clock, clock_to_use,
32 .arg = "<clock>",
33 .default_val = "auto"));
34
35static struct clock *timekeeper_get_clock(void) {
36 if (clock_to_use && strcmp(str1: clock_to_use, str2: "auto") != 0) {
37 return clock_get_by_name(name: clock_to_use);
38 } else {
39 if (tsc_should_use_tsc()) {
40 return tsc_clock_init(freq_hz: smp_bsp()->tsc_hz);
41 } else {
42 return hpet_clock_init();
43 }
44 }
45}
46
47void timekeeper_init(void) {
48 struct clock *clk = enforce(timekeeper_get_clock(), UNINITIALIZED);
49
50 enum irql irql = seq_write_lock(sl: &timekeeper.lock);
51 timekeeper.clock = clk;
52 timekeeper.raw_base_cycles = clk->read(clk);
53 timekeeper.base_ns = 0;
54 seq_write_unlock(sl: &timekeeper.lock, old: irql);
55}
56
57void timekeeper_set_clock(struct clock *cc_nonnull_ptr clk) {
58 enforce_precond(clk != NULL, "timekeeper_set_clock called with NULL clock");
59
60 enum irql irql = seq_write_lock_high(sl: &timekeeper.lock);
61 if (timekeeper.clock) {
62 uint64_t now = timekeeper.clock->read(timekeeper.clock);
63 uint64_t delta = (now >= timekeeper.raw_base_cycles)
64 ? (now - timekeeper.raw_base_cycles)
65 : 0;
66 timekeeper.base_ns += clock_cycles_to_ns(clock: timekeeper.clock, cycles: delta);
67 }
68 timekeeper.clock = clk;
69 timekeeper.raw_base_cycles = clk->read(clk);
70 seq_write_unlock(sl: &timekeeper.lock, old: irql);
71}
72
73time_ns_t timekeeper_get_ns(void) {
74 uint32_t seq;
75 uint64_t cycles, delta, base_ns;
76 struct clock *clk;
77
78 do {
79 seq = seq_begin_read(sl: &timekeeper.lock);
80 clk = timekeeper.clock;
81 if (cc_unlikely(!clk)) {
82 seq_read_retry(sl: &timekeeper.lock, start: seq);
83 return 0;
84 }
85 cycles = clk->read(clk);
86 delta = (cycles >= timekeeper.raw_base_cycles)
87 ? (cycles - timekeeper.raw_base_cycles)
88 : 0;
89 base_ns = timekeeper.base_ns;
90 } while (seq_read_retry(sl: &timekeeper.lock, start: seq));
91
92 return base_ns + clock_cycles_to_ns(clock: clk, cycles: delta);
93}
94
95/* Give up instead of waiting for a writer, for the non-blocking sections */
96bool timekeeper_try_get_ns(time_ns_t *out) {
97 uint32_t seq;
98 uint64_t cycles, delta, base_ns;
99 struct clock *clk;
100 int retries = TIMEKEEPER_TRY_READ_SPINS;
101
102 do {
103 seq = seq_begin_read_raw(sl: &timekeeper.lock);
104 if (cc_unlikely((seq & 1) != 0))
105 continue;
106
107 clk = timekeeper.clock;
108 if (cc_unlikely(!clk))
109 return false;
110
111 cycles = clk->read(clk);
112 delta = (cycles >= timekeeper.raw_base_cycles)
113 ? (cycles - timekeeper.raw_base_cycles)
114 : 0;
115 base_ns = timekeeper.base_ns;
116
117 if (!seq_read_retry(sl: &timekeeper.lock, start: seq)) {
118 *out = base_ns + clock_cycles_to_ns(clock: clk, cycles: delta);
119 return true;
120 }
121 } while (--retries > 0);
122
123 return false;
124}
125
126time_us_t timekeeper_get_us(void) {
127 return NS_TO_US(timekeeper_get_ns());
128}
129