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