1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdbool.h>
#include "lib/defines.h"
#include "lib/utils.h"
#include "lib/kru.h"
/// Initialize defer, incl. shared memory with KRU, excl. idle.
KR_EXPORT
int defer_init(const char *mmap_file, uint32_t log_period, int cpus);
/// Initialize idle.
int defer_init_idle(uv_loop_t *loop);
/// Deinitialize shared memory.
void defer_deinit(void);
/// Increment KRU counters by the given time.
void defer_charge(uint64_t nsec, union kr_sockaddr *addr, bool stream);
typedef struct {
bool is_accounting; /// whether currently accounting the time to someone
union kr_sockaddr addr; /// request source (to which we account) or AF_UNSPEC if unknown yet
bool stream;
uint64_t stamp; /// monotonic nanoseconds, probably won't wrap
} defer_sample_state_t;
extern defer_sample_state_t defer_sample_state;
extern struct defer *defer; /// skip sampling/deferring if NULL
extern bool defer_initialized; /// defer_init was called, possibly keeping defer disabled
extern uint64_t defer_uvtime_stamp; /// stamp of the last uv time update
// TODO: reconsider `static inline` cases below
#include <time.h>
static inline uint64_t defer_get_stamp(void)
{
struct timespec now_ts = {0};
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now_ts);
uint64_t stamp = now_ts.tv_nsec + 1000*1000*1000 * (uint64_t)now_ts.tv_sec;
if (defer_uvtime_stamp + 1000*1000 < stamp) {
defer_uvtime_stamp = stamp;
uv_update_time(uv_default_loop());
}
return stamp;
}
/// Annotate the work currently being accounted by an IP address.
static inline void defer_sample_addr(const union kr_sockaddr *addr, bool stream)
{
if (!defer || kr_fails_assert(addr)) return;
if (!defer_sample_state.is_accounting) return;
if (defer_sample_state.addr.ip.sa_family != AF_UNSPEC) {
// TODO: this costs performance, so only in some debug mode?
if (kr_sockaddr_cmp(&addr->ip, &defer_sample_state.addr.ip) != kr_ok()) {
char defer_addr[KR_STRADDR_MAXLEN + 1] = { 0 };
strncpy(defer_addr, kr_straddr(&defer_sample_state.addr.ip), sizeof(defer_addr) - 1);
kr_log_warning(DEFER, "Sampling address mismatch: %s != %s\n",
kr_straddr(&addr->ip),
defer_addr);
return;
}
}
switch (addr->ip.sa_family) {
case AF_INET:
defer_sample_state.addr.ip4 = addr->ip4;
break;
case AF_INET6:
defer_sample_state.addr.ip6 = addr->ip6;
break;
default:
defer_sample_state.addr.ip.sa_family = AF_UNSPEC;
break;
}
defer_sample_state.stream = stream;
}
/// Internal; start accounting work at specified timestamp.
static inline void defer_sample_start_stamp(uint64_t stamp)
{
if (!defer) return;
kr_assert(!defer_sample_state.is_accounting);
defer_sample_state.is_accounting = true;
defer_sample_state.stamp = stamp;
defer_sample_state.addr.ip.sa_family = AF_UNSPEC;
}
/// Internal; stop accounting work at specified timestamp and charge the source if applicable.
static inline void defer_sample_stop_stamp(uint64_t stamp)
{
if (!defer) return;
kr_assert(defer_sample_state.is_accounting);
defer_sample_state.is_accounting = false;
if (defer_sample_state.addr.ip.sa_family == AF_UNSPEC) return;
const uint64_t elapsed = stamp - defer_sample_state.stamp;
if (elapsed == 0) return;
// TODO: some queries of internal origin have suspicioiusly high numbers.
// We won't be really accounting those, but it might suggest some other issue.
defer_charge(elapsed, &defer_sample_state.addr, defer_sample_state.stream);
}
static inline bool defer_sample_is_accounting(void)
{
return defer_sample_state.is_accounting;
}
/// Start accounting work; optionally save state of current accounting.
/// Current state can be saved only after having an address assigned.
static inline void defer_sample_start(defer_sample_state_t *prev_state_out) {
if (!defer) return;
uint64_t stamp = defer_get_stamp();
// suspend
if (prev_state_out) {
*prev_state_out = defer_sample_state; // TODO stamp is not needed
if (defer_sample_state.is_accounting)
defer_sample_stop_stamp(stamp);
}
// start
defer_sample_start_stamp(stamp);
}
/// Stop accounting and start it again.
static inline void defer_sample_restart(void) {
if (!defer) return;
uint64_t stamp = defer_get_stamp();
// stop
defer_sample_stop_stamp(stamp);
// start
defer_sample_start_stamp(stamp);
}
/// Stop accounting and charge the source if applicable; optionally resume previous accounting.
static inline void defer_sample_stop(defer_sample_state_t *prev_state, bool reuse_last_stamp) {
if (!defer) return;
uint64_t stamp = reuse_last_stamp ? defer_sample_state.stamp : defer_get_stamp();
// stop
defer_sample_stop_stamp(stamp);
// resume
if (prev_state) {
defer_sample_state = *prev_state;
defer_sample_state.stamp = stamp;
}
}
|