core: add new RandomSec= setting for time units

This allows configuration of a random time on top of the elapse events,
in order to spread time events in a network evenly across a range.
This commit is contained in:
Lennart Poettering 2015-11-18 13:37:30 +01:00
parent 45090bf2ff
commit 744c769375
6 changed files with 101 additions and 6 deletions

View file

@ -190,13 +190,12 @@
<varname>OnUnitInactiveSec=</varname> and ending the time
configured with <varname>AccuracySec=</varname> later. Within
this time window, the expiry time will be placed at a
host-specific, randomized but stable position that is
host-specific, randomized, but stable position that is
synchronized between all local timer units. This is done in
order to distribute the wake-up time in networked
installations, as well as optimizing power consumption to
suppress unnecessary CPU wake-ups. To get best accuracy, set
this option to 1us. Note that the timer is still subject to
the timer slack configured via
order to optimize power consumption to suppress unnecessary
CPU wake-ups. To get best accuracy, set this option to
1us. Note that the timer is still subject to the timer slack
configured via
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
<varname>TimerSlackNSec=</varname> setting. See
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
@ -204,6 +203,38 @@
this value as high as possible and as low as
necessary.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RandomSec=</varname></term>
<listitem><para>Delay the timer by a randomly selected, evenly
distributed amount of time between 0 and the specified time
value. Defaults to 0, indicating that no randomized delay
shall be applied. Each timer unit will determine this delay
randomly each time it is started, and the delay will simply be
added on top of the next determined elapsing time. This is
useful to stretch dispatching of similarly configured timer
events over a certain amount time, to avoid that they all fire
at the same time, possibly resulting in resource
congestion. Note the relation to
<varname>AccuracySec=</varname> above: the latter allows the
service manager to coalesce timer events within a specified
time range in order to minimize wakeups, the former does the
opposite: it stretches timer events over a time range, to make
it unlikely that they fire simultaneously. If
<varname>RandomSec=</varname> and
<varname>AccuracySec=</varname> are used in conjunction, first
the a randomized time is added, and the result is then
possibly shifted further to coalesce it with other timer
events possibly happening on the system. As mentioned above
<varname>AccuracySec=</varname> defaults to 1min and
<varname>RandomSec=</varname> to 0, thus encouraging
coalescing of timer events. In order to optimally stretch
timer events over a certain range of time, make sure to set
<varname>RandomSec=</varname> to a higher value, and
<varname>AccuracySec=1us</varname>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Unit=</varname></term>

View file

@ -180,6 +180,7 @@ const sd_bus_vtable bus_timer_vtable[] = {
BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RandomUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
@ -283,6 +284,22 @@ static int bus_timer_set_transient_property(
return 1;
} else if (streq(name, "RandomUSec")) {
usec_t u = 0;
r = sd_bus_message_read(message, "t", &u);
if (r < 0)
return r;
if (mode != UNIT_CHECK) {
char time[FORMAT_TIMESPAN_MAX];
t->random_usec = u;
unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomSec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
}
return 1;
} else if (streq(name, "WakeSystem")) {
int b;

View file

@ -347,6 +347,7 @@ Timer.Persistent, config_parse_bool, 0,
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
Timer.RandomSec, config_parse_sec, 0, offsetof(Timer, random_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0

View file

@ -27,6 +27,7 @@
#include "dbus-timer.h"
#include "fs-util.h"
#include "parse-util.h"
#include "random-util.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@ -330,6 +331,28 @@ static usec_t monotonic_to_boottime(usec_t t) {
return 0;
}
static void add_random(Timer *t, usec_t *v) {
char s[FORMAT_TIMESPAN_MAX];
usec_t add;
assert(t);
assert(*v);
if (t->random_usec == 0)
return;
if (*v == USEC_INFINITY)
return;
add = random_u64() % t->random_usec;
if (*v + add < *v) /* overflow */
*v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
else
*v += add;
log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
}
static void timer_enter_waiting(Timer *t, bool initial) {
bool found_monotonic = false, found_realtime = false;
usec_t ts_realtime, ts_monotonic;
@ -452,6 +475,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
char buf[FORMAT_TIMESPAN_MAX];
usec_t left;
add_random(t, &t->next_elapse_monotonic_or_boottime);
left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
@ -486,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
add_random(t, &t->next_elapse_realtime);
log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
if (t->realtime_event_source) {

View file

@ -58,6 +58,7 @@ struct Timer {
Unit meta;
usec_t accuracy_usec;
usec_t random_usec;
LIST_HEAD(TimerValue, values);
usec_t next_elapse_realtime;

View file

@ -1442,6 +1442,23 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return bus_log_create_error(r);
return 0;
} else if (streq(field, "RandomSec")) {
usec_t t;
r = parse_sec(eq, &t);
if (r < 0)
return log_error_errno(r, "Failed to parse RandomSec= parameter: %s", eq);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomUSec");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "v", "t", t);
if (r < 0)
return bus_log_create_error(r);
return 0;
}
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);