1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2011 Lennart Poettering
9 #include "alloc-util.h"
11 #include "journald-rate-limit.h"
13 #include "random-util.h"
14 #include "string-util.h"
18 #define BUCKETS_MAX 127
19 #define GROUPS_MAX 2047
21 static const int priority_map
[] = {
32 typedef struct JournalRateLimitPool JournalRateLimitPool
;
33 typedef struct JournalRateLimitGroup JournalRateLimitGroup
;
35 struct JournalRateLimitPool
{
41 struct JournalRateLimitGroup
{
42 JournalRateLimit
*parent
;
45 JournalRateLimitPool pools
[POOLS_MAX
];
48 LIST_FIELDS(JournalRateLimitGroup
, bucket
);
49 LIST_FIELDS(JournalRateLimitGroup
, lru
);
52 struct JournalRateLimit
{
56 JournalRateLimitGroup
* buckets
[BUCKETS_MAX
];
57 JournalRateLimitGroup
*lru
, *lru_tail
;
64 JournalRateLimit
*journal_rate_limit_new(usec_t interval
, unsigned burst
) {
67 assert(interval
> 0 || burst
== 0);
69 r
= new0(JournalRateLimit
, 1);
73 r
->interval
= interval
;
76 random_bytes(r
->hash_key
, sizeof(r
->hash_key
));
81 static void journal_rate_limit_group_free(JournalRateLimitGroup
*g
) {
85 assert(g
->parent
->n_groups
> 0);
87 if (g
->parent
->lru_tail
== g
)
88 g
->parent
->lru_tail
= g
->lru_prev
;
90 LIST_REMOVE(lru
, g
->parent
->lru
, g
);
91 LIST_REMOVE(bucket
, g
->parent
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
93 g
->parent
->n_groups
--;
100 void journal_rate_limit_free(JournalRateLimit
*r
) {
104 journal_rate_limit_group_free(r
->lru
);
109 _pure_
static bool journal_rate_limit_group_expired(JournalRateLimitGroup
*g
, usec_t ts
) {
114 for (i
= 0; i
< POOLS_MAX
; i
++)
115 if (g
->pools
[i
].begin
+ g
->parent
->interval
>= ts
)
121 static void journal_rate_limit_vacuum(JournalRateLimit
*r
, usec_t ts
) {
124 /* Makes room for at least one new item, but drop all
125 * expored items too. */
127 while (r
->n_groups
>= GROUPS_MAX
||
128 (r
->lru_tail
&& journal_rate_limit_group_expired(r
->lru_tail
, ts
)))
129 journal_rate_limit_group_free(r
->lru_tail
);
132 static JournalRateLimitGroup
* journal_rate_limit_group_new(JournalRateLimit
*r
, const char *id
, usec_t ts
) {
133 JournalRateLimitGroup
*g
;
134 struct siphash state
;
139 g
= new0(JournalRateLimitGroup
, 1);
147 siphash24_init(&state
, r
->hash_key
);
148 string_hash_func(g
->id
, &state
);
149 g
->hash
= siphash24_finalize(&state
);
151 journal_rate_limit_vacuum(r
, ts
);
153 LIST_PREPEND(bucket
, r
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
154 LIST_PREPEND(lru
, r
->lru
, g
);
163 journal_rate_limit_group_free(g
);
167 static unsigned burst_modulate(unsigned burst
, uint64_t available
) {
170 /* Modulates the burst rate a bit with the amount of available
173 k
= u64log2(available
);
179 burst
= (burst
* (k
-16)) / 4;
195 int journal_rate_limit_test(JournalRateLimit
*r
, const char *id
, int priority
, uint64_t available
) {
197 JournalRateLimitGroup
*g
;
198 JournalRateLimitPool
*p
;
199 struct siphash state
;
207 * 0 → the log message shall be suppressed,
208 * 1 + n → the log message shall be permitted, and n messages were dropped from the peer before
215 if (r
->interval
== 0 || r
->burst
== 0)
218 burst
= burst_modulate(r
->burst
, available
);
220 ts
= now(CLOCK_MONOTONIC
);
222 siphash24_init(&state
, r
->hash_key
);
223 string_hash_func(id
, &state
);
224 h
= siphash24_finalize(&state
);
225 g
= r
->buckets
[h
% BUCKETS_MAX
];
227 LIST_FOREACH(bucket
, g
, g
)
228 if (streq(g
->id
, id
))
232 g
= journal_rate_limit_group_new(r
, id
, ts
);
237 p
= &g
->pools
[priority_map
[priority
]];
246 if (p
->begin
+ r
->interval
< ts
) {
257 if (p
->num
< burst
) {