Skip to content

Commit dedabea

Browse files
committed
timer: support timers that can resume the system from suspend
1 parent e955c45 commit dedabea

File tree

7 files changed

+104
-30
lines changed

7 files changed

+104
-30
lines changed

man/systemd.timer.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,23 @@
271271
<varname>OnCalendar=</varname>.
272272
</para></listitem>
273273
</varlistentry>
274+
275+
<varlistentry>
276+
<term><varname>WakeSystem=</varname></term>
277+
278+
<listitem><para>Takes a boolean
279+
argument. If true an elapsing timer
280+
will cause the system to resume from
281+
suspend, should it be suspended and if
282+
the system supports this. Note that
283+
this option will only make sure the
284+
system resumes on the appropriate
285+
times, it will not take care of
286+
suspending it again after any work
287+
that is to be done is
288+
finished. Defaults to
289+
<varname>false</varname>.</para></listitem>
290+
</varlistentry>
274291
</variablelist>
275292
</refsect1>
276293

src/core/dbus-timer.c

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,51 @@ static int property_get_unit(
135135
return sd_bus_message_append(reply, "s", trigger ? trigger->id : "");
136136
}
137137

138+
static int property_get_next_elapse_monotonic(
139+
sd_bus *bus,
140+
const char *path,
141+
const char *interface,
142+
const char *property,
143+
sd_bus_message *reply,
144+
void *userdata,
145+
sd_bus_error *error) {
146+
147+
Timer *t = userdata;
148+
usec_t x;
149+
150+
assert(bus);
151+
assert(reply);
152+
assert(t);
153+
154+
if (t->next_elapse_monotonic_or_boottime <= 0)
155+
x = 0;
156+
else if (t->wake_system) {
157+
usec_t a, b;
158+
159+
a = now(CLOCK_MONOTONIC);
160+
b = now(CLOCK_BOOTTIME);
161+
162+
if (t->next_elapse_monotonic_or_boottime + a > b)
163+
x = t->next_elapse_monotonic_or_boottime + a - b;
164+
else
165+
x = 0;
166+
} else
167+
x = t->next_elapse_monotonic_or_boottime;
168+
169+
return sd_bus_message_append(reply, "t", x);
170+
}
171+
138172
const sd_bus_vtable bus_timer_vtable[] = {
139173
SD_BUS_VTABLE_START(0),
140174
SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
141175
SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
142176
SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
143177
SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
144-
SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", bus_property_get_usec, offsetof(Timer, next_elapse_monotonic), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
145-
SD_BUS_PROPERTY("LastTriggerUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, last_trigger.realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
146-
SD_BUS_PROPERTY("LastTriggerUSecMonotonic", "t", bus_property_get_usec, offsetof(Timer, last_trigger.monotonic), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
178+
SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
179+
BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
147180
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
148181
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
149182
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
183+
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
150184
SD_BUS_VTABLE_END
151185
};

src/core/load-fragment-gperf.gperf.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ Timer.OnStartupSec, config_parse_timer, 0,
286286
Timer.OnUnitActiveSec, config_parse_timer, 0, 0
287287
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
288288
Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
289+
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
289290
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
290291
Timer.Unit, config_parse_trigger_unit, 0, 0
291292
m4_dnl

src/core/load-fragment.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,6 @@ int config_parse_timer(const char *unit,
12541254
TimerValue *v;
12551255
TimerBase b;
12561256
CalendarSpec *c = NULL;
1257-
clockid_t id;
12581257

12591258
assert(filename);
12601259
assert(lvalue);
@@ -1281,25 +1280,20 @@ int config_parse_timer(const char *unit,
12811280
rvalue);
12821281
return 0;
12831282
}
1284-
1285-
id = CLOCK_REALTIME;
12861283
} else {
12871284
if (parse_sec(rvalue, &u) < 0) {
12881285
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
12891286
"Failed to parse timer value, ignoring: %s",
12901287
rvalue);
12911288
return 0;
12921289
}
1293-
1294-
id = CLOCK_MONOTONIC;
12951290
}
12961291

12971292
v = new0(TimerValue, 1);
12981293
if (!v)
12991294
return log_oom();
13001295

13011296
v->base = b;
1302-
v->clock_id = id;
13031297
v->value = u;
13041298
v->calendar_spec = c;
13051299

src/core/timer.c

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static void timer_init(Unit *u) {
4646
assert(u);
4747
assert(u->load_state == UNIT_STUB);
4848

49-
t->next_elapse_monotonic = (usec_t) -1;
49+
t->next_elapse_monotonic_or_boottime = (usec_t) -1;
5050
t->next_elapse_realtime = (usec_t) -1;
5151
t->accuracy_usec = USEC_PER_MINUTE;
5252
}
@@ -203,10 +203,14 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
203203
"%sTimer State: %s\n"
204204
"%sResult: %s\n"
205205
"%sUnit: %s\n"
206+
"%sPersistent: %s\n"
207+
"%sWakeSystem: %s\n"
206208
"%sAccuracy: %s\n",
207209
prefix, timer_state_to_string(t->state),
208210
prefix, timer_result_to_string(t->result),
209211
prefix, trigger ? trigger->id : "n/a",
212+
prefix, yes_no(t->persistent),
213+
prefix, yes_no(t->wake_system),
210214
prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
211215

