1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "alloc-util.h"
26 #include "journald-rate-limit.h"
28 #include "random-util.h"
29 #include "string-util.h"
33 #define BUCKETS_MAX 127
34 #define GROUPS_MAX 2047
36 static const int priority_map
[] = {
47 typedef struct JournalRateLimitPool JournalRateLimitPool
;
48 typedef struct JournalRateLimitGroup JournalRateLimitGroup
;
50 struct JournalRateLimitPool
{
56 struct JournalRateLimitGroup
{
57 JournalRateLimit
*parent
;
60 JournalRateLimitPool pools
[POOLS_MAX
];
63 LIST_FIELDS(JournalRateLimitGroup
, bucket
);
64 LIST_FIELDS(JournalRateLimitGroup
, lru
);
67 struct JournalRateLimit
{
71 JournalRateLimitGroup
* buckets
[BUCKETS_MAX
];
72 JournalRateLimitGroup
*lru
, *lru_tail
;
79 JournalRateLimit
*journal_rate_limit_new(usec_t interval
, unsigned burst
) {
82 assert(interval
> 0 || burst
== 0);
84 r
= new0(JournalRateLimit
, 1);
88 r
->interval
= interval
;
91 random_bytes(r
->hash_key
, sizeof(r
->hash_key
));
96 static void journal_rate_limit_group_free(JournalRateLimitGroup
*g
) {
100 assert(g
->parent
->n_groups
> 0);
102 if (g
->parent
->lru_tail
== g
)
103 g
->parent
->lru_tail
= g
->lru_prev
;
105 LIST_REMOVE(lru
, g
->parent
->lru
, g
);
106 LIST_REMOVE(bucket
, g
->parent
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
108 g
->parent
->n_groups
--;
115 void journal_rate_limit_free(JournalRateLimit
*r
) {
119 journal_rate_limit_group_free(r
->lru
);
124 _pure_
static bool journal_rate_limit_group_expired(JournalRateLimitGroup
*g
, usec_t ts
) {
129 for (i
= 0; i
< POOLS_MAX
; i
++)
130 if (g
->pools
[i
].begin
+ g
->parent
->interval
>= ts
)
136 static void journal_rate_limit_vacuum(JournalRateLimit
*r
, usec_t ts
) {
139 /* Makes room for at least one new item, but drop all
140 * expored items too. */
142 while (r
->n_groups
>= GROUPS_MAX
||
143 (r
->lru_tail
&& journal_rate_limit_group_expired(r
->lru_tail
, ts
)))
144 journal_rate_limit_group_free(r
->lru_tail
);
147 static JournalRateLimitGroup
* journal_rate_limit_group_new(JournalRateLimit
*r
, const char *id
, usec_t ts
) {
148 JournalRateLimitGroup
*g
;
149 struct siphash state
;
154 g
= new0(JournalRateLimitGroup
, 1);
162 siphash24_init(&state
, r
->hash_key
);
163 string_hash_func(g
->id
, &state
);
164 g
->hash
= siphash24_finalize(&state
);
166 journal_rate_limit_vacuum(r
, ts
);
168 LIST_PREPEND(bucket
, r
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
169 LIST_PREPEND(lru
, r
->lru
, g
);
178 journal_rate_limit_group_free(g
);
182 static unsigned burst_modulate(unsigned burst
, uint64_t available
) {
185 /* Modulates the burst rate a bit with the amount of available
188 k
= u64log2(available
);
194 burst
= (burst
* (k
-16)) / 4;
210 int journal_rate_limit_test(JournalRateLimit
*r
, const char *id
, int priority
, uint64_t available
) {
212 JournalRateLimitGroup
*g
;
213 JournalRateLimitPool
*p
;
214 struct siphash state
;
222 * 0 → the log message shall be suppressed,
223 * 1 + n → the log message shall be permitted, and n messages were dropped from the peer before
230 if (r
->interval
== 0 || r
->burst
== 0)
233 burst
= burst_modulate(r
->burst
, available
);
235 ts
= now(CLOCK_MONOTONIC
);
237 siphash24_init(&state
, r
->hash_key
);
238 string_hash_func(id
, &state
);
239 h
= siphash24_finalize(&state
);
240 g
= r
->buckets
[h
% BUCKETS_MAX
];
242 LIST_FOREACH(bucket
, g
, g
)
243 if (streq(g
->id
, id
))
247 g
= journal_rate_limit_group_new(r
, id
, ts
);
252 p
= &g
->pools
[priority_map
[priority
]];
261 if (p
->begin
+ r
->interval
< ts
) {
272 if (p
->num
< burst
) {