|
|
eb6853 |
From 9b2dad78f267ab3944d8b1145e3efe6c97bef4cf Mon Sep 17 00:00:00 2001
|
|
|
1ec9a6 |
From: =?UTF-8?q?Michal=20Koutn=C3=BD?= <mkoutny@suse.com>
|
|
|
1ec9a6 |
Date: Fri, 2 Nov 2018 20:56:08 +0100
|
|
|
1ec9a6 |
Subject: [PATCH] core: Detect initial timer state from serialized data
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
We keep a mark whether a single-shot timer was triggered in the caller's
|
|
|
1ec9a6 |
variable initial. When such a timer elapses while we are
|
|
|
1ec9a6 |
serializing/deserializing the inner state, we consider the timer
|
|
|
1ec9a6 |
incorrectly as elapsed and don't trigger it later.
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
This patch exploits last_trigger timestamp that we already serialize,
|
|
|
1ec9a6 |
hence we can eliminate the argument initial completely.
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
A reproducer for OnBootSec= timers:
|
|
|
1ec9a6 |
cat >repro.c <
|
|
|
1ec9a6 |
/*
|
|
|
1ec9a6 |
* Compile: gcc repro.c -o repro
|
|
|
1ec9a6 |
* Run: ./repro
|
|
|
1ec9a6 |
*/
|
|
|
1ec9a6 |
#include <errno.h>
|
|
|
1ec9a6 |
#include <fcntl.h>
|
|
|
1ec9a6 |
#include <stdio.h>
|
|
|
1ec9a6 |
#include <stdlib.h>
|
|
|
1ec9a6 |
#include <sys/stat.h>
|
|
|
1ec9a6 |
#include <sys/types.h>
|
|
|
1ec9a6 |
#include <time.h>
|
|
|
1ec9a6 |
#include <unistd.h>
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
int main(int argc, char *argv[]) {
|
|
|
1ec9a6 |
char command[1024];
|
|
|
1ec9a6 |
int pause;
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
struct timespec now;
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
while (1) {
|
|
|
1ec9a6 |
usleep(rand() % 200000); // prevent periodic repeats
|
|
|
1ec9a6 |
clock_gettime(CLOCK_MONOTONIC, &now;;
|
|
|
1ec9a6 |
printf("%i\n", now.tv_sec);
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
system("rm -f $PWD/mark");
|
|
|
1ec9a6 |
snprintf(command, 1024, "systemd-run --user --on-boot=%i --timer-property=AccuracySec=100ms "
|
|
|
1ec9a6 |
"touch $PWD/mark", now.tv_sec + 1);
|
|
|
1ec9a6 |
system(command);
|
|
|
1ec9a6 |
system("systemctl --user list-timers");
|
|
|
1ec9a6 |
pause = (1000000000 - now.tv_nsec)/1000 - 70000; // fiddle to hit the middle of reloading
|
|
|
1ec9a6 |
usleep(pause > 0 ? pause : 0);
|
|
|
1ec9a6 |
system("systemctl --user daemon-reload");
|
|
|
1ec9a6 |
sync();
|
|
|
1ec9a6 |
sleep(2);
|
|
|
1ec9a6 |
if (open("./mark", 0) < 0)
|
|
|
1ec9a6 |
if (errno == ENOENT) {
|
|
|
1ec9a6 |
printf("mark file does not exist\n");
|
|
|
1ec9a6 |
break;
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
return 0;
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
EOD
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
(cherry picked from commit aa1f95d2647197eca84c33a0f10adaeada08467d)
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
Resolves: #1764908
|
|
|
1ec9a6 |
---
|
|
|
1ec9a6 |
src/core/timer.c | 16 ++++++++--------
|
|
|
1ec9a6 |
1 file changed, 8 insertions(+), 8 deletions(-)
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
diff --git a/src/core/timer.c b/src/core/timer.c
|
|
|
eb6853 |
index fb192d558a..b36700c186 100644
|
|
|
1ec9a6 |
--- a/src/core/timer.c
|
|
|
1ec9a6 |
+++ b/src/core/timer.c
|
|
|
1ec9a6 |
@@ -267,10 +267,10 @@ static void timer_set_state(Timer *t, TimerState state) {
|
|
|
1ec9a6 |
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
-static void timer_enter_waiting(Timer *t, bool initial);
|
|
|
1ec9a6 |
+static void timer_enter_waiting(Timer *t);
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
static int timer_enter_waiting_coldplug(Unit *u) {
|
|
|
1ec9a6 |
- timer_enter_waiting(TIMER(u), false);
|
|
|
1ec9a6 |
+ timer_enter_waiting(TIMER(u));
|
|
|
1ec9a6 |
return 0;
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
@@ -338,7 +338,7 @@ static void add_random(Timer *t, usec_t *v) {
|
|
|
1ec9a6 |
log_unit_debug(UNIT(t)->id, "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
-static void timer_enter_waiting(Timer *t, bool initial) {
|
|
|
1ec9a6 |
+static void timer_enter_waiting(Timer *t) {
|
|
|
1ec9a6 |
bool found_monotonic = false, found_realtime = false;
|
|
|
1ec9a6 |
usec_t ts_realtime, ts_monotonic;
|
|
|
1ec9a6 |
usec_t base = 0;
|
|
|
1ec9a6 |
@@ -442,7 +442,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
v->next_elapse = base + v->value;
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
- if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
|
|
|
1ec9a6 |
+ if (dual_timestamp_is_set(&t->last_trigger) && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
|
|
|
1ec9a6 |
/* This is a one time trigger, disable it now */
|
|
|
1ec9a6 |
v->disabled = true;
|
|
|
1ec9a6 |
continue;
|
|
|
1ec9a6 |
@@ -619,7 +619,7 @@ static int timer_start(Unit *u) {
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
t->result = TIMER_SUCCESS;
|
|
|
1ec9a6 |
- timer_enter_waiting(t, true);
|
|
|
1ec9a6 |
+ timer_enter_waiting(t);
|
|
|
1ec9a6 |
return 1;
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
@@ -742,14 +742,14 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
|
|
|
1ec9a6 |
case TIMER_ELAPSED:
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
/* Recalculate sleep time */
|
|
|
1ec9a6 |
- timer_enter_waiting(t, false);
|
|
|
1ec9a6 |
+ timer_enter_waiting(t);
|
|
|
1ec9a6 |
break;
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
case TIMER_RUNNING:
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
|
|
|
1ec9a6 |
log_unit_debug(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id);
|
|
|
1ec9a6 |
- timer_enter_waiting(t, false);
|
|
|
1ec9a6 |
+ timer_enter_waiting(t);
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
break;
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
@@ -782,7 +782,7 @@ static void timer_time_change(Unit *u) {
|
|
|
1ec9a6 |
return;
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
log_unit_debug(u->id, "%s: time change, recalculating next elapse.", u->id);
|
|
|
1ec9a6 |
- timer_enter_waiting(t, false);
|
|
|
1ec9a6 |
+ timer_enter_waiting(t);
|
|
|
1ec9a6 |
}
|
|
|
1ec9a6 |
|
|
|
1ec9a6 |
static const char* const timer_state_table[_TIMER_STATE_MAX] = {
|