]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-rate-limit.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / journal / journald-rate-limit.c
CommitLineData
6e409ce1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
6e409ce1
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
6e409ce1 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
6e409ce1
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
6e409ce1 22#include <errno.h>
07630cea 23#include <string.h>
6e409ce1 24
6e409ce1 25#include "hashmap.h"
07630cea 26#include "list.h"
3df3e884 27#include "random-util.h"
07630cea
LP
28#include "string-util.h"
29#include "util.h"
30#include "journald-rate-limit.h"
6e409ce1
LP
31
32#define POOLS_MAX 5
33#define BUCKETS_MAX 127
34#define GROUPS_MAX 2047
35
36static const int priority_map[] = {
37 [LOG_EMERG] = 0,
38 [LOG_ALERT] = 0,
39 [LOG_CRIT] = 0,
40 [LOG_ERR] = 1,
41 [LOG_WARNING] = 2,
42 [LOG_NOTICE] = 3,
43 [LOG_INFO] = 3,
44 [LOG_DEBUG] = 4
45};
46
47typedef struct JournalRateLimitPool JournalRateLimitPool;
48typedef struct JournalRateLimitGroup JournalRateLimitGroup;
49
50struct JournalRateLimitPool {
51 usec_t begin;
52 unsigned num;
53 unsigned suppressed;
54};
55
56struct JournalRateLimitGroup {
57 JournalRateLimit *parent;
58
59 char *id;
60 JournalRateLimitPool pools[POOLS_MAX];
0cb3c286 61 uint64_t hash;
6e409ce1
LP
62
63 LIST_FIELDS(JournalRateLimitGroup, bucket);
64 LIST_FIELDS(JournalRateLimitGroup, lru);
65};
66
67struct JournalRateLimit {
68 usec_t interval;
69 unsigned burst;
70
71 JournalRateLimitGroup* buckets[BUCKETS_MAX];
72 JournalRateLimitGroup *lru, *lru_tail;
73
74 unsigned n_groups;
9bf3b535
LP
75
76 uint8_t hash_key[16];
6e409ce1
LP
77};
78
79JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) {
80 JournalRateLimit *r;
81
82 assert(interval > 0 || burst == 0);
83
84 r = new0(JournalRateLimit, 1);
85 if (!r)
86 return NULL;
87
88 r->interval = interval;
89 r->burst = burst;
90
9bf3b535
LP
91 random_bytes(r->hash_key, sizeof(r->hash_key));
92
6e409ce1
LP
93 return r;
94}
95
96static void journal_rate_limit_group_free(JournalRateLimitGroup *g) {
97 assert(g);
98
99 if (g->parent) {
100 assert(g->parent->n_groups > 0);
101
102 if (g->parent->lru_tail == g)
103 g->parent->lru_tail = g->lru_prev;
104
71fda00f
LP
105 LIST_REMOVE(lru, g->parent->lru, g);
106 LIST_REMOVE(bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g);
6e409ce1
LP
107
108 g->parent->n_groups --;
109 }
110
111 free(g->id);
112 free(g);
113}
114
115void journal_rate_limit_free(JournalRateLimit *r) {
116 assert(r);
117
118 while (r->lru)
119 journal_rate_limit_group_free(r->lru);
783d2675
LP
120
121 free(r);
6e409ce1
LP
122}
123
44a6b1b6 124_pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
6e409ce1
LP
125 unsigned i;
126
127 assert(g);
128
129 for (i = 0; i < POOLS_MAX; i++)
130 if (g->pools[i].begin + g->parent->interval >= ts)
131 return false;
132
133 return true;
134}
135
136static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
137 assert(r);
138
139 /* Makes room for at least one new item, but drop all
140 * expored items too. */
141
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);
145}
146
147static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
148 JournalRateLimitGroup *g;
b826ab58 149 struct siphash state;
6e409ce1
LP
150
151 assert(r);
152 assert(id);
153
154 g = new0(JournalRateLimitGroup, 1);
155 if (!g)
156 return NULL;
157
158 g->id = strdup(id);
159 if (!g->id)
160 goto fail;
161
0cb3c286 162 siphash24_init(&state, r->hash_key);
b826ab58 163 string_hash_func(g->id, &state);
0cb3c286 164 siphash24_finalize((uint8_t*)&g->hash, &state);
6e409ce1
LP
165
166 journal_rate_limit_vacuum(r, ts);
167
71fda00f
LP
168 LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g);
169 LIST_PREPEND(lru, r->lru, g);
6e409ce1
LP
170 if (!g->lru_next)
171 r->lru_tail = g;
172 r->n_groups ++;
173
174 g->parent = r;
175 return g;
176
177fail:
178 journal_rate_limit_group_free(g);
179 return NULL;
180}
181
6e409ce1
LP
182static unsigned burst_modulate(unsigned burst, uint64_t available) {
183 unsigned k;
184
185 /* Modulates the burst rate a bit with the amount of available
186 * disk space */
187
188 k = u64log2(available);
189
190 /* 1MB */
191 if (k <= 20)
192 return burst;
193
194 burst = (burst * (k-20)) / 4;
195
196 /*
197 * Example:
198 *
199 * <= 1MB = rate * 1
200 * 16MB = rate * 2
201 * 256MB = rate * 3
202 * 4GB = rate * 4
203 * 64GB = rate * 5
204 * 1TB = rate * 6
205 */
206
207 return burst;
208}
209
210int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
0cb3c286 211 uint64_t h;
6e409ce1
LP
212 JournalRateLimitGroup *g;
213 JournalRateLimitPool *p;
b826ab58 214 struct siphash state;
6e409ce1
LP
215 unsigned burst;
216 usec_t ts;
217
218 assert(id);
219
220 if (!r)
221 return 1;
222
223 if (r->interval == 0 || r->burst == 0)
224 return 1;
225
226 burst = burst_modulate(r->burst, available);
227
228 ts = now(CLOCK_MONOTONIC);
229
0cb3c286 230 siphash24_init(&state, r->hash_key);
b826ab58 231 string_hash_func(id, &state);
0cb3c286 232 siphash24_finalize((uint8_t*)&h, &state);
6e409ce1
LP
233 g = r->buckets[h % BUCKETS_MAX];
234
235 LIST_FOREACH(bucket, g, g)
236 if (streq(g->id, id))
237 break;
238
239 if (!g) {
240 g = journal_rate_limit_group_new(r, id, ts);
241 if (!g)
242 return -ENOMEM;
243 }
244
245 p = &g->pools[priority_map[priority]];
246
247 if (p->begin <= 0) {
248 p->suppressed = 0;
249 p->num = 1;
250 p->begin = ts;
251 return 1;
252 }
253
254 if (p->begin + r->interval < ts) {
255 unsigned s;
256
257 s = p->suppressed;
258 p->suppressed = 0;
259 p->num = 1;
260 p->begin = ts;
261
262 return 1 + s;
263 }
264
265 if (p->num <= burst) {
266 p->num++;
267 return 1;
268 }
269
270 p->suppressed++;
271 return 0;
272}