212216
LIST_FOREACH(value, v, t->values) {
@@ -282,15 +286,34 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
282286
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
283287
}
284288

289+
static usec_t monotonic_to_boottime(usec_t t) {
290+
usec_t a, b;
291+
292+
if (t <= 0)
293+
return 0;
294+
295+
a = now(CLOCK_BOOTTIME);
296+
b = now(CLOCK_MONOTONIC);
297+
298+
if (t + a > b)
299+
return t + a - b;
300+
else
301+
return 0;
302+
}
303+
285304
static void timer_enter_waiting(Timer *t, bool initial) {
286-
TimerValue *v;
287-
usec_t base = 0;
288-
dual_timestamp ts;
289305
bool found_monotonic = false, found_realtime = false;
306+
usec_t ts_realtime, ts_monotonic;
307+
usec_t base = 0;
308+
TimerValue *v;
290309
int r;
291310

292-
dual_timestamp_get(&ts);
293-
t->next_elapse_monotonic = t->next_elapse_realtime = 0;
311+
/* If we shall wake the system we use the boottime clock
312+
* rather than the monotonic clock. */
313+
314+
ts_realtime = now(CLOCK_REALTIME);
315+
ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
316+
t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
294317

295318
LIST_FOREACH(value, v, t->values) {
296319

@@ -305,7 +328,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
305328
* to that. If we don't just start from
306329
* now. */
307330

308-
b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts.realtime;
331+
b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
309332

310333
r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
311334
if (r < 0)
@@ -325,7 +348,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
325348
if (state_translation_table[t->state] == UNIT_ACTIVE)
326349
base = UNIT(t)->inactive_exit_timestamp.monotonic;
327350
else
328-
base = ts.monotonic;
351+
base = ts_monotonic;
329352
break;
330353

331354
case TIMER_BOOT:
@@ -365,18 +388,21 @@ static void timer_enter_waiting(Timer *t, bool initial) {
365388
assert_not_reached("Unknown timer base");
366389
}
367390

391+
if (t->wake_system)
392+
base = monotonic_to_boottime(base);
393+
368394
v->next_elapse = base + v->value;
369395

370-
if (!initial && v->next_elapse < ts.monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
396+
if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
371397
/* This is a one time trigger, disable it now */
372398
v->disabled = true;
373399
continue;
374400
}
375401

376402
if (!found_monotonic)
377-
t->next_elapse_monotonic = v->next_elapse;
403+
t->next_elapse_monotonic_or_boottime = v->next_elapse;
378404
else
379-
t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
405+
t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
380406

381407
found_monotonic = true;
382408
}
@@ -390,10 +416,13 @@ static void timer_enter_waiting(Timer *t, bool initial) {
390416

391417
if (found_monotonic) {
392418
char buf[FORMAT_TIMESPAN_MAX];
393-
log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.", UNIT(t)->id, format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
419+
420+
log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
421+
UNIT(t)->id,
422+
format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
394423

395424
if (t->monotonic_event_source) {
396-
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic);
425+
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
397426
if (r < 0)
398427
goto fail;
399428

@@ -402,8 +431,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
402431
r = sd_event_add_time(
403432
UNIT(t)->manager->event,
404433
&t->monotonic_event_source,
405-
CLOCK_MONOTONIC,
406-
t->next_elapse_monotonic, t->accuracy_usec,
434+
t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
435+
t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
407436
timer_dispatch, t);
408437
if (r < 0)
409438
goto fail;
@@ -429,7 +458,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
429458
r = sd_event_add_time(
430459
UNIT(t)->manager->event,
431460
&t->realtime_event_source,
432-
CLOCK_REALTIME,
461+
t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
433462
t->next_elapse_realtime, t->accuracy_usec,
434463
timer_dispatch, t);
435464
if (r < 0)

src/core/timer.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ typedef enum TimerBase {
5050
typedef struct TimerValue {
5151
TimerBase base;
5252
bool disabled;
53-
clockid_t clock_id;
5453

5554
usec_t value; /* only for monotonic events */
5655
CalendarSpec *calendar_spec; /* only for calendar events */
@@ -72,8 +71,9 @@ struct Timer {
7271
usec_t accuracy_usec;
7372

7473
LIST_HEAD(TimerValue, values);
75-
usec_t next_elapse_monotonic;
7674
usec_t next_elapse_realtime;
75+
usec_t next_elapse_monotonic_or_boottime;
76+
dual_timestamp last_trigger;
7777

7878
TimerState state, deserialized_state;
7979

@@ -83,8 +83,7 @@ struct Timer {
8383
TimerResult result;
8484

8585
bool persistent;
86-
87-
dual_timestamp last_trigger;
86+
bool wake_system;
8887

8988
char *stamp_path;
9089
};

src/systemctl/systemctl.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ static int get_last_trigger(
964964
"org.freedesktop.systemd1",
965965
path,
966966
"org.freedesktop.systemd1.Timer",
967-
"LastTriggerUSecRealtime",
967+
"LastTriggerUSec",
968968
&error,
969969
't',
970970
last);

0 commit comments

Comments
 (0)