From c052db8cb1bc6be4b314e69ba24a17d3fdc8c35a Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 26 Jun 2026 13:57:27 +0100 Subject: [PATCH] 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. --- src/analyze/analyze-time-data.c | 7 ++--- src/analyze/analyze-time-data.h | 1 - src/core/manager-serialize.c | 51 +++++++++++++++++++++++++++------ src/core/manager.c | 2 +- src/core/manager.h | 10 ++++--- 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c index 744de87c6d0..f71be1e7a7f 100644 --- a/src/analyze/analyze-time-data.c +++ b/src/analyze/analyze-time-data.c @@ -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); diff --git a/src/analyze/analyze-time-data.h b/src/analyze/analyze-time-data.h index 7b4af3dc8c5..d3d686948d9 100644 --- a/src/analyze/analyze-time-data.h +++ b/src/analyze/analyze-time-data.h @@ -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; diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c index 72c6aa19a3b..efa9f65bfb4 100644 --- a/src/core/manager-serialize.c +++ b/src/core/manager-serialize.c @@ -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); } } diff --git a/src/core/manager.c b/src/core/manager.c index f965e0a33a1..eedb5e710d3 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -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), diff --git a/src/core/manager.h b/src/core/manager.h index 91555e5ad0c..0a0974bf1cc 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -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 { -- 2.47.3