On Tue, Dec 23, 2025 at 6:51 PM Motomasa Suzuki <moto0123moto(a)gmail.com> wrote:
This commit enhances the 'sys' command to show if a kernel livepatch is
currently in a transition phase, directly within the KERNEL output line.
Currently, diagnosing system state during or immediately after livepatch
operations can be ambiguous. While 'livepatch' is indicated by
'[LIVEPATCH]', there's no direct indicator within 'crash' itself to
show if a livepatch is actively applying, reverting, or in some other
transient state. This lack of immediate visibility can complicate crash
analysis, as the system might be in an inconsistent state due to an
ongoing patch application/reversion.
This change introduces a new '[TRANSITION]' flag which appears next to
'[LIVEPATCH]' and '[TAINTED]' in the 'sys' command output. This
flag is set if the livepatch subsystem indicates an in-progress
transition
(e.g., as exposed via '/sys/kernel/livepatch/<patch_name>/transition').
Example 'sys' output with this change:
KERNEL: /usr/lib/debug/lib/modules/<version_name>/vmlinux [LIVEPATCH]
[TRANSITION] [TAINTED]
This enhancement provides critical, at-a-glance information for
developers and administrators, allowing them to quickly ascertain if
ongoing livepatch activity might be influencing a system's behavior
during crash investigations. It directly leverages existing kernel
livepatch status infrastructure to enrich the crash utility's diagnostic
capabilities.
Changes v1 -> v2:
- Reworked klp_patches list traversal to use the crash utility’s
do_list() helper instead of manual list handling.
The v2 LGTM, so ack.
Thanks,
Tao Liu
Signed-off-by: Motomasa Suzuki <moto0123moto(a)gmail.com>
---
defs.h | 1 +
kernel.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/defs.h b/defs.h
index ceed3a9..cfdfa08 100644
--- a/defs.h
+++ b/defs.h
@@ -2289,6 +2289,7 @@ struct offset_table { /* stash of commonly-used
offsets */
long bpf_ringbuf_consumer_pos;
long bpf_ringbuf_nr_pages;
long hrtimer_clock_base_index;
+ long klp_patch_list;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/kernel.c b/kernel.c
index bb148d0..ccc4b3d 100644
--- a/kernel.c
+++ b/kernel.c
@@ -464,6 +464,11 @@ kernel_init()
"list_head.next offset: %ld: list command may fail\n",
OFFSET(list_head_next));
+ if (STRUCT_EXISTS("klp_patch")) {
+ if (MEMBER_EXISTS("klp_patch", "list"))
+ MEMBER_OFFSET_INIT(klp_patch_list, "klp_patch",
"list");
+ }
+
MEMBER_OFFSET_INIT(hlist_node_next, "hlist_node", "next");
MEMBER_OFFSET_INIT(hlist_node_pprev, "hlist_node",
"pprev");
STRUCT_SIZE_INIT(hlist_head, "hlist_head");
@@ -5689,6 +5694,70 @@ is_livepatch(void)
return FALSE;
}
+struct klp_transition_ctx {
+ ulong transition_patch;
+ int found;
+};
+
+static int
+klp_transition_match(void *entry, void *data)
+{
+ struct klp_transition_ctx *ctx = data;
+
+ if ((ulong)entry == ctx->transition_patch) {
+ ctx->found = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+is_livepatch_transition(void)
+{
+ struct kernel_list_head head;
+ struct list_data ld;
+ struct klp_transition_ctx ctx;
+ ulong transition_patch;
+ ulong list_addr;
+ int ret;
+
+ if (!try_get_symbol_data("klp_transition_patch",
+ sizeof(ulong), &transition_patch) || !transition_patch)
+ return FALSE;
+
+ if (!STRUCT_EXISTS("klp_patch") || !VALID_MEMBER(klp_patch_list) ||
+ !kernel_symbol_exists("klp_patches"))
+ return TRUE;
+
+ list_addr = symbol_value("klp_patches");
+ if (!readmem(list_addr, KVADDR, &head, sizeof(head),
"klp_patches",
+ RETURN_ON_ERROR | QUIET))
+ return TRUE;
+
+ if (!head.next || head.next == (void *)list_addr)
+ return TRUE;
+
+ BZERO(&ctx, sizeof(ctx));
+ ctx.transition_patch = transition_patch;
+
+ BZERO(&ld, sizeof(ld));
+ ld.flags = LIST_CALLBACK|CALLBACK_RETURN|RETURN_ON_LIST_ERROR|
+ RETURN_ON_DUPLICATE|LIST_ALLOCATE;
+ ld.start = (ulong)head.next;
+ ld.end = list_addr;
+ ld.member_offset = OFFSET(list_head_next);
+ ld.list_head_offset = OFFSET(klp_patch_list);
+ ld.callback_func = klp_transition_match;
+ ld.callback_data = &ctx;
+
+ ret = do_list(&ld);
+ if (ret < 0)
+ return FALSE;
+
+ return ctx.found;
+}
+
/*
* Display system stats at init-time or for the sys command.
*/
@@ -5732,17 +5801,19 @@ display_sys_stats(void)
}
} else {
if (pc->system_map) {
- fprintf(fp, " SYSTEM MAP: %s%s%s\n",
pc->system_map,
+ fprintf(fp, " SYSTEM MAP: %s%s%s%s\n",
pc->system_map,
is_livepatch() ? " [LIVEPATCH]" :
"",
+ is_livepatch_transition() ? " [TRANSITION]" :
"",
is_kernel_tainted() ? " [TAINTED]" :
"");
fprintf(fp, "DEBUG KERNEL: %s %s\n",
pc->namelist_orig ?
pc->namelist_orig : pc->namelist,
debug_kernel_version(pc->namelist));
} else
- fprintf(fp, " KERNEL: %s%s%s\n",
pc->namelist_orig ?
+ fprintf(fp, " KERNEL: %s%s%s%s\n",
pc->namelist_orig ?
pc->namelist_orig : pc->namelist,
is_livepatch() ? " [LIVEPATCH]" :
"",
+ is_livepatch_transition() ? " [TRANSITION]" :
"",
is_kernel_tainted() ? " [TAINTED]" :
"");
}
--
2.47.3