6636126
From d2686a718564c0873e2ed7138e7a31471279c1ec Mon Sep 17 00:00:00 2001
6636126
From: Lennart Poettering <lennart@poettering.net>
6636126
Date: Wed, 18 Nov 2015 13:37:30 +0100
6636126
Subject: [PATCH 5/6] core: add new RandomSec= setting for time units
6636126
6636126
This allows configuration of a random time on top of the elapse events,
6636126
in order to spread time events in a network evenly across a range.
6636126
6636126
(cherry picked from commit 744c7693751830149ae78fdaf95c6c6f99d59f07)
6636126
6636126
Resolves: #1303552
6636126
---
6636126
 man/systemd.timer.xml                 | 43 ++++++++++++++++++++++++++++++-----
6636126
 src/core/dbus-timer.c                 | 17 ++++++++++++++
6636126
 src/core/load-fragment-gperf.gperf.m4 |  1 +
6636126
 src/core/timer.c                      | 32 +++++++++++++++++++++++++-
6636126
 src/core/timer.h                      |  1 +
6636126
 src/shared/bus-util.c                 | 31 +++++++++++++++++++++++++
6636126
 6 files changed, 118 insertions(+), 7 deletions(-)
6636126
6636126
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
6636126
index 20890f2..bdd14d8 100644
6636126
--- a/man/systemd.timer.xml
6636126
+++ b/man/systemd.timer.xml
6636126
@@ -180,13 +180,12 @@
6636126
         <varname>OnUnitInactiveSec=</varname> and ending the time
6636126
         configured with <varname>AccuracySec=</varname> later. Within
6636126
         this time window, the expiry time will be placed at a
6636126
-        host-specific, randomized but stable position that is
6636126
+        host-specific, randomized, but stable position that is
6636126
         synchronized between all local timer units. This is done in
6636126
-        order to distribute the wake-up time in networked
6636126
-        installations, as well as optimizing power consumption to
6636126
-        suppress unnecessary CPU wake-ups. To get best accuracy, set
6636126
-        this option to 1us. Note that the timer is still subject to
6636126
-        the timer slack configured via
6636126
+        order to optimize power consumption to suppress unnecessary
6636126
+        CPU wake-ups. To get best accuracy, set this option to
6636126
+        1us. Note that the timer is still subject to the timer slack
6636126
+        configured via
6636126
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
6636126
         <varname>TimerSlackNSec=</varname> setting. See
6636126
         <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
6636126
@@ -194,6 +193,38 @@
6636126
         this value as high as possible and as low as
6636126
         necessary.</para></listitem>
6636126
       </varlistentry>
6636126
+
6636126
+      <varlistentry>
6636126
+        <term><varname>RandomSec=</varname></term>
6636126
+
6636126
+        <listitem><para>Delay the timer by a randomly selected, evenly
6636126
+        distributed amount of time between 0 and the specified time
6636126
+        value. Defaults to 0, indicating that no randomized delay
6636126
+        shall be applied. Each timer unit will determine this delay
6636126
+        randomly each time it is started, and the delay will simply be
6636126
+        added on top of the next determined elapsing time. This is
6636126
+        useful to stretch dispatching of similarly configured timer
6636126
+        events over a certain amount time, to avoid that they all fire
6636126
+        at the same time, possibly resulting in resource
6636126
+        congestion. Note the relation to
6636126
+        <varname>AccuracySec=</varname> above: the latter allows the
6636126
+        service manager to coalesce timer events within a specified
6636126
+        time range in order to minimize wakeups, the former does the
6636126
+        opposite: it stretches timer events over a time range, to make
6636126
+        it unlikely that they fire simultaneously. If
6636126
+        <varname>RandomSec=</varname> and
6636126
+        <varname>AccuracySec=</varname> are used in conjunction, first
6636126
+        the a randomized time is added, and the result is then
6636126
+        possibly shifted further to coalesce it with other timer
6636126
+        events possibly happening on the system. As mentioned above
6636126
+        <varname>AccuracySec=</varname> defaults to 1min and
6636126
+        <varname>RandomSec=</varname> to 0, thus encouraging
6636126
+        coalescing of timer events. In order to optimally stretch
6636126
+        timer events over a certain range of time, make sure to set
6636126
+        <varname>RandomSec=</varname> to a higher value, and
6636126
+        <varname>AccuracySec=1us</varname>.</para></listitem>
6636126
+      </varlistentry>
6636126
+
6636126
       <varlistentry>
6636126
         <term><varname>Unit=</varname></term>
6636126
 
