core: deserialize soft-reboot shutdown timestamps into previous-* fields

Until now the SHUTDOWN_START/FINISH timestamps were carried across a
soft-reboot in the same fields, so afterwards they described the
previous boot rather than the current one, mixing current- and
previous-boot data in the same set. Mirror what is now done on kexec
and move them into the PREVIOUS_SHUTDOWN_* fields on deserialization.

Also reset local timestamps for events that rerun (eg: generators,
units loading, etc) on soft-reboot and switch-root.
This commit is contained in:
Luca Boccassi
2026-07-01 11:16:46 +01:00
parent 768b3423a4
commit c052db8cb1
5 changed files with 53 additions and 18 deletions
+3 -4
View File
@@ -45,7 +45,6 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
{ "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) },
{ "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) },
{ "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) },
{ "ShutdownStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, shutdown_start_time) },
{ "PreviousShutdownStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, previous_shutdown_start_time) },
{ "PreviousShutdownFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, previous_shutdown_finish_time) },
{ "PreviousShutdownLateStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, previous_shutdown_late_start_time) },
@@ -100,7 +99,7 @@ int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret) {
times.initrd_security_start_time = times.initrd_security_finish_time =
times.initrd_generators_start_time = times.initrd_generators_finish_time =
times.initrd_unitsload_start_time = times.initrd_unitsload_finish_time = 0;
times.reverse_offset = times.shutdown_start_time;
times.reverse_offset = times.previous_shutdown_start_time;
/* Clamp all timestamps to avoid showing huge graphs */
if (timestamp_is_set(times.finish_time))
@@ -366,8 +365,8 @@ int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) {
/* Activated in the previous soft-reboot iteration? Ignore it, we want new activations */
if (boot_times->soft_reboots_count > 0 &&
((t->activated > 0 && t->activated < boot_times->shutdown_start_time) ||
(t->activating > 0 && t->activating < boot_times->shutdown_start_time)))
((t->activated > 0 && t->activated < boot_times->previous_shutdown_start_time) ||
(t->activating > 0 && t->activating < boot_times->previous_shutdown_start_time)))
continue;
subtract_timestamp(&t->activating, boot_times->reverse_offset);
-1
View File
@@ -12,7 +12,6 @@ typedef struct BootTimes {
usec_t initrd_time;
usec_t userspace_time;
usec_t finish_time;
usec_t shutdown_start_time;
usec_t previous_shutdown_start_time;
usec_t previous_shutdown_finish_time;
usec_t previous_shutdown_late_start_time;
+43 -8
View File
@@ -35,12 +35,12 @@ int manager_open_serialization(Manager *m, FILE **ret_f) {
return open_serialization_file("systemd-state", ret_f);
}
static bool manager_timestamp_shall_serialize(ManagerObjective o, ManagerTimestamp t) {
if (!in_initrd() && o != MANAGER_SOFT_REBOOT)
static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
if (!in_initrd())
return true;
/* The following timestamps only apply to the host system (or first boot in case of soft-reboot),
* hence only serialize them there. */
/* In the initrd the following timestamps describe the host boot we are about to hand over to,
* which re-takes them, so don't serialize them. */
return !IN_SET(t,
MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
@@ -48,6 +48,39 @@ static bool manager_timestamp_shall_serialize(ManagerObjective o, ManagerTimesta
MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
}
static ManagerTimestamp map_timestamp_serialization(ManagerObjective previous_objective, ManagerTimestamp t) {
/* Returns the timestamp field a serialized timestamp 't' should be deserialized into, given the
* objective the previous instance serialized under, or _MANAGER_TIMESTAMP_INVALID to drop it. */
/* Both across a soft-reboot and a switch-root the new instance runs through the early boot phases
* again, so drop those timestamps to have them re-measured; the firmware/loader/kernel/initrd ones
* are kept. */
if (IN_SET(previous_objective, MANAGER_SOFT_REBOOT, MANAGER_SWITCH_ROOT) &&
IN_SET(t,
MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
MANAGER_TIMESTAMP_GENERATORS_START, MANAGER_TIMESTAMP_GENERATORS_FINISH,
MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH))
return _MANAGER_TIMESTAMP_INVALID;
/* Across a soft-reboot the old instance's shutdown timestamps describe the boot that is going away,
* so move them into the previous-shutdown-* fields. */
if (previous_objective == MANAGER_SOFT_REBOOT)
switch (t) {
case MANAGER_TIMESTAMP_SHUTDOWN_START:
return MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_START;
case MANAGER_TIMESTAMP_SHUTDOWN_FINISH:
return MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_FINISH;
default:
break;
}
return t;
}
static void manager_serialize_uid_refs_internal(
FILE *f,
Hashmap *uid_refs,
@@ -130,7 +163,7 @@ int manager_serialize(
for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
_cleanup_free_ char *joined = NULL;
if (!manager_timestamp_shall_serialize(m->objective, q))
if (!manager_timestamp_shall_serialize(q))
continue;
joined = strjoin(manager_timestamp_to_string(q), "-timestamp");
@@ -791,9 +824,11 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
break;
}
if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
(void) deserialize_dual_timestamp(val, m->timestamps + q);
else if (!STARTSWITH_SET(l, "kdbus-fd=", "honor-device-enumeration=", "ready-sent=", "cgroups-agent-fd=")) /* ignore deprecated values */
if (q < _MANAGER_TIMESTAMP_MAX) { /* found it */
ManagerTimestamp target = map_timestamp_serialization(m->previous_objective, q);
if (target >= 0)
(void) deserialize_dual_timestamp(val, m->timestamps + target);
} else if (!STARTSWITH_SET(l, "kdbus-fd=", "honor-device-enumeration=", "ready-sent=", "cgroups-agent-fd=")) /* ignore deprecated values */
log_notice("Unknown serialization item '%s', ignoring.", l);
}
}
+1 -1
View File
@@ -4038,7 +4038,7 @@ static void manager_notify_finished(Manager *m) {
/* The soft-reboot case, where we only report data for the last reboot */
firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
total_usec = userspace_usec = usec_sub_unsigned(m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic,
m->timestamps[MANAGER_TIMESTAMP_SHUTDOWN_START].monotonic);
m->timestamps[MANAGER_TIMESTAMP_PREVIOUS_SHUTDOWN_START].monotonic);
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED_STR),
+6 -4
View File
@@ -84,10 +84,12 @@ typedef enum ManagerObjective {
*
* 9. TIMESTAMP_PREVIOUS_SHUTDOWN_START, TIMESTAMP_PREVIOUS_SHUTDOWN_FINISH,
* TIMESTAMP_PREVIOUS_SHUTDOWN_LATE_START and TIMESTAMP_PREVIOUS_SHUTDOWN_LATE_FINISH describe the
* shutdown of the *previous* boot, restored from the LUO payload after a kexec-based live update
* (the LATE_* ones are taken by systemd-shutdown). Like TIMESTAMP_FIRMWARE/LOADER/KERNEL they refer
* to events before the current systemd cycle took over, hence they are kept distinct from the
* current cycle's SHUTDOWN_START/FINISH instead of overwriting them.
* shutdown of the *previous* boot: either restored from the LUO payload after a kexec-based live
* update, or carried over from the current cycle's SHUTDOWN_START/FINISH across a soft-reboot. The
* LATE_* ones are taken by systemd-shutdown and hence only set for the kexec case. Like
* TIMESTAMP_FIRMWARE/LOADER/KERNEL they refer to events before the current systemd cycle took over,
* hence they are kept distinct from the current cycle's SHUTDOWN_START/FINISH instead of overwriting
* them.
*/
typedef enum ManagerTimestamp {