1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "alloc-util.h"
27 #include "journald-rate-limit.h"
29 #include "random-util.h"
30 #include "string-util.h"
34 #define BUCKETS_MAX 127
35 #define GROUPS_MAX 2047
37 static const int priority_map
[] = {
48 typedef struct JournalRateLimitPool JournalRateLimitPool
;
49 typedef struct JournalRateLimitGroup JournalRateLimitGroup
;
51 struct JournalRateLimitPool
{
57 struct JournalRateLimitGroup
{
58 JournalRateLimit
*parent
;
61 JournalRateLimitPool pools
[POOLS_MAX
];
64 LIST_FIELDS(JournalRateLimitGroup
, bucket
);
65 LIST_FIELDS(JournalRateLimitGroup
, lru
);
68 struct JournalRateLimit
{
72 JournalRateLimitGroup
* buckets
[BUCKETS_MAX
];
73 JournalRateLimitGroup
*lru
, *lru_tail
;
80 JournalRateLimit
*journal_rate_limit_new(usec_t interval
, unsigned burst
) {
83 assert(interval
> 0 || burst
== 0);
85 r
= new0(JournalRateLimit
, 1);
89 r
->interval
= interval
;
92 random_bytes(r
->hash_key
, sizeof(r
->hash_key
));
97 static void journal_rate_limit_group_free(JournalRateLimitGroup
*g
) {
101 assert(g
->parent
->n_groups
> 0);
103 if (g
->parent
->lru_tail
== g
)
104 g
->parent
->lru_tail
= g
->lru_prev
;
106 LIST_REMOVE(lru
, g
->parent
->lru
, g
);
107 LIST_REMOVE(bucket
, g
->parent
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
109 g
->parent
->n_groups
--;
116 void journal_rate_limit_free(JournalRateLimit
*r
) {
120 journal_rate_limit_group_free(r
->lru
);
125 _pure_
static bool journal_rate_limit_group_expired(JournalRateLimitGroup
*g
, usec_t ts
) {
130 for (i
= 0; i
< POOLS_MAX
; i
++)
131 if (g
->pools
[i
].begin
+ g
->parent
->interval
>= ts
)
137 static void journal_rate_limit_vacuum(JournalRateLimit
*r
, usec_t ts
) {
140 /* Makes room for at least one new item, but drop all
141 * expored items too. */
143 while (r
->n_groups
>= GROUPS_MAX
||
144 (r
->lru_tail
&& journal_rate_limit_group_expired(r
->lru_tail
, ts
)))
145 journal_rate_limit_group_free(r
->lru_tail
);
148 static JournalRateLimitGroup
* journal_rate_limit_group_new(JournalRateLimit
*r
, const char *id
, usec_t ts
) {
149 JournalRateLimitGroup
*g
;
150 struct siphash state
;
155 g
= new0(JournalRateLimitGroup
, 1);
163 siphash24_init(&state
, r
->hash_key
);
164 string_hash_func(g
->id
, &state
);
165 g
->hash
= siphash24_finalize(&state
);
167 journal_rate_limit_vacuum(r
, ts
);
169 LIST_PREPEND(bucket
, r
->buckets
[g
->hash
% BUCKETS_MAX
], g
);
170 LIST_PREPEND(lru
, r
->lru
, g
);
179 journal_rate_limit_group_free(g
);
183 static unsigned burst_modulate(unsigned burst
, uint64_t available
) {
186 /* Modulates the burst rate a bit with the amount of available
189 k
= u64log2(available
);
195 burst
= (burst
* (k
-20)) / 4;
211 int journal_rate_limit_test(JournalRateLimit
*r
, const char *id
, int priority
, uint64_t available
) {
213 JournalRateLimitGroup
*g
;
214 JournalRateLimitPool
*p
;
215 struct siphash state
;
224 if (r
->interval
== 0 || r
->burst
== 0)
227 burst
= burst_modulate(r
->burst
, available
);
229 ts
= now(CLOCK_MONOTONIC
);
231 siphash24_init(&state
, r
->hash_key
);
232 string_hash_func(id
, &state
);
233 h
= siphash24_finalize(&state
);
234 g
= r
->buckets
[h
% BUCKETS_MAX
];
236 LIST_FOREACH(bucket
, g
, g
)
237 if (streq(g
->id
, id
))
241 g
= journal_rate_limit_group_new(r
, id
, ts
);
246 p
= &g
->pools
[priority_map
[priority
]];
255 if (p
->begin
+ r
->interval
< ts
) {
266 if (p
->num
<= burst
) {