This is a heavily "refactored by hands" patch for Linux Kernel 5.15.202 LTS trying to solve the source tree janitoring vs 1-single place hack for hackers in need for a simple-bare profiling tool and based on the patch V4 submitted by From: Tim Bird Subject: [PATCH v4 1/1] printk: fix zero-valued printk timestamps in early boot Date: Fri, 10 Apr 2026 14:37:41 -0600 Message-ID: <20260410203741.997410-2-tim.bird@sony.com> During early boot, printk timestamps are reported as zero before kernel timekeeping starts (i.e. before time_init()). This hinders boot-time optimization efforts. This period ranges from 17 to 1700 milliseconds on different embedded machines running Linux. Add support for early timestamps based on processor cycle-generators that need no kernel initialization.This feature isn't intended for a generic distro kernels but for temporary use during kernel development and boot-time research and optimization by kernel hackers only. This yields non-zero timestamps for printks from the very start of kernel execution. The timestamps are relative to the start of an architecture-specific counter (e.g. tsc on x86_64 and cntvct_el0 on arm64). Affected timestamps reflect cycle counter related values since init (usually machine power-on or virtual machine start) instead of time from the kernel's timekeeping initialization. This results in a discontinuity in the printk timestamp values, one time, when kernel timekeeping starts. Signed-off-by: Tim Bird Signed-off-by: Roberto A. Foglietta --- V5 -> V4 Rationale: single point of change hack for printk. Code rewritten, entirely. It provides a feature for kernel hackers willing to profiling the early boot with a basic printk approach (rather than using HW monitors which might not be available or immediately available). Thus, it is a first-look or a last resort hack profiling feature. Something that isn't good to spread around the kernel sources tree and it is fine to have into a single header file. V3 -> V4 Replace config vars with single one: CONFIG_EARLY_CYCLES_KHZ Replace runtime calibration with static config variable Remove reference to get_cycles() Add support for RISCV platforms V2 -> V3 Default CONFIG option to 'n' Move more code into early_times.h (reduce ifdefs in init/main.c) Use match64 helper routines Use cycles_t instead of u64 type Add #defines for EARLY_CYCLES_BIT and EARLY_CYCLES_MASK Invert if logic in adjust_early_ts() V1 -> V2 Remove calibration CONFIG vars Add 'depends on' to restrict arches (to handle ppc bug) Add early_ts_offset to avoid discontinuity Save cycles in ts_nsec, and convert on output Move conditional code to include file early_times.h --- a/init/Kconfig 2026-04-15 11:34:05.823433712 +0200 +++ b/init/Kconfig 2026-04-15 11:36:12.703334604 +0200 @@ -833,6 +833,27 @@ config PRINTK_INDEX There is no additional runtime cost to printk with this enabled. +config PRINTK_EARLY_BOOT_TIMINGS + bool "Early boot printk shows times in raw cycle counter style" + default 0 + depends on PRINTK + depends on ARM64 || X86_64 || RISCV_TIMER + select PRINTK_TIME + help + Boolean value, disabled by default. + + Set this to provide cycles information for printks in early boot + (before the start of kernel timekeeping), that would otherwise + show as 0. Profiling by relative monotonic values, not time. + + Note that this causes the kernel to show, for some early printks, + cycles that are relative to processor power on, instead of + relative to the start of kernel timekeeping. When kernel + timekeeping starts, the timestamps values reset, causing + a discontinuity in the timestamp values. + + If unsure, keep it disabled as per its default. + # # Architectures with an unreliable sched_clock() should select this: # --- /dev/null 2026-04-10 15:35:49.659741644 +0200 +++ b/kernel/printk/early_times.h 2026-04-15 11:32:49.786728602 +0200 @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _EARLY_TIMES_H +#define _EARLY_TIMES_H + +#include + +/* + * Fencing isn't optional here, otherwise unreliable values displaying + */ +#if defined(CONFIG_ARM64) + #include + #define __early_raw_cycles ({ u64 val; \ + asm volatile("isb; mrs %0, cntvct_el0" : "=r"(val)); val; }) +#elif defined(CONFIG_X86_64) + #define __early_raw_cycles ({ u64 val; \ + asm volatile("lfence; rdtsc; shl $32, %%rdx; or %%rdx, %%rax" \ + : "=a"(val) : : "rdx"); val; }) +#elif defined(CONFIG_RISCV_TIMER) + #define __early_raw_cycles ({ u64 val; \ + asm volatile("fence; rdtime %0" : "=r"(val)); val; }) +#else + #define __early_raw_cycles 0 +#endif + +#endif /* _EARLY_TIMES_H */ --- a/kernel/printk/printk.c 2026-04-15 11:39:55.372066802 +0200 +++ b/kernel/printk/printk.c 2026-04-15 11:39:33.458953582 +0200 @@ -2151,6 +2151,16 @@ int vprintk_store(int facility, int leve */ ts_nsec = local_clock(); +#if CONFIG_PRINTK_EARLY_BOOT_TIMINGS +#include "early_times.h" + /* + * Very few developers are using this feature and they're expecting to deal + * with it as a single point of change hack to be further customised by them. + */ + if (unlikely(!ts_nsec)) + ts_nsec = __early_raw_cycles; +#endif + if (!printk_enter_irqsave(recursion_ptr, irqflags)) return 0;