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