Skip to content

Commit

Permalink
core: add new RandomSec= setting for time units
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
poettering committed Nov 18, 2015
1 parent 45090bf commit 744c769
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 6 deletions.
43 changes: 37 additions & 6 deletions man/systemd.timer.xml
Expand Up @@ -190,20 +190,51 @@
<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>
for details. To optimize power consumption, make sure to set
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>

Expand Down
17 changes: 17 additions & 0 deletions src/core/dbus-timer.c
Expand Up @@ -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),
Expand Down Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions src/core/load-fragment-gperf.gperf.m4
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions src/core/timer.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/core/timer.h
Expand Up @@ -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;
Expand Down
17 changes: 17 additions & 0 deletions src/shared/bus-util.c
Expand Up @@ -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);
Expand Down

0 comments on commit 744c769

Please sign in to comment.