mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
timer: support timers that can resume the system from suspend
This commit is contained in:
parent
e955c45881
commit
dedabea4b3
@ -271,6 +271,23 @@
|
||||
<varname>OnCalendar=</varname>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>WakeSystem=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean
|
||||
argument. If true an elapsing timer
|
||||
will cause the system to resume from
|
||||
suspend, should it be suspended and if
|
||||
the system supports this. Note that
|
||||
this option will only make sure the
|
||||
system resumes on the appropriate
|
||||
times, it will not take care of
|
||||
suspending it again after any work
|
||||
that is to be done is
|
||||
finished. Defaults to
|
||||
<varname>false</varname>.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
@ -135,17 +135,51 @@ static int property_get_unit(
|
||||
return sd_bus_message_append(reply, "s", trigger ? trigger->id : "");
|
||||
}
|
||||
|
||||
static int property_get_next_elapse_monotonic(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Timer *t = userdata;
|
||||
usec_t x;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(t);
|
||||
|
||||
if (t->next_elapse_monotonic_or_boottime <= 0)
|
||||
x = 0;
|
||||
else if (t->wake_system) {
|
||||
usec_t a, b;
|
||||
|
||||
a = now(CLOCK_MONOTONIC);
|
||||
b = now(CLOCK_BOOTTIME);
|
||||
|
||||
if (t->next_elapse_monotonic_or_boottime + a > b)
|
||||
x = t->next_elapse_monotonic_or_boottime + a - b;
|
||||
else
|
||||
x = 0;
|
||||
} else
|
||||
x = t->next_elapse_monotonic_or_boottime;
|
||||
|
||||
return sd_bus_message_append(reply, "t", x);
|
||||
}
|
||||
|
||||
const sd_bus_vtable bus_timer_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Unit", "s", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", bus_property_get_usec, offsetof(Timer, next_elapse_monotonic), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("LastTriggerUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, last_trigger.realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("LastTriggerUSecMonotonic", "t", bus_property_get_usec, offsetof(Timer, last_trigger.monotonic), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
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("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_VTABLE_END
|
||||
};
|
||||
|
@ -286,6 +286,7 @@ Timer.OnStartupSec, config_parse_timer, 0,
|
||||
Timer.OnUnitActiveSec, config_parse_timer, 0, 0
|
||||
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
|
||||
Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
|
||||
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
|
||||
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
|
||||
Timer.Unit, config_parse_trigger_unit, 0, 0
|
||||
m4_dnl
|
||||
|
@ -1254,7 +1254,6 @@ int config_parse_timer(const char *unit,
|
||||
TimerValue *v;
|
||||
TimerBase b;
|
||||
CalendarSpec *c = NULL;
|
||||
clockid_t id;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
@ -1281,8 +1280,6 @@ int config_parse_timer(const char *unit,
|
||||
rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
id = CLOCK_REALTIME;
|
||||
} else {
|
||||
if (parse_sec(rvalue, &u) < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
|
||||
@ -1290,8 +1287,6 @@ int config_parse_timer(const char *unit,
|
||||
rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
id = CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
v = new0(TimerValue, 1);
|
||||
@ -1299,7 +1294,6 @@ int config_parse_timer(const char *unit,
|
||||
return log_oom();
|
||||
|
||||
v->base = b;
|
||||
v->clock_id = id;
|
||||
v->value = u;
|
||||
v->calendar_spec = c;
|
||||
|
||||
|
@ -46,7 +46,7 @@ static void timer_init(Unit *u) {
|
||||
assert(u);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
t->next_elapse_monotonic = (usec_t) -1;
|
||||
t->next_elapse_monotonic_or_boottime = (usec_t) -1;
|
||||
t->next_elapse_realtime = (usec_t) -1;
|
||||
t->accuracy_usec = USEC_PER_MINUTE;
|
||||
}
|
||||
@ -203,10 +203,14 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%sTimer State: %s\n"
|
||||
"%sResult: %s\n"
|
||||
"%sUnit: %s\n"
|
||||
"%sPersistent: %s\n"
|
||||
"%sWakeSystem: %s\n"
|
||||
"%sAccuracy: %s\n",
|
||||
prefix, timer_state_to_string(t->state),
|
||||
prefix, timer_result_to_string(t->result),
|
||||
prefix, trigger ? trigger->id : "n/a",
|
||||
prefix, yes_no(t->persistent),
|
||||
prefix, yes_no(t->wake_system),
|
||||
prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
|
||||
|
||||
LIST_FOREACH(value, v, t->values) {
|
||||
@ -282,15 +286,34 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
|
||||
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
|
||||
}
|
||||
|
||||
static usec_t monotonic_to_boottime(usec_t t) {
|
||||
usec_t a, b;
|
||||
|
||||
if (t <= 0)
|
||||
return 0;
|
||||
|
||||
a = now(CLOCK_BOOTTIME);
|
||||
b = now(CLOCK_MONOTONIC);
|
||||
|
||||
if (t + a > b)
|
||||
return t + a - b;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
TimerValue *v;
|
||||
usec_t base = 0;
|
||||
dual_timestamp ts;
|
||||
bool found_monotonic = false, found_realtime = false;
|
||||
usec_t ts_realtime, ts_monotonic;
|
||||
usec_t base = 0;
|
||||
TimerValue *v;
|
||||
int r;
|
||||
|
||||
dual_timestamp_get(&ts);
|
||||
t->next_elapse_monotonic = t->next_elapse_realtime = 0;
|
||||
/* If we shall wake the system we use the boottime clock
|
||||
* rather than the monotonic clock. */
|
||||
|
||||
ts_realtime = now(CLOCK_REALTIME);
|
||||
ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
|
||||
t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
|
||||
|
||||
LIST_FOREACH(value, v, t->values) {
|
||||
|
||||
@ -305,7 +328,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
* to that. If we don't just start from
|
||||
* now. */
|
||||
|
||||
b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts.realtime;
|
||||
b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime;
|
||||
|
||||
r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
|
||||
if (r < 0)
|
||||
@ -325,7 +348,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
if (state_translation_table[t->state] == UNIT_ACTIVE)
|
||||
base = UNIT(t)->inactive_exit_timestamp.monotonic;
|
||||
else
|
||||
base = ts.monotonic;
|
||||
base = ts_monotonic;
|
||||
break;
|
||||
|
||||
case TIMER_BOOT:
|
||||
@ -365,18 +388,21 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
assert_not_reached("Unknown timer base");
|
||||
}
|
||||
|
||||
if (t->wake_system)
|
||||
base = monotonic_to_boottime(base);
|
||||
|
||||
v->next_elapse = base + v->value;
|
||||
|
||||
if (!initial && v->next_elapse < ts.monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
|
||||
if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
|
||||
/* This is a one time trigger, disable it now */
|
||||
v->disabled = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!found_monotonic)
|
||||
t->next_elapse_monotonic = v->next_elapse;
|
||||
t->next_elapse_monotonic_or_boottime = v->next_elapse;
|
||||
else
|
||||
t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
|
||||
t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
|
||||
|
||||
found_monotonic = true;
|
||||
}
|
||||
@ -390,10 +416,13 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
|
||||
if (found_monotonic) {
|
||||
char buf[FORMAT_TIMESPAN_MAX];
|
||||
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));
|
||||
|
||||
log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.",
|
||||
UNIT(t)->id,
|
||||
format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
|
||||
|
||||
if (t->monotonic_event_source) {
|
||||
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic);
|
||||
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -402,8 +431,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
r = sd_event_add_time(
|
||||
UNIT(t)->manager->event,
|
||||
&t->monotonic_event_source,
|
||||
CLOCK_MONOTONIC,
|
||||
t->next_elapse_monotonic, t->accuracy_usec,
|
||||
t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
|
||||
t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
|
||||
timer_dispatch, t);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -429,7 +458,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
|
||||
r = sd_event_add_time(
|
||||
UNIT(t)->manager->event,
|
||||
&t->realtime_event_source,
|
||||
CLOCK_REALTIME,
|
||||
t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
|
||||
t->next_elapse_realtime, t->accuracy_usec,
|
||||
timer_dispatch, t);
|
||||
if (r < 0)
|
||||
|
@ -50,7 +50,6 @@ typedef enum TimerBase {
|
||||
typedef struct TimerValue {
|
||||
TimerBase base;
|
||||
bool disabled;
|
||||
clockid_t clock_id;
|
||||
|
||||
usec_t value; /* only for monotonic events */
|
||||
CalendarSpec *calendar_spec; /* only for calendar events */
|
||||
@ -72,8 +71,9 @@ struct Timer {
|
||||
usec_t accuracy_usec;
|
||||
|
||||
LIST_HEAD(TimerValue, values);
|
||||
usec_t next_elapse_monotonic;
|
||||
usec_t next_elapse_realtime;
|
||||
usec_t next_elapse_monotonic_or_boottime;
|
||||
dual_timestamp last_trigger;
|
||||
|
||||
TimerState state, deserialized_state;
|
||||
|
||||
@ -83,8 +83,7 @@ struct Timer {
|
||||
TimerResult result;
|
||||
|
||||
bool persistent;
|
||||
|
||||
dual_timestamp last_trigger;
|
||||
bool wake_system;
|
||||
|
||||
char *stamp_path;
|
||||
};
|
||||
|
@ -964,7 +964,7 @@ static int get_last_trigger(
|
||||
"org.freedesktop.systemd1",
|
||||
path,
|
||||
"org.freedesktop.systemd1.Timer",
|
||||
"LastTriggerUSecRealtime",
|
||||
"LastTriggerUSec",
|
||||
&error,
|
||||
't',
|
||||
last);
|
||||
|
Loading…
Reference in New Issue
Block a user