Files
systemd/src/basic/ratelimit.c
T
Frantisek Sumsal 9ae50c78cc sd-event: use CLOCK_BOOTTIME for rate limits
When a rate limit is armed, for example via StartLimitInterval=, and the
machine is suspended, the rate limit's "clock" is suspended as well,
since the elapse checks use CLOCK_MONOTONIC. This then causes unexpected
situations where a rate limit armed via StartLimitInterval=1h, followed
by a 10 hour suspend, would elapse after 11 hours total.

Let's avoid this by switching the rate limits to CLOCK_BOOTTIME, which
works the same as CLOCK_MONOTONIC, but accounts for the time spent in
suspend as well.

There's one slight concern when it comes to upgrade path - the old
"begin" value of the rate limit is stored as CLOCK_MONOTONIC, but after
upgrading systemd and serializing/deserializing the state it will be
suddenly compared against now(CLOCK_BOOTTIME), which might cause some
rate limits to elapse "prematurely". But this is just a one-time thing,
after which the rate limit timers should re-assess themselves.

Resolves: #42912
2026-07-08 15:38:39 +01:00

59 lines
1.3 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "ratelimit.h"
#include "time-util.h"
/* Modelled after Linux' lib/ratelimit.c by Dave Young
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
bool ratelimit_below(RateLimit *rl) {
usec_t ts;
assert(rl);
if (!ratelimit_configured(rl))
return true;
ts = now(CLOCK_BOOTTIME);
if (rl->begin <= 0 ||
usec_sub_unsigned(ts, rl->begin) > rl->interval) {
rl->begin = ts; /* Start a new time window */
rl->num = 1; /* Reset counter */
return true;
}
if (_unlikely_(rl->num == UINT_MAX))
return false;
rl->num++;
return rl->num <= rl->burst;
}
unsigned ratelimit_num_dropped(const RateLimit *rl) {
assert(rl);
if (rl->num == UINT_MAX) /* overflow, return as special case */
return UINT_MAX;
return LESS_BY(rl->num, rl->burst);
}
usec_t ratelimit_end(const RateLimit *rl) {
assert(rl);
if (rl->begin == 0)
return 0;
return usec_add(rl->begin, rl->interval);
}
usec_t ratelimit_left(const RateLimit *rl) {
assert(rl);
if (rl->begin == 0)
return 0;
return usec_sub_unsigned(ratelimit_end(rl), now(CLOCK_BOOTTIME));
}