1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include "alloc-util.h"
8 #include "journald-rate-limit.h"
10 #include "random-util.h"
11 #include "string-util.h"
15 #define BUCKETS_MAX 127
16 #define GROUPS_MAX 2047
18 static const int priority_map
[] = {
29 typedef struct JournalRateLimitPool JournalRateLimitPool
;
30 typedef struct JournalRateLimitGroup JournalRateLimitGroup
;
32 struct JournalRateLimitPool
{
38 struct JournalRateLimitGroup
{
39 JournalRateLimit
*parent
;
43 /* Interval is stored to keep track of when the group expires */
46 JournalRateLimitPool pools
[POOLS_MAX
];
49 LIST_FIELDS(JournalRateLimitGroup
, bucket
);
50 LIST_FIELDS(JournalRateLimitGroup
, lru
);
53 struct JournalRateLimit
{
55 JournalRateLimitGroup
* buckets
[BUCKETS_MAX
];
56 JournalRateLimitGroup
*lru
, *lru_tail
;
63 JournalRateLimit
*journal_rate_limit_new(void) {
66 r
= new0(JournalRateLimit
, 1);
70 random_bytes(r
->hash_key
, sizeof(r
->hash_key
));
75 static void journal_rate_limit_group_free(JournalRateLimitGroup
*g
) {
79 assert(g
->parent
->n_groups
> 0);
81 if (g
->parent
->lru_tail
== g
)
82 g
->parent
->lru_tail
= g
->lru_prev
;
84 LIST_REMOVE(lru
, g
->parent
->lru
, g
);
85 LIST_REMOVE(bucket
, g
->parent
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
87 g
->parent
->n_groups
--;
94 void journal_rate_limit_free(JournalRateLimit
*r
) {
98 journal_rate_limit_group_free(r
->lru
);
103 _pure_
static bool journal_rate_limit_group_expired(JournalRateLimitGroup
*g
, usec_t ts
) {
108 for (i
= 0; i
< POOLS_MAX
; i
++)
109 if (g
->pools
[i
].begin
+ g
->interval
>= ts
)
115 static void journal_rate_limit_vacuum(JournalRateLimit
*r
, usec_t ts
) {
118 /* Makes room for at least one new item, but drop all
119 * expored items too. */
121 while (r
->n_groups
>= GROUPS_MAX
||
122 (r
->lru_tail
&& journal_rate_limit_group_expired(r
->lru_tail
, ts
)))
123 journal_rate_limit_group_free(r
->lru_tail
);
126 static JournalRateLimitGroup
* journal_rate_limit_group_new(JournalRateLimit
*r
, const char *id
, usec_t interval
, usec_t ts
) {
127 JournalRateLimitGroup
*g
;
132 g
= new0(JournalRateLimitGroup
, 1);
140 g
->hash
= siphash24_string(g
->id
, r
->hash_key
);
142 g
->interval
= interval
;
144 journal_rate_limit_vacuum(r
, ts
);
146 LIST_PREPEND(bucket
, r
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
147 LIST_PREPEND(lru
, r
->lru
, g
);
156 journal_rate_limit_group_free(g
);
160 static unsigned burst_modulate(unsigned burst
, uint64_t available
) {
163 /* Modulates the burst rate a bit with the amount of available
166 k
= u64log2(available
);
172 burst
= (burst
* (k
-16)) / 4;
188 int journal_rate_limit_test(JournalRateLimit
*r
, const char *id
, usec_t rl_interval
, unsigned rl_burst
, int priority
, uint64_t available
) {
190 JournalRateLimitGroup
*g
;
191 JournalRateLimitPool
*p
;
199 * 0 → the log message shall be suppressed,
200 * 1 + n → the log message shall be permitted, and n messages were dropped from the peer before
207 ts
= now(CLOCK_MONOTONIC
);
209 h
= siphash24_string(id
, r
->hash_key
);
210 g
= r
->buckets
[h
% BUCKETS_MAX
];
212 LIST_FOREACH(bucket
, g
, g
)
213 if (streq(g
->id
, id
))
217 g
= journal_rate_limit_group_new(r
, id
, rl_interval
, ts
);
221 g
->interval
= rl_interval
;
223 if (rl_interval
== 0 || rl_burst
== 0)
226 burst
= burst_modulate(rl_burst
, available
);
228 p
= &g
->pools
[priority_map
[priority
]];
237 if (p
->begin
+ rl_interval
< ts
) {
248 if (p
->num
< burst
) {