6636126
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
6636126
index 74a9914..7875bf6 100644
6636126
--- a/src/core/dbus-timer.c
6636126
+++ b/src/core/dbus-timer.c
6636126
@@ -179,6 +179,7 @@ const sd_bus_vtable bus_timer_vtable[] = {
6636126
         BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
6636126
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
6636126
         SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
6636126
+        SD_BUS_PROPERTY("RandomUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
6636126
         SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
6636126
         SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
6636126
         SD_BUS_VTABLE_END
6636126
@@ -282,6 +283,22 @@ static int bus_timer_set_transient_property(
6636126
 
6636126
                 return 1;
6636126
 
6636126
+        } else if (streq(name, "RandomUSec")) {
6636126
+                usec_t u = 0;
6636126
+
6636126
+                r = sd_bus_message_read(message, "t", &u);
6636126
+                if (r < 0)
6636126
+                        return r;
6636126
+
6636126
+                if (mode != UNIT_CHECK) {
6636126
+                        char time[FORMAT_TIMESPAN_MAX];
6636126
+
6636126
+                        t->random_usec = u;
6636126
+                        unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomSec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
6636126
+                }
6636126
+
6636126
+                return 1;
6636126
+
6636126
         } else if (streq(name, "WakeSystem")) {
6636126
 
6636126
                 int b;
6636126
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
6636126
index aae81c8..270b069 100644
6636126
--- a/src/core/load-fragment-gperf.gperf.m4
6636126
+++ b/src/core/load-fragment-gperf.gperf.m4
6636126
@@ -335,6 +335,7 @@ Timer.OnUnitInactiveSec,         config_parse_timer,                 0,
6636126
 Timer.Persistent,                config_parse_bool,                  0,                             offsetof(Timer, persistent)
6636126
 Timer.WakeSystem,                config_parse_bool,                  0,                             offsetof(Timer, wake_system)
6636126
 Timer.AccuracySec,               config_parse_sec,                   0,                             offsetof(Timer, accuracy_usec)
6636126
+Timer.RandomSec,                 config_parse_sec,                   0,                             offsetof(Timer, random_usec)
6636126
 Timer.Unit,                      config_parse_trigger_unit,          0,                             0
6636126
 m4_dnl
6636126
 Path.PathExists,                 config_parse_path_spec,             0,                             0
6636126
diff --git a/src/core/timer.c b/src/core/timer.c
6636126
index 7f4a2eb..cf50f03 100644
6636126
--- a/src/core/timer.c
6636126
+++ b/src/core/timer.c
6636126
@@ -25,6 +25,7 @@
6636126
 #include "unit-name.h"
6636126
 #include "timer.h"
6636126
 #include "dbus-timer.h"
6636126
+#include "random-util.h"
6636126
 #include "special.h"
6636126
 #include "bus-util.h"
6636126
 #include "bus-error.h"
6636126
@@ -305,6 +306,28 @@ static usec_t monotonic_to_boottime(usec_t t) {
6636126
                 return 0;
6636126
 }
6636126
 
6636126
+static void add_random(Timer *t, usec_t *v) {
6636126
+        char s[FORMAT_TIMESPAN_MAX];
6636126
+        usec_t add;
6636126
+
6636126
+        assert(t);
6636126
+        assert(*v);
6636126
+
6636126
+        if (t->random_usec == 0)
6636126
+                return;
6636126
+        if (*v == USEC_INFINITY)
6636126
+                return;
6636126
+
6636126
+        add = random_u64() % t->random_usec;
6636126
+
6636126
+        if (*v + add < *v) /* overflow */
6636126
+                *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
6636126
+        else
6636126
+                *v += add;
6636126
+
6636126
+        log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
6636126
+}
6636126
+
6636126
 static void timer_enter_waiting(Timer *t, bool initial) {
6636126
         bool found_monotonic = false, found_realtime = false;
6636126
         usec_t ts_realtime, ts_monotonic;
6636126
@@ -420,8 +443,12 @@ static void timer_enter_waiting(Timer *t, bool initial) {
6636126
 
6636126
         if (found_monotonic) {
6636126
                 char buf[FORMAT_TIMESPAN_MAX];
6636126
+                usec_t left;
6636126
 
6636126
-                log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
6636126
+                add_random(t, &t->next_elapse_monotonic_or_boottime);
6636126
+
6636126
+                left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
6636126
+                log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
6636126
 
6636126
                 if (t->monotonic_event_source) {
6636126
                         r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
6636126
@@ -454,6 +481,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
6636126
 
6636126
         if (found_realtime) {
6636126
                 char buf[FORMAT_TIMESTAMP_MAX];
6636126
+
6636126
+                add_random(t, &t->next_elapse_realtime);
6636126
+
6636126
                 log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
6636126
 
6636126
                 if (t->realtime_event_source) {
6636126
diff --git a/src/core/timer.h b/src/core/timer.h
6636126
index 9d919e4..415a71b 100644
6636126
--- a/src/core/timer.h
6636126
+++ b/src/core/timer.h
6636126
@@ -68,6 +68,7 @@ struct Timer {
6636126
         Unit meta;
6636126
 
6636126
         usec_t accuracy_usec;
6636126
+        usec_t random_usec;
6636126
 
6636126
         LIST_HEAD(TimerValue, values);
6636126
         usec_t next_elapse_realtime;
6636126
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
6636126
index 11350da..b1f598a 100644
6636126
--- a/src/shared/bus-util.c
6636126
+++ b/src/shared/bus-util.c
6636126
@@ -1374,6 +1374,37 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
6636126
                         return bus_log_create_error(r);
6636126
 
6636126
                 return 0;
6636126
+
6636126
+        } else if (streq(field, "EnvironmentFile")) {
6636126
+
6636126
+                r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
6636126
+                if (r < 0)
6636126
+                        return bus_log_create_error(r);
6636126
+
6636126
+                r = sd_bus_message_append(m, "v", "a(sb)", 1,
6636126
+                                          eq[0] == '-' ? eq + 1 : eq,
6636126
+                                          eq[0] == '-');
6636126
+                if (r < 0)
6636126
+                        return bus_log_create_error(r);
6636126
+
6636126
+                return 0;
6636126
+
6636126
+        } else if (streq(field, "RandomSec")) {
6636126
+                usec_t t;
6636126
+
6636126
+                r = parse_sec(eq, &t);
6636126
+                if (r < 0)
6636126
+                        return log_error_errno(r, "Failed to parse RandomSec= parameter: %s", eq);
6636126
+
6636126
+                r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomUSec");
6636126
+                if (r < 0)
6636126
+                        return bus_log_create_error(r);
6636126
+
6636126
+                r = sd_bus_message_append(m, "v", "t", t);
6636126
+                if (r < 0)
6636126
+                        return bus_log_create_error(r);
6636126
+
6636126
+                return 0;
6636126
         }
6636126
 
6636126
         r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
6636126
-- 
6636126
2.5.0
6636126