1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
7 #include "journald-rate-limit.h"
9 #include "random-util.h"
10 #include "string-util.h"
11 #include "time-util.h"
14 #define BUCKETS_MAX 127
15 #define GROUPS_MAX 2047
17 static const int priority_map
[] = {
28 typedef struct JournalRateLimitPool JournalRateLimitPool
;
29 typedef struct JournalRateLimitGroup JournalRateLimitGroup
;
31 struct JournalRateLimitPool
{
37 struct JournalRateLimitGroup
{
38 JournalRateLimit
*parent
;
42 /* Interval is stored to keep track of when the group expires */
45 JournalRateLimitPool pools
[POOLS_MAX
];
48 LIST_FIELDS(JournalRateLimitGroup
, bucket
);
49 LIST_FIELDS(JournalRateLimitGroup
, lru
);
52 struct JournalRateLimit
{
54 JournalRateLimitGroup
* buckets
[BUCKETS_MAX
];
55 JournalRateLimitGroup
*lru
, *lru_tail
;
62 JournalRateLimit
*journal_ratelimit_new(void) {
65 r
= new0(JournalRateLimit
, 1);
69 random_bytes(r
->hash_key
, sizeof(r
->hash_key
));
74 static void journal_ratelimit_group_free(JournalRateLimitGroup
*g
) {
78 assert(g
->parent
->n_groups
> 0);
80 if (g
->parent
->lru_tail
== g
)
81 g
->parent
->lru_tail
= g
->lru_prev
;
83 LIST_REMOVE(lru
, g
->parent
->lru
, g
);
84 LIST_REMOVE(bucket
, g
->parent
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
86 g
->parent
->n_groups
--;
93 void journal_ratelimit_free(JournalRateLimit
*r
) {
97 journal_ratelimit_group_free(r
->lru
);
102 static bool journal_ratelimit_group_expired(JournalRateLimitGroup
*g
, usec_t ts
) {
107 for (i
= 0; i
< POOLS_MAX
; i
++)
108 if (g
->pools
[i
].begin
+ g
->interval
>= ts
)
114 static void journal_ratelimit_vacuum(JournalRateLimit
*r
, usec_t ts
) {
117 /* Makes room for at least one new item, but drop all
118 * expored items too. */
120 while (r
->n_groups
>= GROUPS_MAX
||
121 (r
->lru_tail
&& journal_ratelimit_group_expired(r
->lru_tail
, ts
)))
122 journal_ratelimit_group_free(r
->lru_tail
);
125 static JournalRateLimitGroup
* journal_ratelimit_group_new(JournalRateLimit
*r
, const char *id
, usec_t interval
, usec_t ts
) {
126 JournalRateLimitGroup
*g
;
131 g
= new0(JournalRateLimitGroup
, 1);
139 g
->hash
= siphash24_string(g
->id
, r
->hash_key
);
141 g
->interval
= interval
;
143 journal_ratelimit_vacuum(r
, ts
);
145 LIST_PREPEND(bucket
, r
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
146 LIST_PREPEND(lru
, r
->lru
, g
);
155 journal_ratelimit_group_free(g
);
159 static unsigned burst_modulate(unsigned burst
, uint64_t available
) {
162 /* Modulates the burst rate a bit with the amount of available
165 k
= u64log2(available
);
171 burst
= (burst
* (k
-16)) / 4;
187 int journal_ratelimit_test(JournalRateLimit
*r
, const char *id
, usec_t rl_interval
, unsigned rl_burst
, int priority
, uint64_t available
) {
189 JournalRateLimitGroup
*g
;
190 JournalRateLimitPool
*p
;
198 * 0 → the log message shall be suppressed,
199 * 1 + n → the log message shall be permitted, and n messages were dropped from the peer before
206 ts
= now(CLOCK_MONOTONIC
);
208 h
= siphash24_string(id
, r
->hash_key
);
209 g
= r
->buckets
[h
% BUCKETS_MAX
];
211 LIST_FOREACH(bucket
, g
, g
)
212 if (streq(g
->id
, id
))
216 g
= journal_ratelimit_group_new(r
, id
, rl_interval
, ts
);
220 g
->interval
= rl_interval
;
222 if (rl_interval
== 0 || rl_burst
== 0)
225 burst
= burst_modulate(rl_burst
, available
);
227 p
= &g
->pools
[priority_map
[priority
]];
236 if (p
->begin
+ rl_interval
< ts
) {
247 if (p
->num
< burst
) {