]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: deserialize soft-reboot shutdown timestamps into previous-* fields 42671/head
authorLuca Boccassi <luca.boccassi@gmail.com>
Fri, 26 Jun 2026 12:57:27 +0000 (13:57 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 1 Jul 2026 10:16:46 +0000 (11:16 +0100)
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
src/analyze/analyze-time-data.h
src/core/manager-serialize.c
src/core/manager.c
src/core/manager.h

index 744de87c6d036b6c7d1af79521e52274c4706479..f71be1e7a7f74662153e475d7996d90cb5c582ed 100644 (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);
index 7b4af3dc8c52352859cff15f70c27368fe2a91f3..d3d686948d9eb69763faa02f007ee3dd058dfe46 100644 (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;
index 72c6aa19a3bdcc58b699c5f2ef45e5722bf3ae5b..efa9f65bfb4b55d3ff658ad9b70f1c6b9d31fa62 100644 (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);
                 }
         }
index f965e0a33a1d51d718c20f366ea27a82dfd3b10e..eedb5e710d377cde6b89fc8ae114890fcc929c9b 100644 (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),
index 91555e5ad0c93eebbcc7380d3e1a6e2eb7e587cf..0a0974bf1cc591e83cc48eb7ccea2afbbd0d2c87 100644 (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 {