Debrand for AlmaLinux

This commit is contained in:
Eduard Abdullin 2026-08-12 01:19:49 +00:00 committed by root
commit fee1bc9bdd
46 changed files with 4576 additions and 17 deletions

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,99 @@
From 61fe107ff96a22e7df0029877529b7ce6da36850 Mon Sep 17 00:00:00 2001
From: Tao Liu <ltao@redhat.com>
Date: Fri, 24 Apr 2026 15:14:08 +1200
Subject: [PATCH 01/39] crash-9.0.1 -> crash-9.0.2
Signed-off-by: Tao Liu <ltao@redhat.com>
---
.rh_rpm_package | 2 +-
README | 18 +++++++++---------
help.c | 2 +-
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/.rh_rpm_package b/.rh_rpm_package
index 1ef1fd1e0ed7..3beeadd423d3 100644
--- a/.rh_rpm_package
+++ b/.rh_rpm_package
@@ -1 +1 @@
-9.0.1++
+9.0.2
diff --git a/README b/README
index 9ef8c75d5485..e2d740a57f8f 100644
--- a/README
+++ b/README
@@ -80,8 +80,8 @@
To build the crash utility:
- $ tar -xf crash-9.0.1.tar.gz
- $ cd crash-9.0.1
+ $ tar -xf crash-9.0.2.tar.gz
+ $ cd crash-9.0.2
$ make
To cross compile the crash utility for aarch64 on x86_64:
@@ -137,7 +137,7 @@
If neither /dev/mem or /dev/crash are available, then /proc/kcore will be
be used as the live memory source. If /proc/kcore is also restricted, then
the Red Hat /dev/crash driver may be compiled and installed; its source
- is included in the crash-9.0.1/memory_driver subdirectory.
+ is included in the crash-9.0.2/memory_driver subdirectory.
If the kernel file is stored in /boot, /, /boot/efi, or in any /usr/src
or /usr/lib/debug/lib/modules subdirectory, then no command line arguments
@@ -148,8 +148,8 @@
$ crash
- crash 9.0.1
- Copyright (C) 2002-2025 Red Hat, Inc.
+ crash 9.0.2
+ Copyright (C) 2002-2026 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
@@ -174,7 +174,7 @@
KERNEL: /boot/vmlinux
DUMPFILE: /dev/mem
CPUS: 1
- DATE: Tue Jun 10 18:03:09 CST 2025
+ DATE: Fri Apr 24 10:38:29 CST 2026
UPTIME: 10 days, 22:55:18
LOAD AVERAGE: 0.08, 0.03, 0.01
TASKS: 42
@@ -216,8 +216,8 @@
$ crash vmlinux vmcore
- crash 9.0.0
- Copyright (C) 2002-2025 Red Hat, Inc.
+ crash 9.0.2
+ Copyright (C) 2002-2026 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
@@ -242,7 +242,7 @@
KERNEL: vmlinux
DUMPFILE: vmcore
CPUS: 4
- DATE: Tue Jun 10 18:03:09 CST 2025
+ DATE: Fri Apr 24 10:38:29 CST 2026
UPTIME: 00:02:40
LOAD AVERAGE: 2.24, 0.96, 0.37
TASKS: 70
diff --git a/help.c b/help.c
index 91d155098993..810075b97f35 100644
--- a/help.c
+++ b/help.c
@@ -8557,7 +8557,7 @@ display_version(void)
static
char *version_info[] = {
-"Copyright (C) 2002-2025 Red Hat, Inc.",
+"Copyright (C) 2002-2026 Red Hat, Inc.",
"Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation",
"Copyright (C) 1999-2006 Hewlett-Packard Co",
"Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited",
--
2.54.0

View File

@ -0,0 +1,21 @@
From 89d36e12d7b753028038dfb93068fdfd0f6f1501 Mon Sep 17 00:00:00 2001
From: Tao Liu <ltao@redhat.com>
Date: Fri, 24 Apr 2026 15:15:19 +1200
Subject: [PATCH 02/39] Mark start of 9.0.3 development phase with version
9.0.2++
Signed-off-by: Tao Liu <ltao@redhat.com>
---
.rh_rpm_package | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.rh_rpm_package b/.rh_rpm_package
index 3beeadd423d3..a3cb14a5a842 100644
--- a/.rh_rpm_package
+++ b/.rh_rpm_package
@@ -1 +1 @@
-9.0.2
+9.0.2++
--
2.54.0

View File

@ -0,0 +1,91 @@
From 4e4dc1c89310e4106f7d0da8029084124c5e016d Mon Sep 17 00:00:00 2001
From: Austin Kim <austindh.kim@gmail.com>
Date: Mon, 6 Apr 2026 13:53:37 +0900
Subject: [PATCH 03/39] RISCV64: introduce riscv64_VTOP() and riscv64_PTOV()
The VTOP and PTOV macros are typically implemented using architecture-specific
functions to improve maintainability and debugging capabilities.
This patch moves the RISCV64-specific VTOP/PTOV logic from inline macros
in defs.h to dedicated functions in riscv64.c. This refactoring makes the
code easier to understand and provides a better location for adding
future debugging or validation checks.
No functional changes are introduced by this patch.
Signed-off-by: Austin Kim <austindh.kim@gmail.com>
---
defs.h | 14 ++++----------
riscv64.c | 24 ++++++++++++++++++++++++
2 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/defs.h b/defs.h
index a6f43725b6b8..54f1725d089a 100644
--- a/defs.h
+++ b/defs.h
@@ -3803,16 +3803,8 @@ typedef signed int s32;
/*
* Direct memory mapping
*/
-#define PTOV(X) \
- (((unsigned long)(X)+(machdep->kvbase)) - machdep->machspec->phys_base)
-#define VTOP(X) ({ \
- ulong _X = X; \
- (THIS_KERNEL_VERSION >= LINUX(5,13,0) && \
- (_X) >= machdep->machspec->kernel_link_addr) ? \
- ((unsigned long)(_X)-(machdep->machspec->va_kernel_pa_offset)): \
- (((unsigned long)(_X)-(machdep->kvbase)) + \
- machdep->machspec->phys_base); \
- })
+#define PTOV(X) riscv64_PTOV((ulong)(X))
+#define VTOP(X) riscv64_VTOP((ulong)(X))
#define PAGEBASE(X) (((ulong)(X)) & (ulong)machdep->pagemask)
/*
@@ -7211,6 +7203,8 @@ void riscv64_display_regs_from_elf_notes(int, FILE *);
void riscv64_init(int);
void riscv64_dump_machdep_table(ulong);
int riscv64_IS_VMALLOC_ADDR(ulong);
+ulong riscv64_PTOV(ulong);
+ulong riscv64_VTOP(ulong);
#define display_idt_table() \
error(FATAL, "-d option is not applicable to RISCV64 architecture\n")
diff --git a/riscv64.c b/riscv64.c
index ee9d4e321f6b..ff7b19eeb0d7 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -1606,6 +1606,30 @@ riscv64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verb
}
}
+ulong riscv64_PTOV(ulong paddr)
+{
+ ulong vaddr;
+ ulong offset = paddr - machdep->machspec->phys_base;
+
+ vaddr = offset + machdep->kvbase;
+
+ return vaddr;
+}
+
+ulong
+riscv64_VTOP(ulong addr)
+{
+ ulong paddr;
+
+ if ( (THIS_KERNEL_VERSION >= LINUX(5,13,0)) &&
+ (addr >= machdep->machspec->kernel_link_addr))
+ paddr = (addr - (machdep->machspec->va_kernel_pa_offset));
+ else
+ paddr = (addr - (ulong)machdep->kvbase + machdep->machspec->phys_base);
+
+ return paddr;
+}
+
static int
riscv64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose)
{
--
2.54.0

View File

@ -0,0 +1,39 @@
From c0097c84fed048e3e073f73a72ec9f16f8232c4b Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Thu, 16 Apr 2026 04:35:42 +0000
Subject: [PATCH 04/39] Fix wrong detection of "[LIVEPATCH]" on old kernels
The crash-utility wrongly prints "[LIVEPATCH]" occasionally on old
kernels:
KERNEL: vmlinux [LIVEPATCH]
DUMPFILE: dump.d31 [PARTIAL DUMP]
...
RELEASE: 2.6.24
In is_livepatch(), show_kernel_tains() does not set any value to buf on
kernels that have the "tainted" symbol, so it's printed when the
uninitialized buf has "K" by chance. On very old kernels, but this
issue affects makedumpfile tests.
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
kernel.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel.c b/kernel.c
index 8781d6a22414..1e639ec6a007 100644
--- a/kernel.c
+++ b/kernel.c
@@ -5678,7 +5678,7 @@ is_livepatch(void)
{
int i;
struct load_module *lm;
- char buf[BUFSIZE];
+ char buf[BUFSIZE] = {0};
show_kernel_taints(buf, !VERBOSE);
if (strstr(buf, "K")) /* TAINT_LIVEPATCH */
--
2.54.0

View File

@ -0,0 +1,103 @@
From 5da4d159a5efec95988349abfa12050dfc6578a6 Mon Sep 17 00:00:00 2001
From: Lucas Oakley <soakley@redhat.com>
Date: Wed, 6 May 2026 09:26:51 -0400
Subject: [PATCH 05/39] Fix unwinding with 32k stacks in ppc64le
With commit 18f14afe2816 ("powerpc/64s: Increase default stack
size to 32KB") in upstream ppc64le, the stack size was adjusted
from 16k to 32k. As a result, ppc64_back_trace() can bail
prematurely when checking if the stack pointer exists in the
range of the range of the irq stacks, since SIZE(irq_ctx), used
in ppc64_in_irqstack(), is set to 16k. This patch ensures that
irq_ctx is updated to 32k if a 32k stack size is used.
Tested against:
el6 x86_64
el7 ppc64le s390x x86_64
el8 aarch64 ppc64le s390x x86_64
el9 aarch64 ppc64le s390x x86_64
el10 aarch64 ppc64le s390x x86_64
Without the commit:
crash> bt -c 3
PID: 17524 TASK: c0000000b2c0e400 CPU: 3 COMMAND: "xyz"
cannot find the stack info.
With the commit:
crash> bt -c 3
PID: 17524 TASK: c0000000b2c0e400 CPU: 3 COMMAND: "xyz"
#0 [c000001dff7d7c10] smp_call_function_single_async at c00000000028dd38
#1 [c000001dff7d7d30] _raw_spin_lock_irqsave at c000000001023f1c
#2 [c000001dff7d7d60] ibmvscsi_handle_crq at c0080000044635ec [ibmvscsi]
#3 [c000001dff7d7de0] ibmvscsi_task at c008000004463804 [ibmvscsi]
#4 [c000001dff7d7e30] tasklet_action_common.constprop.0 at c0000000001624cc
#5 [c000001dff7d7e90] __do_softirq at c0000000010244cc
#6 [c000001dff7d7f90] do_softirq_own_stack at c000000000016480
#7 [c000000140d67700] __irq_exit_rcu at c0000000001613b8
#8 [c000000140d67730] irq_exit at c000000000162170
#9 [c000000140d67750] do_IRQ at c000000000015fa4
#10 [c000000140d67780] hardware_interrupt_common_virt at c000000000009080
Hardware Interrupt [500] exception frame:
R0: c000000001023de0 R1: c000000140d67a90 R2: c000000002c02500
R3: c00800000b30269c R4: 0000000000000001 R5: 0000000000000001
R6: ffffffffffffffff R7: 0000000000000000 R8: 0000000000000000
R9: fffffffffffe0000 R10: 0000000000000002 R11: 0000000048422824
R12: c000000001023d70 R13: c000001dffffd480 R14: 00007ffd6ef6d238
R15: 0000000000000028 R16: c000001dfc1e2280 R17: c000001dfc1e2280
R18: 00000000ab97fa48 R19: 0000000000000000 R20: 0000000000000001
R21: 0000001df9ff0000 R22: 0000000000000028 R23: c0000000021f2280
R24: 0000000000000000 R25: c0000000021f2280 R26: c0000000021f2380
R27: 0000000000000000 R28: c00800000b30269c R29: 0000000000000000
R30: c000000002c47190 R31: 000000000020000b
NIP: c0000000000aea14 MSR: 800000000280b033 OR3: c0000000000ae944
CTR: c000000001023d70 LR: c000000001023de0 XER: 0000000020040001
CCR: 0000000088422824 MQ: 0000000000000000 DAR: 0000000000000001
DSISR: c0080000073b1e94 Syscall Result: 0000000000000000
[NIP : queued_spin_lock_slowpath+1204]
[LR : _raw_spin_lock+112]
#11 [c000000140d67a90] queued_spin_lock_slowpath at c0000000000aea14
#12 [c000000140d67bb0] _raw_spin_lock at c000000001023de0 (unreliable)
#13 [c000000140d67bd0] dm_blk_close at c00800000b2c6850 [dm_mod]
#14 [c000000140d67c00] blkdev_put_whole at c0000000007d2738
#15 [c000000140d67c30] bdev_release at c0000000007d3a38
#16 [c000000140d67c90] blkdev_release at c0000000007d4224
#17 [c000000140d67cb0] __fput at c0000000005d2e98
#18 [c000000140d67d00] task_work_run at c00000000018fb14
#19 [c000000140d67d50] do_notify_resume at c000000000020bd4
#20 [c000000140d67d80] interrupt_exit_user_prepare_main at c00000000002ed98
#21 [c000000140d67de0] syscall_exit_prepare at c00000000002f240
#22 [c000000140d67e10] system_call_vectored_common at c00000000000bff4
Signed-off-by: Lucas Oakley <soakley@redhat.com>
---
task.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/task.c b/task.c
index ec04b556c337..d7f3c129a709 100644
--- a/task.c
+++ b/task.c
@@ -739,6 +739,18 @@ irqstacks_init(void)
if (!(tt->softirq_tasks = (ulong *)calloc(NR_CPUS, sizeof(ulong))))
error(FATAL, "cannot malloc softirq_tasks space.");
+ /*
+ * With the stack size adjusted from 16k to 32k for ppc64le,
+ * such as rhel-9.4.z. We need to ensure that SIZE(irq_ctx) is
+ * correctly set so the unwinder doesn't prematurely bail
+ * when switching between the kernel stack and irq stacks.
+ * The stack size is updated in task_init(), which calls
+ * this routine, irqstacks_init() after checking for the
+ * existence of irq_ctx.
+ */
+ if (STACKSIZE() > SIZE(irq_ctx))
+ ASSIGN_SIZE(irq_ctx) = STACKSIZE();
+
thread_info_buf = GETBUF(SIZE(irq_ctx));
if ((hard_sp = per_cpu_symbol_search("per_cpu__hardirq_ctx")) ||
--
2.54.0

View File

@ -0,0 +1,232 @@
From fa4ba91e0383343112478decce41cea3b499da15 Mon Sep 17 00:00:00 2001
From: lipengfei28 <lipengfei28@xiaomi.com>
Date: Wed, 1 Apr 2026 10:32:35 +0800
Subject: [PATCH 06/39] arm64: fix gdb register feeding for exception frames
and stack switching
On ARM64, crash's gdb bt relies on feeding a register snapshot (PC/SP/FP,
etc.) into gdb and letting gdb unwind from there. Before this change, the
register snapshot handling across exception frames / stack switching could be
broken in several ways, leading to truncated backtraces, garbage substacks, or
invalid addresses (e.g. -3 / 0xff...fd) showing up in gdb output.
Fixes included in this patch:
Fix out-of-bounds read when populating gdb registers from an exception frame
In arm64_print_exception_frame() the previous code copied
sizeof(struct arm64_pt_regs) bytes from a smaller stackframe-derived
buffer, which could read past valid data and poison the gdb registers.
Replace this with explicit field/register assignment and initialize the
bitmap accordingly.
Improve unwinding across IRQ/overflow stack transitions on newer kernels
When switching stacks (IRQ / overflow), gdb may stop at the trampoline (e.g.
call_on_irq_stack) because the discontinuity prevents it from recovering
the caller frame automatically. For UNW_4_14+, "peek" one frame ahead by
reading the saved FP/PC from the current frame, and register that as the
next gdb substack, so gdb can continue unwinding on the process stack.
Avoid creating invalid/empty substacks
Only add a gdb substack when the recovered PC is non-zero, preventing bogus
threads from being created.
This patch only changes ARM64 unwinding/register setup logic. It does not try
to reformat or merge gdb output.
Tested on: Android Linux 6.x, arm64
Signed-off-by: lipengfei28 <lipengfei28@xiaomi.com>
---
arm64.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 87 insertions(+), 17 deletions(-)
diff --git a/arm64.c b/arm64.c
index c125655d6807..d17709299b2d 100644
--- a/arm64.c
+++ b/arm64.c
@@ -228,7 +228,7 @@ arm64_get_current_task_reg(int regno, const char *name,
return FALSE;
if (sid && sid <= extra_stacks_idx) {
- ur_bitmap = extra_stacks_regs[extra_stacks_idx - 1];
+ ur_bitmap = extra_stacks_regs[sid - 1];
goto get_sub;
}
@@ -3026,6 +3026,9 @@ static char *arm64_exception_functions[] = {
"do_el0_irq_bp_hardening",
"do_sp_pc_abort",
"handle_bad_stack",
+ "el1h_64_sync",
+ "el1h_64_irq",
+ "el1h_64_error",
NULL
};
@@ -3123,6 +3126,11 @@ arm64_print_stackframe_entry(struct bt_info *bt, int level, struct arm64_stackfr
fprintf(ofp, "\n");
+ if (STREQ(name, "el1h_64_irq") || STREQ(name, "el1h_64_sync")) {
+ if (arm64_is_kernel_exception_frame(bt, frame->sp))
+ arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp);
+ }
+
if (bt->flags & BT_LINE_NUMBERS) {
get_line_number(branch_pc, buf, FALSE);
if (strlen(buf))
@@ -3775,11 +3783,13 @@ arm64_back_trace_cmd(struct bt_info *bt)
REG_SEQ(arm64_pt_regs, pc));
SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap,
REG_SEQ(arm64_pt_regs, sp));
- if (!bt->machdep ||
+ if (extra_stacks_regs[extra_stacks_idx]->ur.pc &&
+ (bt->task != tt->panic_task) &&
+ (!bt->machdep ||
(extra_stacks_regs[extra_stacks_idx]->ur.sp !=
((struct user_regs_bitmap_struct *)(bt->machdep))->ur.sp &&
extra_stacks_regs[extra_stacks_idx]->ur.pc !=
- ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc)) {
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc))) {
gdb_add_substack (extra_stacks_idx++);
}
}
@@ -3925,11 +3935,13 @@ arm64_back_trace_cmd_v2(struct bt_info *bt)
REG_SEQ(arm64_pt_regs, pc));
SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap,
REG_SEQ(arm64_pt_regs, sp));
- if (!bt->machdep ||
+ if (extra_stacks_regs[extra_stacks_idx]->ur.pc &&
+ (bt->task != tt->panic_task) &&
+ (!bt->machdep ||
(extra_stacks_regs[extra_stacks_idx]->ur.sp !=
((struct user_regs_bitmap_struct *)(bt->machdep))->ur.sp &&
extra_stacks_regs[extra_stacks_idx]->ur.pc !=
- ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc)) {
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc))) {
gdb_add_substack (extra_stacks_idx++);
}
}
@@ -4232,6 +4244,46 @@ arm64_in_kdump_text_on_irq_stack(struct bt_info *bt)
return FALSE;
}
+static void
+arm64_gdb_add_next_frame_substack(struct bt_info *bt, const struct arm64_stackframe *frame)
+{
+ struct machine_specific *ms = machdep->machspec;
+ struct user_regs_bitmap_struct *ur_ptr;
+ ulong next_fp, next_pc, next_sp;
+
+ if (!extra_stacks_regs[extra_stacks_idx]) {
+ extra_stacks_regs[extra_stacks_idx] = (struct user_regs_bitmap_struct *)
+ malloc(sizeof(struct user_regs_bitmap_struct));
+ }
+
+ memset(extra_stacks_regs[extra_stacks_idx], 0, sizeof(struct user_regs_bitmap_struct));
+ ur_ptr = extra_stacks_regs[extra_stacks_idx];
+
+ next_fp = GET_STACK_ULONG(frame->fp);
+ next_pc = GET_STACK_ULONG(frame->fp + 8);
+ next_sp = frame->fp + 16;
+
+ if (is_kernel_text(next_pc | ms->CONFIG_ARM64_KERNELPACMASK))
+ next_pc |= ms->CONFIG_ARM64_KERNELPACMASK;
+
+ ur_ptr->ur.pc = next_pc;
+ ur_ptr->ur.sp = next_sp;
+ ur_ptr->ur.regs[29] = next_fp;
+
+ SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, pc));
+ SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, sp));
+ SET_BIT(ur_ptr->bitmap, REG_SEQ(arm64_pt_regs, regs[0]) + 29);
+
+ if (ur_ptr->ur.pc &&
+ (!bt->machdep ||
+ (ur_ptr->ur.sp !=
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.sp &&
+ ur_ptr->ur.pc !=
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc))) {
+ gdb_add_substack(extra_stacks_idx++);
+ }
+}
+
static int
arm64_switch_stack(struct bt_info *bt, struct arm64_stackframe *frame, FILE *ofp)
{
@@ -4263,8 +4315,11 @@ arm64_switch_stack(struct bt_info *bt, struct arm64_stackframe *frame, FILE *ofp
if (frame->fp == 0)
return USER_MODE;
- if (!(machdep->flags & UNW_4_14))
+ if (!(machdep->flags & UNW_4_14)) {
arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp);
+ } else {
+ arm64_gdb_add_next_frame_substack(bt, frame);
+ }
return KERNEL_MODE;
}
@@ -4300,8 +4355,11 @@ arm64_switch_stack_from_overflow(struct bt_info *bt, struct arm64_stackframe *fr
if (frame->fp == 0)
return USER_MODE;
- if (!(machdep->flags & UNW_4_14))
+ if (!(machdep->flags & UNW_4_14)) {
arm64_print_exception_frame(bt, frame->sp, KERNEL_MODE, ofp);
+ } else {
+ arm64_gdb_add_next_frame_substack(bt, frame);
+ }
return KERNEL_MODE;
}
@@ -4549,6 +4607,9 @@ arm64_print_exception_frame(struct bt_info *bt, ulong pt_regs, int mode, FILE *o
(ulong)regs->orig_x0, (ulong)regs->syscallno);
fprintf(ofp, " PSTATE: %08lx\n", (ulong)regs->pstate);
} else if (!(bt->flags & BT_EFRAME_SEARCH)) {
+ struct user_regs_bitmap_struct *ur_ptr;
+ int i;
+
if (!extra_stacks_regs[extra_stacks_idx]) {
extra_stacks_regs[extra_stacks_idx] =
(struct user_regs_bitmap_struct *)
@@ -4556,17 +4617,26 @@ arm64_print_exception_frame(struct bt_info *bt, ulong pt_regs, int mode, FILE *o
}
memset(extra_stacks_regs[extra_stacks_idx], 0,
sizeof(struct user_regs_bitmap_struct));
- memcpy(&extra_stacks_regs[extra_stacks_idx]->ur, regs,
- sizeof(struct arm64_pt_regs));
- for (int i = 0; i < sizeof(struct arm64_pt_regs)/sizeof(long); i++)
- SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap, i);
- if (!bt->machdep ||
- (extra_stacks_regs[extra_stacks_idx]->ur.sp !=
+
+ ur_ptr = extra_stacks_regs[extra_stacks_idx];
+
+ ur_ptr->ur.pc = regs->pc;
+ ur_ptr->ur.sp = regs->sp;
+ ur_ptr->ur.pstate = regs->pstate;
+ for (i = 0; i < 31; i++)
+ ur_ptr->ur.regs[i] = regs->regs[i];
+
+ for (i = 0; i < 34; i++)
+ SET_BIT(ur_ptr->bitmap, i);
+
+ if (ur_ptr->ur.pc &&
+ (!bt->machdep ||
+ (ur_ptr->ur.sp !=
((struct user_regs_bitmap_struct *)(bt->machdep))->ur.sp &&
- extra_stacks_regs[extra_stacks_idx]->ur.pc !=
- ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc)) {
- gdb_add_substack (extra_stacks_idx++);
- }
+ ur_ptr->ur.pc !=
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.pc))) {
+ gdb_add_substack(extra_stacks_idx++);
+ }
}
}
--
2.54.0

View File

@ -0,0 +1,32 @@
From 4f66cea2487d511dbc9b7ff10a7e9fb98337273f Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Wed, 29 Apr 2026 14:13:25 +0800
Subject: [PATCH 07/39] RISCV64: fix pmd address calculation in
riscv64_vtop_4level_4k
Fix a bug in the PMD address calculation where the operator was
incorrectly using '+' instead of '*'. This caused the PMD index
to be added as an offset rather than multiplied by the entry size,
resulting in incorrect page table traversal for 4-level 4KB paging.
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
riscv64.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/riscv64.c b/riscv64.c
index ff7b19eeb0d7..8c6aa9557ec5 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -1241,7 +1241,7 @@ riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
/* PMD */
FILL_PMD(PAGEBASE(pmd_base), PHYSADDR, PAGESIZE());
- pmd_addr = pmd_base + sizeof(pmd_t) + pmd_index_l4_4k(vaddr);
+ pmd_addr = pmd_base + sizeof(pmd_t) * pmd_index_l4_4k(vaddr);
pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_addr));
if (verbose)
fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
--
2.54.0

View File

@ -0,0 +1,71 @@
From ce0fc6b73de86e448fd13541a4ea6b60d7af0e1c Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Wed, 6 May 2026 12:27:29 +0800
Subject: [PATCH 08/39] symbols: optimize symname_hash with larger table and
FNV-1a hash
Optimize the symbol name hash table to reduce collision and improve
performance, especially on RISC-V architecture where symbol lookup
is a hotspot.
Changes:
- Increase SYMNAME_HASH from 512 to 16384 (32x) to reduce collisions
- Replace simple hash with FNV-1a algorithm for better distribution
- Remove strlen() call, compute hash in single pass
This reduces the average chain length in hash buckets significantly,
improving symbol lookup performance at the cost of ~248KB additional
memory.
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
defs.h | 2 +-
symbols.c | 15 +++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/defs.h b/defs.h
index 54f1725d089a..6a91343f0313 100644
--- a/defs.h
+++ b/defs.h
@@ -2900,7 +2900,7 @@ struct downsized {
#define SYMVAL_HASH_INDEX(vaddr) \
(((vaddr) >> machdep->pageshift) % SYMVAL_HASH)
-#define SYMNAME_HASH (512)
+#define SYMNAME_HASH (16384)
#define PATCH_KERNEL_SYMBOLS_START ((char *)(1))
#define PATCH_KERNEL_SYMBOLS_STOP ((char *)(2))
diff --git a/symbols.c b/symbols.c
index e6865cabef74..afdf4a61cea2 100644
--- a/symbols.c
+++ b/symbols.c
@@ -1170,16 +1170,19 @@ symname_hash_init(void)
static unsigned int
symname_hash_index(char *name)
{
- unsigned int len, value;
- unsigned char *array = (unsigned char *)name;
+ unsigned int hash = 2166136261U;
+ unsigned char *p = (unsigned char *)name;
- len = strlen(name);
- if (!len)
+ if (!*p)
error(FATAL, "The length of the symbol name is zero!\n");
- value = array[len - 1] * array[len / 2];
+ /* FNV-1a hash algorithm for better distribution */
+ while (*p) {
+ hash ^= *p++;
+ hash *= 16777619;
+ }
- return (array[0] ^ value) % SYMNAME_HASH;
+ return hash % SYMNAME_HASH;
}
/*
--
2.54.0

View File

@ -0,0 +1,76 @@
From ad1e05f3d1ae96ce5862b2877db4bded9a9a6336 Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Wed, 6 May 2026 12:27:30 +0800
Subject: [PATCH 09/39] RISCV64: add mapping symbol filter in
riscv64_verify_symbol
Add mapping symbol filter in riscv64_verify_symbol() to filter out
linker mapping symbols like '.L*', 'L0*' and '$*'. These symbols
should not end up in the symbol list.
Also optimize riscv64_verify_symbol() by consolidating name validity
checks at the function entry.
Changes:
- riscv64.c: Add mapping symbol filter and optimize name checks
- symbols.c: Add machine_type("RISCV64") to enable verify_symbol()
call in store_module_kallsyms_v2()
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
riscv64.c | 14 +++++++++++---
symbols.c | 5 ++++-
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/riscv64.c b/riscv64.c
index 8c6aa9557ec5..f0783d570c7b 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -209,15 +209,23 @@ riscv64_cmd_mach(void)
static int
riscv64_verify_symbol(const char *name, ulong value, char type)
{
- if (CRASHDEBUG(8) && name && strlen(name))
+ if (!name || !strlen(name))
+ return FALSE;
+
+ if (CRASHDEBUG(8))
fprintf(fp, "%08lx %s\n", value, name);
+ /* Filter out mapping symbols */
+ if ((name[0] == '.' && name[1] == 'L') ||
+ (name[0] == 'L' && name[1] == '0') ||
+ (name[0] == '$'))
+ return FALSE;
+
if (!(machdep->flags & KSYMS_START)) {
if (STREQ(name, "_text") || STREQ(name, "_stext"))
machdep->flags |= KSYMS_START;
- return (name && strlen(name) && !STRNEQ(name, "__func__.") &&
- !STRNEQ(name, "__crc_"));
+ return (!STRNEQ(name, "__func__.") && !STRNEQ(name, "__crc_"));
}
return TRUE;
diff --git a/symbols.c b/symbols.c
index afdf4a61cea2..8eb8b37abc23 100644
--- a/symbols.c
+++ b/symbols.c
@@ -2990,9 +2990,12 @@ store_module_kallsyms_v2(struct load_module *lm, int start, int curr,
* or '$x' for ARM64, and '$d'.
* On LoongArch we have linker mapping symbols like '.L'
* or 'L0'.
+ * On RISCV64 we have linker mapping symbols like '.L',
+ * 'L0' or '$'.
* Make sure that these don't end up into our symbol list.
*/
- if ((machine_type("ARM") || machine_type("ARM64") || machine_type("LOONGARCH64")) &&
+ if ((machine_type("ARM") || machine_type("ARM64") || machine_type("LOONGARCH64") ||
+ machine_type("RISCV64")) &&
!machdep->verify_symbol(nameptr, ec->st_value, ec->st_info))
continue;
--
2.54.0

View File

@ -0,0 +1,71 @@
From 7ff3146d46ea9afb0724b57b3425c9a965a67d12 Mon Sep 17 00:00:00 2001
From: "Aureau, Georges (Kernel Tools ERT)" <georges.aureau@hpe.com>
Date: Thu, 7 May 2026 04:12:32 +0000
Subject: [PATCH 10/39] Fix get_xtime() for kernel 6.13 and higher
On kernels 6.13 and higher, DATE is not displayed properly:
crash> sys | grep -e DATE -e REL
DATE: Wed Dec 31 18:00:00 CST 1969
RELEASE: 7.0.0-14-generic
The function get_xtime() was enhanced to support:
- kernel 6.13 ("shadow_timekeeper" moved to "struct tk_data tk_core")
- kernel 6.17 ("tk_core" is now "timekeeper_data[0]).
With this get_xtime() fix:
crash> sys | grep DATE
DATE: Mon Apr 27 09:19:50 CDT 2026
Signed-off-by: Georges Aureau <georges.aureau@hpe.com>
---
defs.h | 1 +
kernel.c | 14 +++++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/defs.h b/defs.h
index 6a91343f0313..89044b18cdbe 100644
--- a/defs.h
+++ b/defs.h
@@ -2290,6 +2290,7 @@ struct offset_table { /* stash of commonly-used offsets */
long bpf_ringbuf_nr_pages;
long hrtimer_clock_base_index;
long klp_patch_list;
+ long tk_data_timekeeper;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/kernel.c b/kernel.c
index 1e639ec6a007..6dfc8714a1ff 100644
--- a/kernel.c
+++ b/kernel.c
@@ -258,6 +258,7 @@ kernel_init()
MEMBER_OFFSET_INIT(timekeeper_xtime, "timekeeper", "xtime");
MEMBER_OFFSET_INIT(timekeeper_xtime_sec, "timekeeper", "xtime_sec");
+ MEMBER_OFFSET_INIT(tk_data_timekeeper, "tk_data", "timekeeper");
get_xtime(&kt->date);
if (CRASHDEBUG(1))
fprintf(fp, "xtime timespec.tv_sec: %lx: %s\n",
@@ -11134,7 +11135,18 @@ get_xtime(struct timespec *date)
struct syment *sp;
uint64_t xtime_sec;
- if (VALID_MEMBER(timekeeper_xtime) &&
+ if (VALID_MEMBER(tk_data_timekeeper) &&
+ VALID_MEMBER(timekeeper_xtime_sec)) {
+ long offset = OFFSET(tk_data_timekeeper) +
+ OFFSET(timekeeper_xtime_sec);
+ if ((sp = kernel_symbol_search("timekeeper_data")) ||
+ (sp = kernel_symbol_search("tk_core"))) {
+ readmem(sp->value + offset, KVADDR,
+ &xtime_sec, sizeof(uint64_t),
+ "tk_data timekeeper xtime_sec", RETURN_ON_ERROR);
+ date->tv_sec = (__time_t)xtime_sec;
+ }
+ } else if (VALID_MEMBER(timekeeper_xtime) &&
(sp = kernel_symbol_search("timekeeper"))) {
readmem(sp->value + OFFSET(timekeeper_xtime), KVADDR,
date, sizeof(struct timespec),
--
2.54.0

View File

@ -0,0 +1,74 @@
From 3a415c07a18a465df2580365b43b4d8f8a5d815b Mon Sep 17 00:00:00 2001
From: Alexander Egorenkov <egorenar@linux.ibm.com>
Date: Tue, 28 Apr 2026 12:16:37 +0200
Subject: [PATCH 11/39] symbols: Add support for mod symtab with combined GPL
and non-GPL symbols
The commit b4760ff2a5e4 ("module: deprecate usage of *_gpl sections in module loader")
eliminated separate GPL symbol sections representing GPL only symbols.
Therefore, depending on whether the member gpl_syms is present
in struct module, decide whether GPL module symbols are separated or not.
https://lore.kernel.org/all/20260326-kflagstab-v5-4-fa0796fe88d9@google.com
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
---
kernel.c | 13 ++++++++-----
symbols.c | 9 +++++++--
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/kernel.c b/kernel.c
index 6dfc8714a1ff..eb9754c5e082 100644
--- a/kernel.c
+++ b/kernel.c
@@ -3635,9 +3635,11 @@ module_init(void)
case KMOD_V2:
MEMBER_OFFSET_INIT(module_num_syms, "module", "num_syms");
MEMBER_OFFSET_INIT(module_list, "module", "list");
- MEMBER_OFFSET_INIT(module_gpl_syms, "module", "gpl_syms");
- MEMBER_OFFSET_INIT(module_num_gpl_syms, "module",
- "num_gpl_syms");
+ if (MEMBER_EXISTS("module", "gpl_syms")) {
+ MEMBER_OFFSET_INIT(module_gpl_syms, "module", "gpl_syms");
+ MEMBER_OFFSET_INIT(module_num_gpl_syms, "module",
+ "num_gpl_syms");
+ }
if (MEMBER_EXISTS("module", "mem")) { /* 6.4 and later */
kt->flags2 |= KMOD_MEMORY; /* MODULE_MEMORY() can be used. */
@@ -3831,8 +3833,9 @@ module_init(void)
nsyms = UINT(modbuf + OFFSET(module_nsyms));
break;
case KMOD_V2:
- nsyms = UINT(modbuf + OFFSET(module_num_syms)) +
- UINT(modbuf + OFFSET(module_num_gpl_syms));
+ nsyms = UINT(modbuf + OFFSET(module_num_syms));
+ if (VALID_MEMBER(module_num_gpl_syms))
+ nsyms += UINT(modbuf + OFFSET(module_num_gpl_syms));
break;
}
diff --git a/symbols.c b/symbols.c
index 8eb8b37abc23..3c62f54d4a93 100644
--- a/symbols.c
+++ b/symbols.c
@@ -1979,9 +1979,14 @@ store_module_symbols_6_4(ulong total, int mods_installed)
"module buffer", FAULT_ON_ERROR);
syms = ULONG(modbuf + OFFSET(module_syms));
- gpl_syms = ULONG(modbuf + OFFSET(module_gpl_syms));
nsyms = UINT(modbuf + OFFSET(module_num_syms));
- ngplsyms = UINT(modbuf + OFFSET(module_num_gpl_syms));
+ if (VALID_MEMBER(module_gpl_syms)) {
+ gpl_syms = ULONG(modbuf + OFFSET(module_gpl_syms));
+ ngplsyms = UINT(modbuf + OFFSET(module_num_gpl_syms));
+ } else {
+ gpl_syms = 0;
+ ngplsyms = 0;
+ }
nksyms = UINT(modbuf + OFFSET(module_num_symtab));
--
2.54.0

View File

@ -0,0 +1,202 @@
From 744dfa3a190d3dd2b1cc42d3378a1cb299958c0a Mon Sep 17 00:00:00 2001
From: Huang Shijie <huangsj@hygon.cn>
Date: Mon, 18 May 2026 19:31:38 +0800
Subject: [PATCH 12/39] add folio_order function
The folio_order() has three versions:
1.) In verion 1, folio_order() uses page[1].compound_order to
get the folio order. We use "folio_batch" as the indicator.
Please refer to patch set:
https://lore.kernel.org/all/20211208042256.1923824-1-willy@infradead.org/
2.) In version 2, the following patch introduces _folio_order:
c3a15bff46cb5149 "mm: reimplement folio_order() and folio_nr_pages()"
We use "_folio_order" as the indicator.
Please refer to patch set:
https://lore.kernel.org/all/20220808193430.3378317-1-willy@infradead.org/
3.) In version 3, the following patch replaces the _folio_order with _flags_1:
ebc1baf5c9b46c22 "mm: free up a word in the first tail page"
folio_order() uses _flags_1 to get the folio order.
We use "free_huge_folio" as the indicator.
Please refer to patch set:
https://lore.kernel.org/linux-mm/20230816151201.3655946-1-willy@infradead.org/
(Note, we do not use the struct modifications within the patch set as the indicator,
since they have been removed in later kernel.)
This patch will be used in later patches.
Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
defs.h | 8 ++++++
memory.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
symbols.c | 6 +++++
3 files changed, 93 insertions(+)
diff --git a/defs.h b/defs.h
index 89044b18cdbe..bd996ef38707 100644
--- a/defs.h
+++ b/defs.h
@@ -2291,6 +2291,9 @@ struct offset_table { /* stash of commonly-used offsets */
long hrtimer_clock_base_index;
long klp_patch_list;
long tk_data_timekeeper;
+ long page_compound_order;
+ long folio__folio_order;
+ long folio__flags_1;
};
struct size_table { /* stash of commonly-used sizes */
@@ -2470,6 +2473,9 @@ struct size_table { /* stash of commonly-used sizes */
long cpumask_t;
long task_struct_exit_state;
long bpf_ringbuf_map;
+ long page_compound_order;
+ long folio__folio_order;
+ long folio__flags_1;
};
struct array_table {
@@ -5998,6 +6004,8 @@ ulong do_xarray(ulong, int, struct list_pair *);
#define XARRAY_TAG_MASK (3UL)
#define XARRAY_TAG_INTERNAL (2UL)
+int folio_order(ulong folio);
+
int file_dump(ulong, ulong, ulong, int, int);
#define DUMP_FULL_NAME 0x1
#define DUMP_INODE_ONLY 0x2
diff --git a/memory.c b/memory.c
index 17423a588464..502f8fdd0ea7 100644
--- a/memory.c
+++ b/memory.c
@@ -410,6 +410,10 @@ mem_init(void)
DISPLAY_DEFAULT = (sizeof(long) == 8) ? DISPLAY_64 : DISPLAY_32;
}
+#define FOLIO_ORDER_V1 1
+#define FOLIO_ORDER_V2 2
+#define FOLIO_ORDER_V3 3
+static int folio_order_version;
/*
* Stash a few popular offsets and some basic kernel virtual memory
@@ -547,6 +551,37 @@ vm_init(void)
MEMBER_OFFSET_INIT(page_freelist, "page", "freelist");
MEMBER_OFFSET_INIT(page_page_type, "page", "page_type");
+ /*
+ * The "folio_batch" was introduced at patch set:
+ * https://lore.kernel.org/all/20211208042256.1923824-1-willy@infradead.org/
+ * Use it as the indicator for folio_order() version 1.
+ */
+ if (STRUCT_EXISTS("folio_batch"))
+ folio_order_version = FOLIO_ORDER_V1;
+
+ MEMBER_OFFSET_INIT(page_compound_order, "page", "compound_order");
+ MEMBER_SIZE_INIT(page_compound_order, "page", "compound_order");
+
+ /*
+ * The "_folio_order" was introduced at patch set:
+ * https://lore.kernel.org/all/20220808193430.3378317-1-willy@infradead.org/
+ * Use it as the indicator for folio_order() version 2.
+ */
+ MEMBER_SIZE_INIT(folio__folio_order, "folio", "_folio_order");
+ MEMBER_OFFSET_INIT(folio__folio_order, "folio", "_folio_order");
+ if (VALID_MEMBER(folio__folio_order))
+ folio_order_version = FOLIO_ORDER_V2;
+
+ /*
+ * The "free_huge_folio()" was introduced at patch set:
+ * https://lore.kernel.org/linux-mm/20230816151201.3655946-1-willy@infradead.org/
+ * Use it as the indicator for folio_order() version 3.
+ */
+ MEMBER_OFFSET_INIT(folio__flags_1, "folio", "_flags_1");
+ MEMBER_SIZE_INIT(folio__flags_1, "folio", "_flags_1");
+ if (kernel_symbol_exists("free_huge_folio"))
+ folio_order_version = FOLIO_ORDER_V3;
+
MEMBER_OFFSET_INIT(mm_struct_pgd, "mm_struct", "pgd");
MEMBER_OFFSET_INIT(swap_info_struct_swap_file,
@@ -20426,6 +20461,50 @@ static unsigned int oo_objects(ulong oo)
return (oo & ((1 << 16) - 1));
}
+int
+folio_order(ulong folio)
+{
+ ulong v = 0;
+ int PG_head = 16;
+
+ if (folio_order_version == FOLIO_ORDER_V1) {
+ readmem(folio + OFFSET(page_flags), KVADDR, &v, sizeof(ulong),
+ "folio.page.flags", FAULT_ON_ERROR);
+ if (!(v & (1 << PG_head)))
+ return 0;
+
+ readmem(folio + SIZE(page) + OFFSET(page_compound_order), KVADDR, &v,
+ SIZE(page_compound_order), "page[1].compound_order", FAULT_ON_ERROR);
+
+ return v;
+ } else if (folio_order_version == FOLIO_ORDER_V2) {
+ readmem(folio + OFFSET(page_flags), KVADDR, &v, sizeof(ulong),
+ "folio.page.flags", FAULT_ON_ERROR);
+ if (!(v & (1 << PG_head)))
+ return 0;
+
+ readmem(folio + OFFSET(folio__folio_order), KVADDR, &v,
+ SIZE(folio__folio_order), "folio->_folio_order", FAULT_ON_ERROR);
+
+ return v;
+ } else if (folio_order_version == FOLIO_ORDER_V3) {
+ /* The PG_head changes to bit 6 in this version */
+ PG_head = 6;
+
+ readmem(folio + OFFSET(page_flags), KVADDR, &v, sizeof(ulong),
+ "folio.page.flags", FAULT_ON_ERROR);
+ if (!(v & (1 << PG_head)))
+ return 0;
+
+ readmem(folio + OFFSET(folio__flags_1), KVADDR, &v,
+ SIZE(folio__flags_1), "folio->_flags_1", FAULT_ON_ERROR);
+
+ return v & 0xff;
+ } else {
+ return 0;
+ }
+}
+
#ifdef NOT_USED
ulong
slab_to_kmem_cache_node(struct meminfo *si, ulong slab_page)
diff --git a/symbols.c b/symbols.c
index 3c62f54d4a93..c0285058c5cb 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10462,6 +10462,9 @@ dump_offset_table(char *spec, ulong makestruct)
fprintf(fp, " page_private: %ld\n", OFFSET(page_private));
fprintf(fp, " page_page_type: %ld\n",
OFFSET(page_page_type));
+ fprintf(fp, " page_compound_order: %ld\n", OFFSET(page_compound_order));
+ fprintf(fp, " folio__folio_order: %ld\n", OFFSET(folio__folio_order));
+ fprintf(fp, " folio__flags_1: %ld\n", OFFSET(folio__flags_1));
fprintf(fp, " trace_print_flags_mask: %ld\n",
OFFSET(trace_print_flags_mask));
@@ -11972,6 +11975,9 @@ dump_offset_table(char *spec, ulong makestruct)
fprintf(fp, "\n size_table:\n");
fprintf(fp, " page: %ld\n", SIZE(page));
fprintf(fp, " page_flags: %ld\n", SIZE(page_flags));
+ fprintf(fp, " page_compound_order: %ld\n", SIZE(page_compound_order));
+ fprintf(fp, " folio__folio_order: %ld\n", SIZE(folio__folio_order));
+ fprintf(fp, " folio__flags_1: %ld\n", SIZE(folio__flags_1));
fprintf(fp, " trace_print_flags: %ld\n", SIZE(trace_print_flags));
fprintf(fp, " free_area_struct: %ld\n",
SIZE(free_area_struct));
--
2.54.0

View File

@ -0,0 +1,232 @@
From 5d494e101bdd7d87add51d13bc1b42af8d8a151d Mon Sep 17 00:00:00 2001
From: Huang Shijie <huangsj@hygon.cn>
Date: Mon, 18 May 2026 19:31:39 +0800
Subject: [PATCH 13/39] xarray: add large folio support
We may meet a large folio at two cases:
1.) when height is 1, the real data layout may looks like this:
--------------------------------------------
crash> p *(struct xa_node*)0xffff889883a6a910
$5 = {
shift = 0 '\000',
offset = 0 '\000',
count = 64 '@',
nr_values = 0 '\000',
.................
slots = { 0xffffea0061d21000, 0x2, 0x2, 0x2,
0xffffea0121d85c00, 0x12, 0x12, 0x12,
0xffffea0121d9f400, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
0xffffea0121583800, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0xffffea0121833e00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
0xffffea0121eb3800, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82
},
.................
}
--------------------------------------------
The page orders for above folios:
0xffffea0061d21000 --> order 2
0xffffea0121d85c00 --> order 2
0xffffea0121d9f400 --> order 3
0xffffea0121583800 --> order 3
0xffffea0121833e00 --> order 3
0xffffea0121eb3800 --> order 5
2.) when height is not 1, the real data layout may looks like this:
--------------------------------------------
slot: { 0xffff8a2c866b745a, 0xffff8a2c866b16d2, 0xfffff9fcc4ea4000, 0xfffff9fcc4eb9000, ..}
--------------------------------------------
The 0xffff8a2c866b745a and 0xffff8a2c866b16d2 is a internal node, not a folio.
The page orders for above folios:
0xfffff9fcc4ea4000 --> order 6
Current code does not work correctly with large folio page cache.
This patch adds the large folio support with following:
1.) Add XARRAY_TYPE_PAGE_CACHE flag for do_xarray().
2.) Add update_count hook for do_xarray_info{},
add update_off hook for xarray_ops{}.
3.) changed the do_xarray_iter() to check the update_off
at proper place (height == 1 and height > 1).
4.) Implement the folio_update_count/folio_xarray_update_off
hooks for large folio page cache.
Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
defs.h | 5 +++++
filesys.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
tools.c | 15 +++++++++++++--
3 files changed, 64 insertions(+), 7 deletions(-)
diff --git a/defs.h b/defs.h
index bd996ef38707..e7e97e7d80b2 100644
--- a/defs.h
+++ b/defs.h
@@ -5689,6 +5689,9 @@ int do_radix_tree_traverse(ulong ptr, int is_root, struct radix_tree_ops *ops);
struct xarray_ops {
void (*entry)(ulong node, ulong slot, const char *path,
ulong index, void *private);
+ uint (*update_off)(ulong node, uint height, char *path, ulong index,
+ ulong slot, uint off, ulong shift, struct xarray_ops *ops,
+ bool *should_continue);
uint radix;
void *private;
};
@@ -6003,6 +6006,8 @@ ulong do_xarray(ulong, int, struct list_pair *);
#define XARRAY_DUMP_CB (5)
#define XARRAY_TAG_MASK (3UL)
#define XARRAY_TAG_INTERNAL (2UL)
+#define XARRAY_TYPE_PAGE_CACHE 0x8
+extern ulong XA_CHUNK_SHIFT;
int folio_order(ulong folio);
diff --git a/filesys.c b/filesys.c
index 34944e2739aa..35d55a543739 100644
--- a/filesys.c
+++ b/filesys.c
@@ -2289,7 +2289,7 @@ dump_inode_page_cache_info(ulong inode)
if (root_rnode)
count = do_radix_tree(root_rnode, RADIX_TREE_DUMP_CB, &lp);
else if (xarray)
- count = do_xarray(xarray, XARRAY_DUMP_CB, &lp);
+ count = do_xarray(xarray, XARRAY_DUMP_CB | XARRAY_TYPE_PAGE_CACHE, &lp);
if (count != nrpages)
error(INFO, "%s page count: %ld nrpages: %ld\n",
@@ -4217,12 +4217,46 @@ struct do_xarray_info {
ulong maxcount;
ulong count;
void *data;
+ ulong (*update_count)(ulong);
};
+
+static ulong
+folio_update_count(ulong slot)
+{
+ return 1 << folio_order(slot);
+}
+
+static uint folio_xarray_update_off(ulong node, uint height, char *path, ulong index,
+ ulong slot, uint off, ulong shift, struct xarray_ops *ops, bool *should_continue)
+{
+ uint order;
+
+ *should_continue = false;
+
+ if (height == 1) {
+ order = folio_order(slot) % XA_CHUNK_SHIFT;
+ return 1 << order;
+ }
+
+ /* height > 1 */
+ if ((slot & XARRAY_TAG_MASK) == 0) {
+ ops->entry(node, slot, path, index | off, ops->private);
+ *should_continue = true;
+ order = folio_order(slot) % XA_CHUNK_SHIFT;
+ return 1 << order;
+ }
+ return 1;
+}
+
static void do_xarray_count(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
- info->count++;
+
+ if (info->update_count)
+ info->count += info->update_count(slot);
+ else
+ info->count++;
}
static void do_xarray_search(ulong node, ulong slot, const char *path,
ulong index, void *private)
@@ -4239,8 +4273,9 @@ static void do_xarray_dump(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
+
fprintf(fp, "[%ld] %lx\n", index, slot);
- info->count++;
+ do_xarray_count(node, slot, path, index, private);
}
static void do_xarray_gather(ulong node, ulong slot, const char *path,
ulong index, void *private)
@@ -4274,7 +4309,8 @@ static void do_xarray_dump_cb(ulong node, ulong slot, const char *path,
"operation failed: entry: %ld item: %lx\n",
info->count, slot);
}
- info->count++;
+
+ do_xarray_count(node, slot, path, index, private);
}
/*
@@ -4318,7 +4354,12 @@ do_xarray(ulong root, int flag, struct list_pair *xp)
.private = &info,
};
- switch (flag)
+ if (flag & XARRAY_TYPE_PAGE_CACHE) {
+ info.update_count = folio_update_count;
+ ops.update_off = folio_xarray_update_off;
+ }
+
+ switch (flag & 0x7)
{
case XARRAY_COUNT:
ops.entry = do_xarray_count;
diff --git a/tools.c b/tools.c
index 69250c48fa91..7dc11aedf56b 100644
--- a/tools.c
+++ b/tools.c
@@ -4728,7 +4728,7 @@ error_height:
return -1;
}
-static ulong XA_CHUNK_SHIFT = UNINITIALIZED;
+ulong XA_CHUNK_SHIFT = UNINITIALIZED;
static ulong XA_CHUNK_SIZE = UNINITIALIZED;
static ulong XA_CHUNK_MASK = UNINITIALIZED;
@@ -4737,21 +4737,32 @@ do_xarray_iter(ulong node, uint height, char *path,
ulong index, struct xarray_ops *ops)
{
uint off;
+ uint update_off;
+ bool should_continue;
if (!hq_enter(node))
error(FATAL,
"\nduplicate tree node: %lx\n", node);
- for (off = 0; off < XA_CHUNK_SIZE; off++) {
+ for (off = 0; off < XA_CHUNK_SIZE; off += update_off) {
ulong slot;
ulong shift = (height - 1) * XA_CHUNK_SHIFT;
+ update_off = 1;
+
readmem(node + OFFSET(xa_node_slots) +
sizeof(void *) * off, KVADDR, &slot, sizeof(void *),
"xa_node.slots[off]", FAULT_ON_ERROR);
if (!slot)
continue;
+ if (ops->update_off) {
+ update_off = ops->update_off(node, height, path, index,
+ slot, off, shift, ops, &should_continue);
+ if (should_continue)
+ continue;
+ }
+
if ((slot & XARRAY_TAG_MASK) == XARRAY_TAG_INTERNAL)
slot &= ~XARRAY_TAG_INTERNAL;
--
2.54.0

View File

@ -0,0 +1,216 @@
From 28889183d3f55c581b0810aa8bdac31e20bbf364 Mon Sep 17 00:00:00 2001
From: Huang Shijie <huangsj@hygon.cn>
Date: Mon, 18 May 2026 19:31:40 +0800
Subject: [PATCH 14/39] add "files -n" command for an inode
In the NUMA machine, it is useful to know the memory distribution of
an inode page cache:
How many pages in the node 0?
How many pages in the node 1?
Add "files -n" command to get the memory distribution information:
1.) Add new argument for dump_inode_page_cache_info()
2.) make page_to_nid() a global function.
3.) Add summary_inode_page() to check each page's node
information.
4.) Use print_inode_summary_info() to print the
memory distribution information of an inode.
Signed-off-by: Huang Shijie <huangsj@hygon.cn>
---
defs.h | 1 +
filesys.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
help.c | 24 +++++++++++++++++++++-
memory.c | 4 ++--
4 files changed, 81 insertions(+), 8 deletions(-)
diff --git a/defs.h b/defs.h
index e7e97e7d80b2..6373ee1831af 100644
--- a/defs.h
+++ b/defs.h
@@ -5948,6 +5948,7 @@ int dump_inode_page(ulong);
ulong valid_section_nr(ulong);
void display_memory_from_file_offset(ulonglong, long, void *);
void swap_info_init(void);
+int page_to_nid(ulong);
/*
* filesys.c
diff --git a/filesys.c b/filesys.c
index 35d55a543739..7ff47876bbb7 100644
--- a/filesys.c
+++ b/filesys.c
@@ -49,7 +49,7 @@ static int match_file_string(char *, char *, char *);
static ulong get_root_vfsmount(char *);
static void check_live_arch_mismatch(void);
static long get_inode_nrpages(ulong);
-static void dump_inode_page_cache_info(ulong);
+static void dump_inode_page_cache_info(ulong, void *callback);
#define DENTRY_CACHE (20)
#define INODE_CACHE (20)
@@ -2240,8 +2240,38 @@ get_inode_nrpages(ulong i_mapping)
return nrpages;
}
+/* Used to collect the numa information for an inode */
+static ulong *numa_node;
+
static void
-dump_inode_page_cache_info(ulong inode)
+print_inode_summary_info(void)
+{
+ int i;
+
+ fprintf(fp, " NODE PAGES\n");
+ for (i = 0; i < vt->numnodes; i++)
+ fprintf(fp, " %2d %8ld\n", i, numa_node[i]);
+}
+
+static int
+summary_inode_page(ulong page)
+{
+ int node;
+
+ if (!is_page_ptr(page, NULL))
+ error(FATAL, "Invalid inode page(0x%lx)\n", page);
+
+ node = page_to_nid(page);
+ if (node < 0 || node >= vt->numnodes)
+ error(FATAL, "Invalid node(%d) for page(0x%lx)\n", node, page);
+
+ numa_node[node] += 1 << folio_order(page);
+
+ return 1;
+}
+
+static void
+dump_inode_page_cache_info(ulong inode, void *callback)
{
char *inode_buf;
ulong i_mapping, nrpages, root_rnode, xarray, count;
@@ -2284,7 +2314,7 @@ dump_inode_page_cache_info(ulong inode)
root_rnode = i_mapping + OFFSET(address_space_page_tree);
lp.index = 0;
- lp.value = (void *)&dump_inode_page;
+ lp.value = callback;
if (root_rnode)
count = do_radix_tree(root_rnode, RADIX_TREE_DUMP_CB, &lp);
@@ -2324,7 +2354,7 @@ cmd_files(void)
ref = NULL;
refarg = NULL;
- while ((c = getopt(argcnt, args, "d:R:p:c")) != EOF) {
+ while ((c = getopt(argcnt, args, "d:n:R:p:c")) != EOF) {
switch(c)
{
case 'R':
@@ -2343,11 +2373,31 @@ cmd_files(void)
display_dentry_info(value);
return;
+ case 'n':
+ if (VALID_MEMBER(address_space_page_tree) &&
+ VALID_MEMBER(inode_i_mapping)) {
+ value = htol(optarg, FAULT_ON_ERROR, NULL);
+
+ /* Allocate the array for this inode */
+ numa_node = (ulong *)GETBUF(sizeof(ulong) * vt->numnodes);
+ BZERO(numa_node, sizeof(ulong) * vt->numnodes);
+
+ dump_inode_page_cache_info(value, (void *)&summary_inode_page);
+
+ /* Print out the NUMA node information for this inode */
+ print_inode_summary_info();
+
+ FREEBUF(numa_node);
+ numa_node = NULL;
+ } else
+ option_not_supported('n');
+ return;
+
case 'p':
if (VALID_MEMBER(address_space_page_tree) &&
VALID_MEMBER(inode_i_mapping)) {
value = htol(optarg, FAULT_ON_ERROR, NULL);
- dump_inode_page_cache_info(value);
+ dump_inode_page_cache_info(value, (void *)&dump_inode_page);
} else
option_not_supported('p');
return;
diff --git a/help.c b/help.c
index 810075b97f35..5cd5c30e99a3 100644
--- a/help.c
+++ b/help.c
@@ -7900,7 +7900,7 @@ NULL
char *help_files[] = {
"files",
"open files",
-"[-d dentry] | [-p inode] | [-c] [-R reference] [pid | taskp] ... ",
+"[-d dentry] | [-p inode] | [-n inode]|[-c] [-R reference] [pid | taskp] ... ",
" This command displays information about open files of a context.",
" It prints the context's current root directory and current working",
" directory, and then for each open file descriptor it prints a pointer",
@@ -7915,6 +7915,8 @@ char *help_files[] = {
" super block, file type, and full pathname.",
" -p inode given a hexadecimal inode address, dump all of its pages",
" that are in the page cache.",
+" -n inode given a hexadecimal inode address, check all the pages",
+" in the page cache, and display a NUMA node distribution.",
" -c for each open file descriptor, prints a pointer to its",
" inode, a pointer to the inode's i_mapping address_space",
" structure, the number of pages of the inode that are in",
@@ -8024,6 +8026,26 @@ char *help_files[] = {
" ca1ddde0 2eeef000 f59b91ac 3 2 82c referenced,uptodate,lru,private",
" ca36b300 3b598000 f59b91ac 4 2 82c referenced,uptodate,lru,private",
" ca202680 30134000 f59b91ac 5 2 82c referenced,uptodate,lru,private",
+" ",
+" For the inode at address ffff8898857c55a8, display the NUMA node",
+" distribution of its pages that are in the page cache:",
+" %s> files -n ffff8898857c55a8",
+" INODE NRPAGES",
+" ffff8898857c55a8 51748",
+" ",
+" NODE PAGES",
+" 0 51536",
+" 1 0",
+" 2 0",
+" 3 0",
+" 4 0",
+" 5 212",
+" 6 0",
+" 7 0",
+" 8 0",
+" 9 0",
+" 10 0",
+" 11 0",
" ",
NULL
};
diff --git a/memory.c b/memory.c
index 502f8fdd0ea7..38c6a139e984 100644
--- a/memory.c
+++ b/memory.c
@@ -301,7 +301,7 @@ static int dump_vm_event_state(void);
static int dump_page_states(void);
static int generic_read_dumpfile(ulonglong, void *, long, char *, ulong);
static int generic_write_dumpfile(ulonglong, void *, long, char *, ulong);
-static int page_to_nid(ulong);
+int page_to_nid(ulong);
static int get_kmem_cache_list(ulong **);
static int get_kmem_cache_root_list(ulong **);
static int get_kmem_cache_child_list(ulong **, ulong);
@@ -20093,7 +20093,7 @@ is_kmem_cache_addr_common(ulong vaddr, char *kbuf)
/*
* Kernel-config-neutral page-to-node evaluator.
*/
-static int
+int
page_to_nid(ulong page)
{
int i;
--
2.54.0

View File

@ -0,0 +1,178 @@
From 7b3f6e0f60be1dd2d4c1b20175be96137cf61438 Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Fri, 29 May 2026 05:00:03 +0000
Subject: [PATCH 15/39] x86_64: Fix "bt" command for noreturn functions
On x86_64, the "bt" command resolves saved return addresses with
value_search(textaddr). However, a return address is the instruction
pointer after the call, not the call site itself.
This becomes a problem when the caller ends with a call to a noreturn
function. In that case, the saved return address can match the start
address of the following symbol, and "bt" loses track of the call chain
and this can lead to very long session initialization.
The same issue also affects symbol+offset formatting, line number
lookup, and ORC-based frame size resolution.
Fix it by resolving normal backtrace return addresses with textaddr-1,
while keeping exact textaddr handling for real RIP values saved in
exception frames. Add value_to_symstr_trace() so the displayed
symbol+offset still reflects the original return address value.
Suggested-by: Kosuke Tatsukawa <tatsu-ab1@nec.com>
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
defs.h | 1 +
symbols.c | 24 +++++++++++++++++++++---
x86_64.c | 31 ++++++++++++++++++++++++++-----
3 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/defs.h b/defs.h
index 6373ee1831af..4a42bc962817 100644
--- a/defs.h
+++ b/defs.h
@@ -5807,6 +5807,7 @@ struct syment *prev_symbol(char *, struct syment *);
void get_symbol_data(char *, long, void *);
int try_get_symbol_data(char *, long, void *);
char *value_to_symstr(ulong, char *, ulong);
+char *value_to_symstr_trace(ulong, char *, ulong);
char *value_symbol(ulong);
ulong symbol_value(char *);
ulong symbol_value_module(char *, char *);
diff --git a/symbols.c b/symbols.c
index c0285058c5cb..78e400ba3756 100644
--- a/symbols.c
+++ b/symbols.c
@@ -104,6 +104,7 @@ static void free_structure(struct struct_elem *);
static unsigned char is_right_brace(const char *);
static struct struct_elem *find_node(struct struct_elem *, char *);
static void dump_node(struct struct_elem *, char *, unsigned char, unsigned char);
+static char *_value_to_symstr(ulong value, char *buf, ulong radix, int trace);
static int module_mem_type(ulong, struct load_module *);
static ulong module_mem_end(ulong, struct load_module *);
@@ -5973,14 +5974,25 @@ generic_machdep_value_to_symbol(ulong value, ulong *offset)
return NULL;
}
+char *
+value_to_symstr(ulong value, char *buf, ulong radix)
+{
+ return _value_to_symstr(value, buf, radix, 0);
+}
+
+char *
+value_to_symstr_trace(ulong value, char *buf, ulong radix)
+{
+ return _value_to_symstr(value, buf, radix, 1);
+}
/*
* For a given value, format a string containing the nearest symbol name
* plus the offset if appropriate. Display the offset in the specified
* radix (10 or 16) -- if it's 0, set it to the current pc->output_radix.
*/
-char *
-value_to_symstr(ulong value, char *buf, ulong radix)
+static char *
+_value_to_symstr(ulong value, char *buf, ulong radix, int trace)
{
struct syment *sp;
ulong offset;
@@ -5996,7 +6008,13 @@ value_to_symstr(ulong value, char *buf, ulong radix)
if ((radix != 10) && (radix != 16))
radix = 16;
- if ((sp = value_search(value, &offset))) {
+ if (trace) {
+ sp = value_search(value-1, &offset);
+ offset++;
+ } else
+ sp = value_search(value, &offset);
+
+ if (sp) {
if (offset)
sprintf(buf, radix == 16 ? "%s+0x%lx" : "%s+%ld",
sp->name, offset);
diff --git a/x86_64.c b/x86_64.c
index b2cddbf8ba3d..ff283ed68191 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -3229,14 +3229,23 @@ x86_64_print_stack_entry(struct bt_info *bt, FILE *ofp, int level,
if (!(bt->flags & BT_SAVE_EFRAME_IP))
bt->eframe_ip = 0;
offset = 0;
- sp = value_search(text, &offset);
+ if (bt->flags & BT_SAVE_EFRAME_IP)
+ sp = value_search(text, &offset);
+ else {
+ sp = value_search(text-1, &offset);
+ offset++;
+ }
if (!sp)
return BACKTRACE_ENTRY_IGNORED;
name = sp->name;
if (offset && (bt->flags & BT_SYMBOL_OFFSET))
- name_plus_offset = value_to_symstr(text, buf2, bt->radix);
+ if (bt->flags & BT_SAVE_EFRAME_IP)
+ name_plus_offset = value_to_symstr(text, buf2, bt->radix);
+ else
+ /* text-1 is used in the function */
+ name_plus_offset = value_to_symstr_trace(text, buf2, bt->radix);
else
name_plus_offset = NULL;
@@ -3337,7 +3346,10 @@ x86_64_print_stack_entry(struct bt_info *bt, FILE *ofp, int level,
fprintf(ofp, "\n");
if (bt->flags & BT_LINE_NUMBERS) {
- get_line_number(text, buf1, FALSE);
+ if (bt->flags & BT_SAVE_EFRAME_IP)
+ get_line_number(text, buf1, FALSE);
+ else
+ get_line_number(text-1, buf1, FALSE);
if (strlen(buf1))
fprintf(ofp, " %s\n", buf1);
}
@@ -3864,8 +3876,10 @@ in_exception_stack:
}
level++;
+ bt->flags |= BT_SAVE_EFRAME_IP;
if ((framesize = x86_64_get_framesize(bt, bt->instptr, rsp, NULL)) >= 0)
rsp += framesize;
+ bt->flags &= ~BT_SAVE_EFRAME_IP;
}
}
@@ -8811,7 +8825,13 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp, char *stack_
return 0;
}
- if (!(sp = value_search(textaddr, &offset))) {
+ if (bt->flags & BT_SAVE_EFRAME_IP)
+ sp = value_search(textaddr, &offset);
+ else {
+ sp = value_search(textaddr-1, &offset);
+ offset++;
+ }
+ if (!sp) {
if (!(bt->flags & BT_FRAMESIZE_DEBUG))
bt->flags |= BT_FRAMESIZE_DISABLE;
return 0;
@@ -8887,7 +8907,8 @@ x86_64_get_framesize(struct bt_info *bt, ulong textaddr, ulong rsp, char *stack_
if ((sp->value >= kt->init_begin) && (sp->value < kt->init_end))
return 0;
- if ((machdep->flags & ORC) && (korc = orc_find(textaddr))) {
+ if ((machdep->flags & ORC) &&
+ (korc = orc_find(bt->flags & BT_SAVE_EFRAME_IP ? textaddr : textaddr-1))) {
if (CRASHDEBUG(1)) {
struct ORC_data *orc = &machdep->machspec->orc;
fprintf(fp,
--
2.54.0

View File

@ -0,0 +1,100 @@
From d0ee428664f90beaf498fa0edc129cdead204abc Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Mon, 1 Jun 2026 05:38:11 +0000
Subject: [PATCH 16/39] x86_64: Fix "bt" command to use correct ORC register
values on Linux 7.1 and later
Kernel commit 1735858caa4b ("objtool/x86: Reorder ORC register numbering")
changed the ORC register numbering. Without the patch, crash can interpret ORC
entry incorrectly and the "bt" command may generate broken backtraces on Linux
7.1 and later kernels like this:
crash> bt 1
PID: 1 TASK: ffff8ab0009cd100 CPU: 2 COMMAND: "systemd"
#0 [ffffd2218003b9c8] __schedule at ffffffffaa9d862b
#1 [ffffd2218003ba20] schedule at ffffffffaa9d8993
#2 [ffffd2218003ba30] schedule_hrtimeout_range_clock at ffffffffaa9df77b
#3 [ffffd2218003bab0] ep_poll at ffffffffaa1231e4
#4 [ffffd2218003bb50] do_epoll_wait at ffffffffaa123272
#5 [ffffd2218003bb88] __x64_sys_epoll_wait at ffffffffaa123b1f
#6 [ffffd2218003bbd8] do_syscall_64 at ffffffffaa9cca6c
#7 [ffffd2218003bc58] __memcg_slab_free_hook at ffffffffaa079da3
#8 [ffffd2218003bcf0] __memcg_slab_free_hook at ffffffffaa079da3
#9 [ffffd2218003bd50] __x64_sys_gettid at ffffffffa9ce1656
#10 [ffffd2218003bd58] do_syscall_64 at ffffffffaa9ccaa4
#11 [ffffd2218003bdc0] update_cfs_rq_load_avg at ffffffffa9d1bf59
#12 [ffffd2218003be00] __update_blocked_fair at ffffffffa9d214b8
#13 [ffffd2218003be70] sched_clock at ffffffffa9c460dc
#14 [ffffd2218003be78] sched_clock_cpu at ffffffffa9d4aeab
#15 [ffffd2218003be98] irqtime_account_irq at ffffffffa9d3af0d
#16 [ffffd2218003bec0] handle_softirqs at ffffffffa9cce5ac
#17 [ffffd2218003bf40] entry_SYSCALL_64_after_hwframe at ffffffffa9a0012b
Fix this by making ORC_REG_SP and ORC_REG_PREV_SP depend on kernel version, as
no other way was found.
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
defs.h | 6 ++++--
x86_64.c | 11 +++++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/defs.h b/defs.h
index 4a42bc962817..a4f70b773cd7 100644
--- a/defs.h
+++ b/defs.h
@@ -6650,6 +6650,8 @@ struct ORC_data {
orc_entry orc_entry_data;
int has_signal;
int has_end;
+ int reg_sp;
+ int reg_prev_sp;
};
#define ORC_TYPE_CALL ((machdep->flags & ORC_6_4) ? 2 : 0)
@@ -6660,11 +6662,11 @@ struct ORC_data {
#define UNWIND_HINT_TYPE_RESTORE 4
#define ORC_REG_UNDEFINED 0
-#define ORC_REG_PREV_SP 1
+#define ORC_REG_PREV_SP (machdep->machspec->orc.reg_prev_sp)
#define ORC_REG_DX 2
#define ORC_REG_DI 3
#define ORC_REG_BP 4
-#define ORC_REG_SP 5
+#define ORC_REG_SP (machdep->machspec->orc.reg_sp)
#define ORC_REG_R10 6
#define ORC_REG_R13 7
#define ORC_REG_BP_INDIRECT 8
diff --git a/x86_64.c b/x86_64.c
index ff283ed68191..55648697baf3 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -999,6 +999,8 @@ x86_64_dump_machdep_table(ulong arg)
fprintf(fp, " module_ORC: %s\n", ms->orc.module_ORC ? "TRUE" : "FALSE");
fprintf(fp, " has_signal: %s\n", ms->orc.has_signal ? "TRUE" : "FALSE");
fprintf(fp, " has_end: %s\n", ms->orc.has_end ? "TRUE" : "FALSE");
+ fprintf(fp, " reg_sp: %d\n", ms->orc.reg_sp);
+ fprintf(fp, " reg_prev_sp: %d\n", ms->orc.reg_prev_sp);
fprintf(fp, " lookup_num_blocks: %d\n", ms->orc.lookup_num_blocks);
fprintf(fp, " __start_orc_unwind_ip: %lx\n", ms->orc.__start_orc_unwind_ip);
fprintf(fp, " __stop_orc_unwind_ip: %lx\n", ms->orc.__stop_orc_unwind_ip);
@@ -6734,6 +6736,15 @@ x86_64_ORC_init(void)
if (orc->has_signal && !orc->has_end)
machdep->flags |= ORC_6_4;
+ /* See kernel commit 1735858caa4b */
+ if (THIS_KERNEL_VERSION >= LINUX(7,1,0)) {
+ ORC_REG_SP = 3;
+ ORC_REG_PREV_SP = 8;
+ } else {
+ ORC_REG_SP = 5;
+ ORC_REG_PREV_SP = 1;
+ }
+
machdep->flags |= ORC;
}
--
2.54.0

View File

@ -0,0 +1,38 @@
From c894f05d3cdc5d3e61775c3f67bd6a0a24362a92 Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Wed, 3 Jun 2026 07:12:57 +0000
Subject: [PATCH 17/39] Fix "kmem -i" option to display swap usage on Linux
6.18 and later
Kernel commit 8578e0c00dcf ("mm, swap: use the swap table to track the
swap count"), which is contained in Linux 6.18 and later kernels,
removed swapper_spaces symbol.
As a result, "kmem -i" skips swap usage output because the existing
check only looks for swapper_space/swapper_spaces.
Also check for the swap_info symbol so dump_swap_info() is called on
newer kernels as well.
Signed-off-by: Kazuhito Hagko <k-hagio-ab@nec.com>
---
memory.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/memory.c b/memory.c
index 38c6a139e984..15946c58eaf2 100644
--- a/memory.c
+++ b/memory.c
@@ -8871,7 +8871,8 @@ dump_kmeminfo(struct meminfo *mi)
* get swap data from dump_swap_info().
*/
fprintf(fp, "\n");
- if (symbol_exists("swapper_space") || symbol_exists("swapper_spaces")) {
+ if (symbol_exists("swap_info") ||
+ symbol_exists("swapper_space") || symbol_exists("swapper_spaces")) {
if (dump_swap_info(RETURN_ON_ERROR, &totalswap_pages,
&totalused_pages)) {
fprintf(fp, "%13s %7ld %11s ----\n",
--
2.54.0

View File

@ -0,0 +1,51 @@
From fdb94ed20ac85e8354224b0d691af342f4b63922 Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Wed, 3 Jun 2026 07:12:58 +0000
Subject: [PATCH 18/39] Fix "swap" command on Linux 7.1 and later
Kernel commit 0d6af9bcf383 ("mm, swap: use the swap table to track the
swap count"), which is contained in Linux 7.1 and later kernels, removed
swap_info_struct.swap_map member.
As a result, the "swap" command and "kmem -i" option fail with the
following error:
swap: invalid structure member offset: swap_info_struct_swap_map
FILE: memory.c LINE: 16293 FUNCTION: dump_swap_info()
Fix it by referencing swap_info_struct.swap_map only for SWAPINFO_V1,
where it is actually needed. Newer kernels no longer have that member,
and the command can use the existing inuse_pages handling instead.
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
memory.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/memory.c b/memory.c
index 15946c58eaf2..3f3aa274ab5a 100644
--- a/memory.c
+++ b/memory.c
@@ -16290,9 +16290,6 @@ dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages)
OFFSET(swap_info_struct_inuse_pages));
}
- swap_map = ULONG(vt->swap_info_struct +
- OFFSET(swap_info_struct_swap_map));
-
if (swap_file) {
if (VALID_MEMBER(swap_info_struct_swap_vfsmnt)) {
vfsmnt = ULONG(vt->swap_info_struct +
@@ -16322,6 +16319,9 @@ dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages)
if (vt->flags & SWAPINFO_V1) {
smap = (ushort *)GETBUF(sizeof(ushort) * max);
+ swap_map = ULONG(vt->swap_info_struct +
+ OFFSET(swap_info_struct_swap_map));
+
if (!readmem(swap_map, KVADDR, smap,
sizeof(ushort) * max, "swap_info swap_map data",
RETURN_ON_ERROR|QUIET)) {
--
2.54.0

View File

@ -0,0 +1,115 @@
From 32ac79ead77839e6a87657edfdccffa943c6ea19 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Thu, 4 Jun 2026 19:52:40 +0800
Subject: [PATCH 19/39] LoongArch64: Fix CPU registers reading from dump notes
LoongArch stores the register set exported in NT_PRSTATUS as an
elf_gregset_t. The special registers are laid out as orig_a0, csr_era,
csr_badvaddr, csr_crmd, csr_prmd, csr_euen, csr_ecfg and csr_estat
after the 32 GPRs.
The old crash-local register layout put csr_crmd at the first
special-register slot and placed epc near the end. That made help -r
and active-task unwinding read nonsensical CSR values from dump notes.
Match the local register structure and indexes to the kernel
pt_regs/elf_gregset_t layout. Also handle modern kernels where
crash_notes may refer to the per-cpu note storage directly, and use
NETDUMP ELF notes for libvirt/QEMU dump files when crash_notes does not
provide usable data.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 38 +++++++++++++++++++++++---------------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/loongarch64.c b/loongarch64.c
index 6437c4aa4656..5adb7d8038db 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -18,20 +18,23 @@
#include <elf.h>
#include "defs.h"
-/* from arch/loongarch/include/asm/ptrace.h */
+/*
+ * This matches the beginning of both kernel struct pt_regs and the
+ * elf_gregset_t layout exported in NT_PRSTATUS notes.
+ */
struct loongarch64_pt_regs {
/* Saved main processor registers. */
unsigned long regs[32];
/* Saved special registers. */
+ unsigned long orig_a0;
+ unsigned long csr_epc;
+ unsigned long csr_badvaddr;
unsigned long csr_crmd;
unsigned long csr_prmd;
unsigned long csr_euen;
unsigned long csr_ecfg;
unsigned long csr_estat;
- unsigned long csr_epc;
- unsigned long csr_badvaddr;
- unsigned long orig_a0;
};
struct loongarch64_unwind_frame {
@@ -108,13 +111,14 @@ typedef struct { ulong pte; } pte_t;
#define LOONGARCH64_EF_RA 1
#define LOONGARCH64_EF_SP 3
#define LOONGARCH64_EF_FP 22
-#define LOONGARCH64_EF_CSR_EPC 32
-#define LOONGARCH64_EF_CSR_BADVADDR 33
-#define LOONGARCH64_EF_CSR_CRMD 34
-#define LOONGARCH64_EF_CSR_PRMD 35
-#define LOONGARCH64_EF_CSR_EUEN 36
-#define LOONGARCH64_EF_CSR_ECFG 37
-#define LOONGARCH64_EF_CSR_ESTAT 38
+#define LOONGARCH64_EF_ORIG_A0 32
+#define LOONGARCH64_EF_CSR_EPC 33
+#define LOONGARCH64_EF_CSR_BADVADDR 34
+#define LOONGARCH64_EF_CSR_CRMD 35
+#define LOONGARCH64_EF_CSR_PRMD 36
+#define LOONGARCH64_EF_CSR_EUEN 37
+#define LOONGARCH64_EF_CSR_ECFG 38
+#define LOONGARCH64_EF_CSR_ESTAT 39
static struct machine_specific loongarch64_machine_specific = { 0 };
@@ -911,8 +915,12 @@ loongarch64_get_crash_notes(void)
/*
* Add __per_cpu_offset for each cpu to form the pointer to the notes
*/
- for (i = 0; i < kt->cpus; i++)
- notes_ptrs[i] = notes_ptrs[kt->cpus-1] + kt->__per_cpu_offset[i];
+ for (i = 0; i < kt->cpus; i++) {
+ if (IS_KVADDR(notes_ptrs[kt->cpus-1]))
+ notes_ptrs[i] = notes_ptrs[kt->cpus-1] + kt->__per_cpu_offset[i];
+ else
+ notes_ptrs[i] = crash_notes + kt->__per_cpu_offset[i];
+ }
}
buf = GETBUF(SIZE(note_buf));
@@ -1009,7 +1017,7 @@ loongarch64_get_elf_notes(void)
struct machine_specific *ms = machdep->machspec;
int i;
- if (!DISKDUMP_DUMPFILE() && !KDUMP_DUMPFILE())
+ if (!DISKDUMP_DUMPFILE() && !KDUMP_DUMPFILE() && !NETDUMP_DUMPFILE())
return FALSE;
panic_task_regs = calloc(kt->cpus, sizeof(*panic_task_regs));
@@ -1022,7 +1030,7 @@ loongarch64_get_elf_notes(void)
if (DISKDUMP_DUMPFILE())
note = diskdump_get_prstatus_percpu(i);
- else if (KDUMP_DUMPFILE())
+ else if (KDUMP_DUMPFILE() || NETDUMP_DUMPFILE())
note = netdump_get_prstatus_percpu(i);
if (!note) {
--
2.54.0

View File

@ -0,0 +1,31 @@
From a6817d475c753f44d10bd5709be1d455768d496a Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Thu, 4 Jun 2026 19:52:41 +0800
Subject: [PATCH 20/39] LoongArch64: Fix pt_regs initialization for active
tasks
The offset of pt_regs.regs was missing, which prevented crash from
falling back to real-time CPU registers for the active task. Initialize
it correctly during architecture initialization.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/loongarch64.c b/loongarch64.c
index 5adb7d8038db..92481f3235b6 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -761,6 +761,8 @@ loongarch64_stackframe_init(void)
ASSIGN_OFFSET(task_struct_thread_reg03) =
task_struct_thread + thread_reg03_sp;
+ MEMBER_OFFSET_INIT(pt_regs_regs, "pt_regs", "regs");
+ STRUCT_SIZE_INIT(pt_regs, "pt_regs");
ASSIGN_OFFSET(task_struct_thread_reg01) =
task_struct_thread + thread_reg01_ra;
--
2.54.0

View File

@ -0,0 +1,63 @@
From f3a02a49653b179a43b07a0befb82934af8ac542 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Thu, 4 Jun 2026 19:52:42 +0800
Subject: [PATCH 21/39] LoongArch64: Support backtracing across exception
boundaries
LoongArch exception and syscall entry code saves pt_regs on the kernel
stack before calling the C handlers. When a backtrace reaches one of
these entry symbols, crash needs to recover the saved pt_regs and
continue unwinding from the interrupted context.
Recognize the LoongArch entry symbols that leave a saved pt_regs at sp,
including syscall, exception, interrupt-vector and TLB page-fault paths
used by current upstream kernels and existing Loongnix variants. This
prevents backtraces from stopping at the first entry frame and allows
crash to print the saved pt_regs context without treating ordinary
handler call frames as exception frames.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/loongarch64.c b/loongarch64.c
index 92481f3235b6..3ec184c908f8 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -707,7 +707,31 @@ loongarch64_is_exception_entry(struct syment *sym)
return STREQ(sym->name, "ret_from_exception") ||
STREQ(sym->name, "ret_from_irq") ||
STREQ(sym->name, "work_resched") ||
- STREQ(sym->name, "handle_sys");
+ STREQ(sym->name, "handle_sys") ||
+ STREQ(sym->name, "handle_syscall") ||
+ STREQ(sym->name, "handle_ade") ||
+ STREQ(sym->name, "handle_ale") ||
+ STREQ(sym->name, "handle_bce") ||
+ STREQ(sym->name, "handle_bp") ||
+ STREQ(sym->name, "handle_fpe") ||
+ STREQ(sym->name, "handle_fpu") ||
+ STREQ(sym->name, "handle_iasub") ||
+ STREQ(sym->name, "handle_ib") ||
+ STREQ(sym->name, "handle_int") ||
+ STREQ(sym->name, "handle_ipe") ||
+ STREQ(sym->name, "handle_lbt") ||
+ STREQ(sym->name, "handle_lsx") ||
+ STREQ(sym->name, "handle_mcheck") ||
+ STREQ(sym->name, "handle_oac") ||
+ STREQ(sym->name, "handle_parchk") ||
+ STREQ(sym->name, "handle_reserved") ||
+ STREQ(sym->name, "handle_ri") ||
+ STREQ(sym->name, "handle_tlb_protect") ||
+ STREQ(sym->name, "tlb_do_page_fault_0") ||
+ STREQ(sym->name, "tlb_do_page_fault_1") ||
+ STREQ(sym->name, "handle_vint") ||
+ STREQ(sym->name, "handle_watch") ||
+ STREQ(sym->name, "handle_lasx");
}
/*
--
2.54.0

View File

@ -0,0 +1,41 @@
From 40145cc1f001e7eedff7753377fb8e4b9e8787f7 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Thu, 4 Jun 2026 19:52:43 +0800
Subject: [PATCH 22/39] LoongArch64: Fix stack frame loop bounds for exception
frames
Remove the incorrect -32 padding from stacktop limits in loongarch64.c.
In modern LoongArch64 kernels, pt_regs rests exactly at the top of the
stack, and this -32 padding forces the backtrace loop to terminate
prematurely before reaching the exception frame.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/loongarch64.c b/loongarch64.c
index 3ec184c908f8..9980b35bdff1 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -461,7 +461,7 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
previous.pc = current.ra = regs->regs[LOONGARCH64_EF_RA];
}
- while (current.sp <= bt->stacktop - 32 - SIZE(pt_regs)) {
+ while (current.sp <= bt->stacktop - SIZE(pt_regs)) {
struct syment *symbol = NULL;
ulong offset;
@@ -501,7 +501,7 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
* * ret_from_kernel_thread
*/
if (symbol && !STRNEQ(symbol->name, "ret_from") && !offset &&
- !current.ra && current.sp < bt->stacktop - 32 - SIZE(pt_regs)) {
+ !current.ra && current.sp < bt->stacktop - SIZE(pt_regs)) {
if (CRASHDEBUG(8))
fprintf(fp, "zero offset at %s, try previous symbol\n",
symbol->name);
--
2.54.0

View File

@ -0,0 +1,52 @@
From 523981942d8962b5246efa5c92f52abfb0f62e86 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Thu, 4 Jun 2026 19:52:44 +0800
Subject: [PATCH 23/39] LoongArch64: Add dummy eframe_search to avoid bt -e
segfault
The lack of machdep->eframe_search initialization causes the crash tool
to segment fault when executing `bt -e`. Provide a stub function that
returns a fatal error indicating the option is not supported, matching
the behavior of other architectures that lack this feature.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/loongarch64.c b/loongarch64.c
index 9980b35bdff1..b0ebf8b1d99e 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -64,6 +64,7 @@ static void loongarch64_dump_backtrace_entry(struct bt_info *bt,
struct loongarch64_unwind_frame *previous, int level);
static void loongarch64_dump_exception_stack(struct bt_info *bt, char *pt_regs);
static int loongarch64_is_exception_entry(struct syment *sym);
+static int loongarch64_eframe_search(struct bt_info *bt);
static void loongarch64_display_full_frame(struct bt_info *bt,
struct loongarch64_unwind_frame *current,
struct loongarch64_unwind_frame *previous);
@@ -1267,6 +1268,7 @@ loongarch64_init(int when)
machdep->kvtop = loongarch64_kvtop;
machdep->cmd_mach = loongarch64_cmd_mach;
machdep->back_trace = loongarch64_back_trace_cmd;
+ machdep->eframe_search = loongarch64_eframe_search;
machdep->get_stack_frame = loongarch64_get_stack_frame;
machdep->vmalloc_start = loongarch64_vmalloc_start;
machdep->processor_speed = loongarch64_processor_speed;
@@ -1389,6 +1391,12 @@ loongarch64_display_regs_from_elf_notes(int cpu, FILE *ofp)
regs->csr_euen);
}
+static int
+loongarch64_eframe_search(struct bt_info *bt)
+{
+ return error(FATAL, "-e option not supported on this architecture\n");
+}
+
#else /* !LOONGARCH64 */
#include "defs.h"
--
2.54.0

View File

@ -0,0 +1,49 @@
From bf279fbd474f1f0c8783e0a0c5589d1bc0d7a16b Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Tue, 16 Jun 2026 20:47:41 +0800
Subject: [PATCH 24/39] LoongArch64: print exception return address as ERA
LoongArch saves the exception return address in CSR.ERA. The crash
LoongArch64 code currently labels this field as EPC in register and
exception-frame output. That is misleading when comparing crash output
with the architecture documentation and the kernel pt_regs layout.
Rename the displayed field to ERA and fix the CSR.EUEN label while
updating the same output block. This is a display-only change and does
not alter how the saved exception return address is collected or used by
the unwinder.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/loongarch64.c b/loongarch64.c
index b0ebf8b1d99e..830c81826ebd 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -688,7 +688,7 @@ loongarch64_dump_exception_stack(struct bt_info *bt, char *pt_regs)
}
value_to_symstr(regs->csr_epc, buf, 16);
- fprintf(fp, " epc : %016lx %s\n", regs->csr_epc, buf);
+ fprintf(fp, " era : %016lx %s\n", regs->csr_epc, buf);
value_to_symstr(regs->regs[LOONGARCH64_EF_RA], buf, 16);
fprintf(fp, " ra : %016lx %s\n", regs->regs[LOONGARCH64_EF_RA], buf);
@@ -1346,10 +1346,10 @@ loongarch64_display_regs_from_elf_notes(int cpu, FILE *ofp)
" R24: %016lx R25: %016lx R26: %016lx\n"
" R27: %016lx R28: %016lx R29: %016lx\n"
" R30: %016lx R31: %016lx\n"
- " CSR epc : %016lx CSR badv: %016lx\n"
+ " CSR era : %016lx CSR badv: %016lx\n"
" CSR crmd: %08lx CSR prmd: %08lx\n"
" CSR ecfg: %08lx CSR estat: %08lx\n"
- " CSR eneu: %08lx",
+ " CSR euen: %08lx",
regs->regs[LOONGARCH64_EF_R0],
regs->regs[LOONGARCH64_EF_R0 + 1],
regs->regs[LOONGARCH64_EF_R0 + 2],
--
2.54.0

View File

@ -0,0 +1,152 @@
From 488777428c96d73964e659a3a6c229502f85d74d Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Tue, 16 Jun 2026 20:47:42 +0800
Subject: [PATCH 25/39] LoongArch64: resolve relocated exception vector
addresses
LoongArch copies exception entry code to the exception_handlers area and,
on SMP systems, may execute vectors from per-cpu pcpu_handlers. A PC
saved while running copied exception code therefore does not necessarily
match the canonical symbol address in vmlinux.
Crash currently looks up those runtime addresses directly. That can
leave frames such as handle_vint unresolved and prevents exception-frame
handling from identifying the interrupted context reliably.
Translate PCs in the relocated exception vector range back to their
canonical handler addresses before symbol lookup. Follow the kernel
layout by folding per-cpu handler addresses back to eentry, computing the
vector type and offset, mapping normal exceptions through
exception_table[], and mapping interrupt vectors to handle_vint.
The per-cpu handler mapping is optional and exists only for NUMA non-RT
kernels. When pcpu_handlers is absent, the normal eentry mapping still
covers the boot-time exception handler area.
Co-developed-by: Xianglai Li <lixianglai@loongson.cn>
Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 77 insertions(+), 1 deletion(-)
diff --git a/loongarch64.c b/loongarch64.c
index 830c81826ebd..0b108e062b2e 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -43,6 +43,11 @@ struct loongarch64_unwind_frame {
unsigned long ra;
};
+#define LOONGARCH64_VECSIZE 0x200
+#define LOONGARCH64_EXCEPTION_VECTOR_NUM 128
+#define LOONGARCH64_EXCCODE_INT_START 64
+#define LOONGARCH64_EXCCODE_INT_END 78
+
static int loongarch64_pgd_vtop(ulong *pgd, ulong vaddr,
physaddr_t *paddr, int verbose);
static int loongarch64_uvtop(struct task_context *tc, ulong vaddr,
@@ -64,6 +69,7 @@ static void loongarch64_dump_backtrace_entry(struct bt_info *bt,
struct loongarch64_unwind_frame *previous, int level);
static void loongarch64_dump_exception_stack(struct bt_info *bt, char *pt_regs);
static int loongarch64_is_exception_entry(struct syment *sym);
+static ulong loongarch64_exception_pc(int cpu, ulong pc);
static int loongarch64_eframe_search(struct bt_info *bt);
static void loongarch64_display_full_frame(struct bt_info *bt,
struct loongarch64_unwind_frame *current,
@@ -466,6 +472,9 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
struct syment *symbol = NULL;
ulong offset;
+ current.pc = loongarch64_exception_pc(bt->tc->processor,
+ current.pc);
+
if (CRASHDEBUG(8))
fprintf(fp, "level %d pc %#lx ra %#lx sp %lx\n",
level, current.pc, current.ra, current.sp);
@@ -687,7 +696,8 @@ loongarch64_dump_exception_stack(struct bt_info *bt, char *pt_regs)
regs->regs[i+2], regs->regs[i+3]);
}
- value_to_symstr(regs->csr_epc, buf, 16);
+ value_to_symstr(loongarch64_exception_pc(bt->tc->processor,
+ regs->csr_epc), buf, 16);
fprintf(fp, " era : %016lx %s\n", regs->csr_epc, buf);
value_to_symstr(regs->regs[LOONGARCH64_EF_RA], buf, 16);
@@ -735,6 +745,72 @@ loongarch64_is_exception_entry(struct syment *sym)
STREQ(sym->name, "handle_lasx");
}
+static ulong
+loongarch64_exception_pc(int cpu, ulong pc)
+{
+ ulong eentry, pcpu_handler, offset, type, func;
+ ulong exception_table, exception_handlers;
+ int i;
+
+ if (!IS_KVADDR(pc) || !symbol_exists("exception_handlers"))
+ return pc;
+
+ exception_handlers = symbol_value("exception_handlers");
+ eentry = exception_handlers;
+ if (symbol_exists("eentry"))
+ readmem(symbol_value("eentry"), KVADDR, &eentry, sizeof(eentry),
+ "LoongArch eentry", RETURN_ON_ERROR|QUIET);
+
+ /*
+ * pcpu_handlers exists only for NUMA non-RT kernels. When absent,
+ * the boot-time eentry/exception_handlers mapping below is still valid.
+ */
+ if (symbol_exists("pcpu_handlers")) {
+ for (i = 0; i < kt->cpus; i++) {
+ if (cpu >= 0 && cpu < kt->cpus && i != cpu)
+ continue;
+ if (!readmem(symbol_value("pcpu_handlers") +
+ (i * sizeof(ulong)), KVADDR, &pcpu_handler,
+ sizeof(pcpu_handler), "LoongArch pcpu_handlers",
+ RETURN_ON_ERROR|QUIET))
+ continue;
+ if (!pcpu_handler)
+ continue;
+ if (pc >= pcpu_handler &&
+ pc < pcpu_handler + LOONGARCH64_VECSIZE *
+ LOONGARCH64_EXCEPTION_VECTOR_NUM) {
+ pc = pc + eentry - pcpu_handler;
+ break;
+ }
+ }
+ }
+
+ if (pc < eentry || pc >= eentry + LOONGARCH64_EXCCODE_INT_END * LOONGARCH64_VECSIZE)
+ return pc;
+
+ offset = (pc - eentry) % LOONGARCH64_VECSIZE;
+ type = (pc - eentry) / LOONGARCH64_VECSIZE;
+
+ if (type < LOONGARCH64_EXCCODE_INT_START &&
+ symbol_exists("exception_table")) {
+ exception_table = symbol_value("exception_table");
+ if (!readmem(exception_table + (type * sizeof(ulong)), KVADDR,
+ &func, sizeof(func), "LoongArch exception_table",
+ RETURN_ON_ERROR|QUIET))
+ func = 0;
+ } else if (type >= LOONGARCH64_EXCCODE_INT_START &&
+ type <= LOONGARCH64_EXCCODE_INT_END &&
+ symbol_exists("handle_vint")) {
+ func = symbol_value("handle_vint");
+ } else if (symbol_exists("handle_reserved")) {
+ func = symbol_value("handle_reserved");
+ } else {
+ func = 0;
+ }
+
+ return func ? func + offset : pc;
+}
+
/*
* 'bt -f' commend output
* Display all stack data contained in a frame
--
2.54.0

View File

@ -0,0 +1,515 @@
From 9e8d1784b910db417c993e9de7ab2cbd0e9d4fc8 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Tue, 16 Jun 2026 20:47:43 +0800
Subject: [PATCH 26/39] LoongArch64: add initial ORC unwinder support
LoongArch kernels can provide ORC unwind tables. Without consuming those
tables, crash falls back to a small prologue scanner, which is fragile
around exception entries and functions whose frame layout does not match
the simple scanner.
Add initial support for reading the kernel ORC IP table, lookup table and
orc_entry records, and use them while unwinding LoongArch64 kernel text
frames. The unwinder handles CALL and REGS entries, carries
frame-pointer state, and normalizes relocated exception-vector PCs before
using them as unwind targets.
For CALL entries, convert the saved return address back to the call
instruction address before validation, matching the kernel unwinder
convention. This avoids looking up the instruction after the call as the
caller frame.
This intentionally covers core kernel text first. Module and ftrace ORC
handling can be added separately.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
defs.h | 25 ++++
loongarch64.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 378 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h
index a4f70b773cd7..b349872f49a4 100644
--- a/defs.h
+++ b/defs.h
@@ -7333,6 +7333,30 @@ void loongarch64_dump_machdep_table(ulong);
#define KSYMS_START (0x1)
+struct loongarch64_orc_entry {
+ short sp_offset;
+ short fp_offset;
+ short ra_offset;
+ uint sp_reg;
+ uint fp_reg;
+ uint ra_reg;
+ uint type;
+ uint signal;
+};
+
+struct loongarch64_ORC_data {
+ int enabled;
+ uint lookup_num_blocks;
+ ulong __start_orc_unwind_ip;
+ ulong __stop_orc_unwind_ip;
+ ulong __start_orc_unwind;
+ ulong __stop_orc_unwind;
+ ulong orc_lookup;
+ ulong ip_entry;
+ ulong orc_entry;
+ struct loongarch64_orc_entry orc_entry_data;
+};
+
struct machine_specific {
ulong phys_base;
ulong vmalloc_start_addr;
@@ -7340,6 +7364,7 @@ struct machine_specific {
ulong modules_end;
struct loongarch64_pt_regs *crash_task_regs;
+ struct loongarch64_ORC_data orc;
};
/*
diff --git a/loongarch64.c b/loongarch64.c
index 0b108e062b2e..2e9e28602b9b 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -41,8 +41,33 @@ struct loongarch64_unwind_frame {
unsigned long sp;
unsigned long pc;
unsigned long ra;
+ unsigned long fp;
};
+typedef struct __attribute__((__packed__)) {
+ short sp_offset;
+ short fp_offset;
+ short ra_offset;
+ unsigned int sp_reg:4;
+ unsigned int fp_reg:4;
+ unsigned int ra_reg:4;
+ unsigned int type:3;
+ unsigned int signal:1;
+} loongarch64_kernel_orc_entry;
+
+#define LOONGARCH64_ORC_REG_UNDEFINED 0
+#define LOONGARCH64_ORC_REG_PREV_SP 1
+#define LOONGARCH64_ORC_REG_SP 2
+#define LOONGARCH64_ORC_REG_FP 3
+
+#define LOONGARCH64_ORC_TYPE_UNDEFINED 0
+#define LOONGARCH64_ORC_TYPE_END_OF_STACK 1
+#define LOONGARCH64_ORC_TYPE_CALL 2
+#define LOONGARCH64_ORC_TYPE_REGS 3
+
+#define LOONGARCH64_LOOKUP_BLOCK_ORDER 8
+#define LOONGARCH64_LOOKUP_BLOCK_SIZE (1 << LOONGARCH64_LOOKUP_BLOCK_ORDER)
+#define LOONGARCH64_INSN_SIZE 4
#define LOONGARCH64_VECSIZE 0x200
#define LOONGARCH64_EXCEPTION_VECTOR_NUM 128
#define LOONGARCH64_EXCCODE_INT_START 64
@@ -70,6 +95,7 @@ static void loongarch64_dump_backtrace_entry(struct bt_info *bt,
static void loongarch64_dump_exception_stack(struct bt_info *bt, char *pt_regs);
static int loongarch64_is_exception_entry(struct syment *sym);
static ulong loongarch64_exception_pc(int cpu, ulong pc);
+static int loongarch64_is_unwind_text(ulong pc);
static int loongarch64_eframe_search(struct bt_info *bt);
static void loongarch64_display_full_frame(struct bt_info *bt,
struct loongarch64_unwind_frame *current,
@@ -82,6 +108,10 @@ static int loongarch64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp);
static int loongarch64_init_active_task_regs(void);
static int loongarch64_get_crash_notes(void);
static int loongarch64_get_elf_notes(void);
+static void loongarch64_ORC_init(void);
+static int loongarch64_orc_unwind(struct bt_info *bt,
+ struct loongarch64_unwind_frame *current,
+ struct loongarch64_unwind_frame *previous);
/*
* 3 Levels paging PAGE_SIZE=16KB
@@ -454,11 +484,12 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
if (bt->flags & BT_REGS_NOT_FOUND)
return;
- previous.sp = previous.pc = previous.ra = 0;
+ previous.sp = previous.pc = previous.ra = previous.fp = 0;
current.pc = bt->instptr;
current.sp = bt->stkptr;
current.ra = 0;
+ current.fp = 0;
if (!INSTACK(current.sp, bt))
return;
@@ -466,6 +497,7 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
if (bt->machdep) {
regs = (struct loongarch64_pt_regs *)bt->machdep;
previous.pc = current.ra = regs->regs[LOONGARCH64_EF_RA];
+ current.fp = regs->regs[LOONGARCH64_EF_FP];
}
while (current.sp <= bt->stacktop - SIZE(pt_regs)) {
@@ -523,12 +555,15 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
}
}
- if (symbol && loongarch64_is_exception_entry(symbol)) {
+ if (loongarch64_orc_unwind(bt, &current, &previous)) {
+ /* ORC has already calculated the caller frame. */
+ } else if (symbol && loongarch64_is_exception_entry(symbol)) {
GET_STACK_DATA(current.sp, pt_regs, sizeof(pt_regs));
regs = (struct loongarch64_pt_regs *) (pt_regs + OFFSET(pt_regs_regs));
previous.ra = regs->regs[LOONGARCH64_EF_RA];
previous.sp = regs->regs[LOONGARCH64_EF_SP];
+ previous.fp = regs->regs[LOONGARCH64_EF_FP];
current.ra = regs->csr_epc;
if (CRASHDEBUG(8))
@@ -554,12 +589,13 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
current.pc = current.ra;
current.sp = previous.sp;
current.ra = previous.ra;
+ current.fp = previous.fp;
if (CRASHDEBUG(8))
fprintf(fp, "next %d pc %#lx ra %#lx sp %lx\n",
level, current.pc, current.ra, current.sp);
- previous.sp = previous.pc = previous.ra = 0;
+ previous.sp = previous.pc = previous.ra = previous.fp = 0;
}
}
@@ -811,6 +847,319 @@ loongarch64_exception_pc(int cpu, ulong pc)
return func ? func + offset : pc;
}
+
+static int
+loongarch64_is_unwind_text(ulong pc)
+{
+ struct syment *symbol;
+ ulong offset;
+
+ if (!is_kernel_text(pc))
+ return FALSE;
+
+ symbol = value_search(pc, &offset);
+ if (!symbol || symbol->name[0] == '.' ||
+ STREQ(symbol->name, "_PROCEDURE_LINKAGE_TABLE_") ||
+ STREQ(symbol->name, "empty_zero_page") ||
+ STRNEQ(symbol->name, "__start_") ||
+ STRNEQ(symbol->name, "__end_"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+loongarch64_orc_ip(ulong ip_entry_addr, ulong *ip)
+{
+ int ip_entry;
+
+ if (!readmem(ip_entry_addr, KVADDR, &ip_entry, sizeof(ip_entry),
+ "LoongArch ORC ip", RETURN_ON_ERROR|QUIET))
+ return FALSE;
+
+ *ip = ip_entry_addr + ip_entry;
+ return TRUE;
+}
+
+static struct loongarch64_orc_entry *
+loongarch64_orc_get_entry(struct loongarch64_ORC_data *orc)
+{
+ loongarch64_kernel_orc_entry korc;
+ struct loongarch64_orc_entry *entry = &orc->orc_entry_data;
+
+ if (!readmem(orc->orc_entry, KVADDR, &korc, sizeof(korc),
+ "LoongArch ORC entry", RETURN_ON_ERROR|QUIET))
+ return NULL;
+
+ entry->sp_offset = korc.sp_offset;
+ entry->fp_offset = korc.fp_offset;
+ entry->ra_offset = korc.ra_offset;
+ entry->sp_reg = korc.sp_reg;
+ entry->fp_reg = korc.fp_reg;
+ entry->ra_reg = korc.ra_reg;
+ entry->type = korc.type;
+ entry->signal = korc.signal;
+
+ return entry;
+}
+
+static struct loongarch64_orc_entry *
+loongarch64_orc_find_in_table(ulong ip_table, ulong orc_table,
+ uint num_entries, ulong ip)
+{
+ int index;
+ ulong first, last, mid, found, vaddr;
+ struct machine_specific *ms = machdep->machspec;
+ struct loongarch64_ORC_data *orc = &ms->orc;
+
+ if (!num_entries)
+ return NULL;
+
+ first = ip_table;
+ last = ip_table + ((num_entries - 1) * sizeof(int));
+ found = first;
+
+ while (first <= last) {
+ mid = first + (((last - first) / sizeof(int)) / 2) *
+ sizeof(int);
+
+ if (!loongarch64_orc_ip(mid, &vaddr))
+ return NULL;
+
+ if (vaddr <= ip) {
+ found = mid;
+ first = mid + sizeof(int);
+ } else {
+ if (mid == ip_table)
+ break;
+ last = mid - sizeof(int);
+ }
+ }
+
+ index = (found - ip_table) / sizeof(int);
+ orc->ip_entry = found;
+ orc->orc_entry = orc_table + (index * SIZE(orc_entry));
+
+ return loongarch64_orc_get_entry(orc);
+}
+
+static struct loongarch64_orc_entry *
+loongarch64_orc_find(ulong ip)
+{
+ uint idx, start, stop, num_entries;
+ struct machine_specific *ms = machdep->machspec;
+ struct loongarch64_ORC_data *orc = &ms->orc;
+
+ if (!orc->enabled)
+ return NULL;
+
+ if ((ip >= kt->stext) && (ip < kt->etext)) {
+ if (orc->lookup_num_blocks < 2)
+ return NULL;
+
+ idx = (ip - kt->stext) / LOONGARCH64_LOOKUP_BLOCK_SIZE;
+ if (idx >= orc->lookup_num_blocks - 1)
+ return NULL;
+
+ if (!readmem(orc->orc_lookup + (idx * sizeof(uint)), KVADDR,
+ &start, sizeof(start), "LoongArch ORC lookup start",
+ RETURN_ON_ERROR|QUIET))
+ return NULL;
+ if (!readmem(orc->orc_lookup + ((idx + 1) * sizeof(uint)),
+ KVADDR, &stop, sizeof(stop), "LoongArch ORC lookup stop",
+ RETURN_ON_ERROR|QUIET))
+ return NULL;
+ stop++;
+
+ if ((orc->__start_orc_unwind + (start * SIZE(orc_entry))) >=
+ orc->__stop_orc_unwind)
+ return NULL;
+ if ((orc->__start_orc_unwind + (stop * SIZE(orc_entry))) >
+ orc->__stop_orc_unwind)
+ return NULL;
+
+ return loongarch64_orc_find_in_table(
+ orc->__start_orc_unwind_ip + (start * sizeof(int)),
+ orc->__start_orc_unwind + (start * SIZE(orc_entry)),
+ stop - start, ip);
+ }
+
+ if (is_kernel_text(ip)) {
+ num_entries = (orc->__stop_orc_unwind_ip -
+ orc->__start_orc_unwind_ip) / sizeof(int);
+ return loongarch64_orc_find_in_table(orc->__start_orc_unwind_ip,
+ orc->__start_orc_unwind, num_entries, ip);
+ }
+
+ return NULL;
+}
+
+static int
+loongarch64_orc_read_stack(ulong addr, ulong *value)
+{
+ return readmem(addr, KVADDR, value, sizeof(*value),
+ "LoongArch ORC stack", RETURN_ON_ERROR|QUIET);
+}
+
+static ulong
+loongarch64_orc_adjust_pc(int cpu, ulong ra)
+{
+ ra = loongarch64_exception_pc(cpu, ra);
+ return loongarch64_is_unwind_text(ra) ? ra : 0;
+}
+
+static int
+loongarch64_orc_unwind(struct bt_info *bt,
+ struct loongarch64_unwind_frame *current,
+ struct loongarch64_unwind_frame *previous)
+{
+ ulong cfa, pcval, fpval = current->fp;
+ struct loongarch64_orc_entry *orc;
+ struct loongarch64_pt_regs regs;
+
+ orc = loongarch64_orc_find(current->pc);
+ if (!orc)
+ return FALSE;
+
+ if (CRASHDEBUG(8))
+ fprintf(fp,
+ "orc pc %lx sp %lx fp %lx -> spo %d fpo %d rao %d "
+ "spr %u fpr %u rar %u type %u\n",
+ current->pc, current->sp, current->fp, orc->sp_offset,
+ orc->fp_offset, orc->ra_offset, orc->sp_reg, orc->fp_reg,
+ orc->ra_reg, orc->type);
+
+ if (orc->type == LOONGARCH64_ORC_TYPE_UNDEFINED ||
+ orc->type == LOONGARCH64_ORC_TYPE_END_OF_STACK)
+ return FALSE;
+
+ switch (orc->sp_reg) {
+ case LOONGARCH64_ORC_REG_SP:
+ cfa = current->sp + orc->sp_offset;
+ break;
+ case LOONGARCH64_ORC_REG_FP:
+ if (!current->fp)
+ return FALSE;
+ cfa = current->fp;
+ break;
+ default:
+ return FALSE;
+ }
+
+ switch (orc->fp_reg) {
+ case LOONGARCH64_ORC_REG_PREV_SP:
+ if (!loongarch64_orc_read_stack(cfa + orc->fp_offset, &fpval))
+ return FALSE;
+ break;
+ case LOONGARCH64_ORC_REG_UNDEFINED:
+ break;
+ default:
+ return FALSE;
+ }
+
+ switch (orc->type) {
+ case LOONGARCH64_ORC_TYPE_CALL:
+ if (orc->ra_reg == LOONGARCH64_ORC_REG_PREV_SP) {
+ if (!loongarch64_orc_read_stack(cfa + orc->ra_offset,
+ &pcval))
+ return FALSE;
+ } else if (orc->ra_reg == LOONGARCH64_ORC_REG_UNDEFINED) {
+ if (!current->ra || current->ra == current->pc)
+ return FALSE;
+ pcval = current->ra;
+ } else {
+ return FALSE;
+ }
+ if (pcval >= LOONGARCH64_INSN_SIZE)
+ pcval -= LOONGARCH64_INSN_SIZE;
+ pcval = loongarch64_orc_adjust_pc(bt->tc->processor, pcval);
+ if (!pcval && current->ra >= LOONGARCH64_INSN_SIZE)
+ pcval = loongarch64_orc_adjust_pc(bt->tc->processor,
+ current->ra - LOONGARCH64_INSN_SIZE);
+ if (!pcval)
+ return FALSE;
+ previous->sp = cfa;
+ previous->fp = fpval;
+ previous->ra = 0;
+ current->ra = pcval;
+ return TRUE;
+
+ case LOONGARCH64_ORC_TYPE_REGS:
+ if (!readmem(cfa, KVADDR, &regs, sizeof(regs),
+ "LoongArch ORC pt_regs", RETURN_ON_ERROR|QUIET))
+ return FALSE;
+ /*
+ * The kernel unwinder treats user-mode or empty pt_regs as a
+ * clean end. Crash does not need to distinguish that from a
+ * fallback here; a non-kernel ERA simply stops ORC unwinding.
+ */
+ pcval = loongarch64_exception_pc(bt->tc->processor, regs.csr_epc);
+ if (!loongarch64_is_unwind_text(pcval))
+ return FALSE;
+ previous->sp = regs.regs[LOONGARCH64_EF_SP];
+ previous->fp = regs.regs[LOONGARCH64_EF_FP];
+ previous->ra = regs.regs[LOONGARCH64_EF_RA];
+ current->ra = pcval;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+loongarch64_ORC_init(void)
+{
+ int i;
+ char *orc_symbols[] = {
+ "lookup_num_blocks",
+ "__start_orc_unwind_ip",
+ "__stop_orc_unwind_ip",
+ "__start_orc_unwind",
+ "__stop_orc_unwind",
+ "orc_lookup",
+ NULL
+ };
+ struct machine_specific *ms = machdep->machspec;
+ struct loongarch64_ORC_data *orc = &ms->orc;
+
+ STRUCT_SIZE_INIT(orc_entry, "orc_entry");
+ if (!VALID_STRUCT(orc_entry) ||
+ SIZE(orc_entry) != sizeof(loongarch64_kernel_orc_entry)) {
+ error(WARNING, "LoongArch64 ORC unwinder: "
+ "orc_entry structure has changed\n");
+ return;
+ }
+
+ if (!MEMBER_EXISTS("orc_entry", "sp_offset") ||
+ !MEMBER_EXISTS("orc_entry", "fp_offset") ||
+ !MEMBER_EXISTS("orc_entry", "ra_offset") ||
+ !MEMBER_EXISTS("orc_entry", "sp_reg") ||
+ !MEMBER_EXISTS("orc_entry", "fp_reg") ||
+ !MEMBER_EXISTS("orc_entry", "ra_reg") ||
+ !MEMBER_EXISTS("orc_entry", "type")) {
+ error(WARNING, "LoongArch64 ORC unwinder: "
+ "orc_entry members have changed\n");
+ return;
+ }
+
+ for (i = 0; orc_symbols[i]; i++) {
+ if (!symbol_exists(orc_symbols[i]))
+ return;
+ }
+
+ if (!readmem(symbol_value("lookup_num_blocks"), KVADDR,
+ &orc->lookup_num_blocks, sizeof(orc->lookup_num_blocks),
+ "LoongArch ORC lookup_num_blocks", RETURN_ON_ERROR|QUIET))
+ return;
+
+ orc->__start_orc_unwind_ip = symbol_value("__start_orc_unwind_ip");
+ orc->__stop_orc_unwind_ip = symbol_value("__stop_orc_unwind_ip");
+ orc->__start_orc_unwind = symbol_value("__start_orc_unwind");
+ orc->__stop_orc_unwind = symbol_value("__stop_orc_unwind");
+ orc->orc_lookup = symbol_value("orc_lookup");
+ orc->enabled = TRUE;
+}
+
/*
* 'bt -f' commend output
* Display all stack data contained in a frame
@@ -1374,6 +1723,7 @@ loongarch64_init(int when)
&machdep->nr_irqs);
loongarch64_stackframe_init();
+ loongarch64_ORC_init();
if (!machdep->hz)
machdep->hz = 250;
--
2.54.0

View File

@ -0,0 +1,364 @@
From a436361b2798534ea722ac696a4eeaa35d80ef33 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Tue, 16 Jun 2026 20:47:44 +0800
Subject: [PATCH 27/39] LoongArch64: unwind dumpfile active tasks from IRQ
stacks
For dumpfile active tasks, the saved SP may point into the per-cpu IRQ
stack instead of the task stack. The existing LoongArch64 unwinder only
uses the task stack buffer, so it can stop early or pick stale task-stack
words as caller frames.
Initialize the per-cpu IRQ stack addresses, switch the bt stack buffer
when an active task starts on an IRQ stack, and continue unwinding back to
the interrupted task stack using the task SP saved at the end of the IRQ
stack. This saved-SP slot matches the kernel IRQ_STACK_START layout.
When that saved task SP points to pt_regs, validate the saved ERA, SP and
RA before using them as the handoff frame.
Hypervisor dump PRSTATUS RA values from IRQ stack contexts are not always
a reliable caller. Keep them only for the narrow IRQ leaf case that
preserves the in-flight generic_handle_domain_irq -> handle_cpu_irq
chain; otherwise let ORC, exception frames, or the saved pt_regs handoff
determine the next frame.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
defs.h | 2 +
loongarch64.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 223 insertions(+), 5 deletions(-)
diff --git a/defs.h b/defs.h
index b349872f49a4..bdb4eb3e0980 100644
--- a/defs.h
+++ b/defs.h
@@ -7363,6 +7363,8 @@ struct machine_specific {
ulong modules_vaddr;
ulong modules_end;
+ ulong irq_stack_size;
+ ulong *irq_stacks;
struct loongarch64_pt_regs *crash_task_regs;
struct loongarch64_ORC_data orc;
};
diff --git a/loongarch64.c b/loongarch64.c
index 2e9e28602b9b..c25a3bbf6d6c 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -70,6 +70,7 @@ typedef struct __attribute__((__packed__)) {
#define LOONGARCH64_INSN_SIZE 4
#define LOONGARCH64_VECSIZE 0x200
#define LOONGARCH64_EXCEPTION_VECTOR_NUM 128
+#define LOONGARCH64_IRQ_STACK_SAVED_SP_OFFSET 16
#define LOONGARCH64_EXCCODE_INT_START 64
#define LOONGARCH64_EXCCODE_INT_END 78
@@ -96,6 +97,9 @@ static void loongarch64_dump_exception_stack(struct bt_info *bt, char *pt_regs);
static int loongarch64_is_exception_entry(struct syment *sym);
static ulong loongarch64_exception_pc(int cpu, ulong pc);
static int loongarch64_is_unwind_text(ulong pc);
+static int loongarch64_use_irq_prstatus_ra(ulong pc, ulong ra);
+static int loongarch64_valid_saved_regs(struct bt_info *bt,
+ struct loongarch64_pt_regs *regs, ulong *pc);
static int loongarch64_eframe_search(struct bt_info *bt);
static void loongarch64_display_full_frame(struct bt_info *bt,
struct loongarch64_unwind_frame *current,
@@ -108,10 +112,17 @@ static int loongarch64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp);
static int loongarch64_init_active_task_regs(void);
static int loongarch64_get_crash_notes(void);
static int loongarch64_get_elf_notes(void);
+static void loongarch64_irq_stack_init(void);
static void loongarch64_ORC_init(void);
static int loongarch64_orc_unwind(struct bt_info *bt,
struct loongarch64_unwind_frame *current,
struct loongarch64_unwind_frame *previous);
+static int loongarch64_on_irq_stack(int cpu, ulong stkptr);
+static void loongarch64_set_irq_stack(struct bt_info *bt);
+static int loongarch64_switch_from_irq_stack(struct bt_info *bt,
+ struct loongarch64_unwind_frame *current);
+static int loongarch64_find_next_kernel_text(struct bt_info *bt,
+ struct loongarch64_unwind_frame *current);
/*
* 3 Levels paging PAGE_SIZE=16KB
@@ -480,6 +491,7 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
char pt_regs[SIZE(pt_regs)];
int level = 0;
int invalid_ok = 1;
+ int on_irq_stack;
if (bt->flags & BT_REGS_NOT_FOUND)
return;
@@ -491,16 +503,30 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
current.ra = 0;
current.fp = 0;
+ if (!INSTACK(current.sp, bt) &&
+ (bt->flags & BT_REGS_NOT_FOUND) == 0 &&
+ loongarch64_on_irq_stack(bt->tc->processor, current.sp))
+ loongarch64_set_irq_stack(bt);
+
if (!INSTACK(current.sp, bt))
return;
+ on_irq_stack = loongarch64_on_irq_stack(bt->tc->processor, current.sp);
if (bt->machdep) {
regs = (struct loongarch64_pt_regs *)bt->machdep;
- previous.pc = current.ra = regs->regs[LOONGARCH64_EF_RA];
+ /*
+ * PRSTATUS RA from IRQ stacks is not always a reliable caller for
+ * hypervisor dumps. Use it only for IRQ leaf frames where it keeps
+ * the in-flight interrupt call chain before crossing stacks.
+ */
+ if (!on_irq_stack ||
+ loongarch64_use_irq_prstatus_ra(current.pc,
+ regs->regs[LOONGARCH64_EF_RA]))
+ previous.pc = current.ra = regs->regs[LOONGARCH64_EF_RA];
current.fp = regs->regs[LOONGARCH64_EF_FP];
}
- while (current.sp <= bt->stacktop - SIZE(pt_regs)) {
+ while (current.sp < bt->stacktop) {
struct syment *symbol = NULL;
ulong offset;
@@ -511,14 +537,28 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
fprintf(fp, "level %d pc %#lx ra %#lx sp %lx\n",
level, current.pc, current.ra, current.sp);
- if (!IS_KVADDR(current.pc) && !invalid_ok)
+ if (!IS_KVADDR(current.pc) && !invalid_ok) {
+ if (loongarch64_switch_from_irq_stack(bt, &current) ||
+ loongarch64_find_next_kernel_text(bt, &current)) {
+ invalid_ok = 1;
+ continue;
+ }
return;
+ }
symbol = value_search(current.pc, &offset);
- if (!symbol && !invalid_ok) {
+ if ((!symbol || !loongarch64_is_unwind_text(current.pc)) &&
+ !invalid_ok) {
+ if (loongarch64_switch_from_irq_stack(bt, &current) ||
+ loongarch64_find_next_kernel_text(bt, &current)) {
+ invalid_ok = 1;
+ continue;
+ }
error(FATAL, "PC is unknown symbol (%lx)", current.pc);
return;
}
+ if (symbol && !loongarch64_is_unwind_text(current.pc))
+ symbol = NULL;
invalid_ok = 0;
/*
@@ -557,7 +597,8 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
if (loongarch64_orc_unwind(bt, &current, &previous)) {
/* ORC has already calculated the caller frame. */
- } else if (symbol && loongarch64_is_exception_entry(symbol)) {
+ } else if (symbol && loongarch64_is_exception_entry(symbol) &&
+ current.sp <= bt->stacktop - SIZE(pt_regs)) {
GET_STACK_DATA(current.sp, pt_regs, sizeof(pt_regs));
regs = (struct loongarch64_pt_regs *) (pt_regs + OFFSET(pt_regs_regs));
@@ -596,6 +637,12 @@ loongarch64_back_trace_cmd(struct bt_info *bt)
level, current.pc, current.ra, current.sp);
previous.sp = previous.pc = previous.ra = previous.fp = 0;
+
+ if (current.sp >= bt->stacktop &&
+ loongarch64_switch_from_irq_stack(bt, &current)) {
+ invalid_ok = 1;
+ continue;
+ }
}
}
@@ -868,6 +915,174 @@ loongarch64_is_unwind_text(ulong pc)
return TRUE;
}
+
+static int
+loongarch64_use_irq_prstatus_ra(ulong pc, ulong ra)
+{
+ struct syment *pcsym, *rasym;
+ ulong offset;
+
+ if (!loongarch64_is_unwind_text(pc) ||
+ !loongarch64_is_unwind_text(ra))
+ return FALSE;
+
+ pcsym = value_search(pc, &offset);
+ rasym = value_search(ra, &offset);
+ if (!pcsym || !rasym)
+ return FALSE;
+
+ return STREQ(pcsym->name, "generic_handle_domain_irq") &&
+ STREQ(rasym->name, "handle_cpu_irq");
+}
+
+static int
+loongarch64_valid_saved_regs(struct bt_info *bt,
+ struct loongarch64_pt_regs *regs, ulong *pc)
+{
+ ulong saved_pc;
+
+ saved_pc = loongarch64_exception_pc(bt->tc->processor, regs->csr_epc);
+ if (!loongarch64_is_unwind_text(saved_pc))
+ return FALSE;
+
+ if (!INSTACK(regs->regs[LOONGARCH64_EF_SP], bt))
+ return FALSE;
+
+ if (regs->regs[LOONGARCH64_EF_RA] &&
+ !loongarch64_is_unwind_text(regs->regs[LOONGARCH64_EF_RA]))
+ return FALSE;
+
+ *pc = saved_pc;
+ return TRUE;
+}
+
+static void
+loongarch64_irq_stack_init(void)
+{
+ int i;
+ struct syment *sp;
+ struct machine_specific *ms = machdep->machspec;
+ ulong p;
+
+ if (!(symbol_exists("irq_stack") &&
+ (sp = per_cpu_symbol_search("irq_stack"))))
+ return;
+
+ ms->irq_stack_size = machdep->stacksize;
+ if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus *
+ sizeof(ulong)))))
+ error(FATAL, "cannot malloc irq_stack addresses\n");
+
+ machdep->flags |= IRQSTACKS;
+
+ for (i = 0; i < kt->cpus; i++) {
+ p = kt->__per_cpu_offset[i] + sp->value;
+ if (!readmem(p, KVADDR, &ms->irq_stacks[i], sizeof(ulong),
+ "IRQ stack pointer", RETURN_ON_ERROR))
+ ms->irq_stacks[i] = 0;
+ }
+}
+
+static int
+loongarch64_on_irq_stack(int cpu, ulong stkptr)
+{
+ struct machine_specific *ms = machdep->machspec;
+
+ if (cpu < 0 || cpu >= kt->cpus || !ms->irq_stacks ||
+ !ms->irq_stacks[cpu] || !ms->irq_stack_size)
+ return FALSE;
+
+ return (stkptr >= ms->irq_stacks[cpu]) &&
+ (stkptr < (ms->irq_stacks[cpu] + ms->irq_stack_size));
+}
+
+static void
+loongarch64_set_irq_stack(struct bt_info *bt)
+{
+ struct machine_specific *ms = machdep->machspec;
+
+ bt->stackbase = ms->irq_stacks[bt->tc->processor];
+ bt->stacktop = bt->stackbase + ms->irq_stack_size;
+ alter_stackbuf(bt);
+}
+
+static int
+loongarch64_switch_from_irq_stack(struct bt_info *bt,
+ struct loongarch64_unwind_frame *current)
+{
+ struct machine_specific *ms = machdep->machspec;
+ struct loongarch64_pt_regs *regs;
+ char pt_regs[SIZE(pt_regs)];
+ ulong irq_stack, saved_sp_addr, saved_sp, saved_pc;
+ ulong task_stackbase, task_stacktop;
+
+ if (bt->tc->processor < 0 || bt->tc->processor >= kt->cpus ||
+ !ms->irq_stacks || !ms->irq_stack_size)
+ return FALSE;
+
+ irq_stack = ms->irq_stacks[bt->tc->processor];
+ if (!irq_stack || current->sp < irq_stack ||
+ current->sp > irq_stack + ms->irq_stack_size + 64)
+ return FALSE;
+
+ /* Matches the kernel's IRQ_STACK_START layout. */
+ saved_sp_addr = irq_stack + ms->irq_stack_size -
+ LOONGARCH64_IRQ_STACK_SAVED_SP_OFFSET;
+ if (!readmem(saved_sp_addr, KVADDR, &saved_sp, sizeof(saved_sp),
+ "saved task stack pointer", RETURN_ON_ERROR))
+ return FALSE;
+
+ task_stackbase = GET_STACKBASE(bt->task);
+ task_stacktop = GET_STACKTOP(bt->task);
+ if (saved_sp < task_stackbase || saved_sp >= task_stacktop)
+ return FALSE;
+
+ bt->stackbase = task_stackbase;
+ bt->stacktop = task_stacktop;
+ alter_stackbuf(bt);
+ if (saved_sp <= bt->stacktop - SIZE(pt_regs)) {
+ GET_STACK_DATA(saved_sp, pt_regs, sizeof(pt_regs));
+ regs = (struct loongarch64_pt_regs *)(pt_regs +
+ OFFSET(pt_regs_regs));
+ if (loongarch64_valid_saved_regs(bt, regs, &saved_pc)) {
+ current->pc = saved_pc;
+ current->sp = regs->regs[LOONGARCH64_EF_SP];
+ current->ra = regs->regs[LOONGARCH64_EF_RA];
+ current->fp = regs->regs[LOONGARCH64_EF_FP];
+ return TRUE;
+ }
+ }
+
+ current->sp = saved_sp;
+ current->ra = 0;
+ return loongarch64_find_next_kernel_text(bt, current);
+}
+
+static int
+loongarch64_find_next_kernel_text(struct bt_info *bt,
+ struct loongarch64_unwind_frame *current)
+{
+ ulong sp, pc;
+
+ if (!INSTACK(current->sp, bt))
+ return FALSE;
+
+ sp = current->sp + sizeof(ulong);
+ sp = roundup(sp, sizeof(ulong));
+ for (; sp < bt->stacktop; sp += sizeof(ulong)) {
+ GET_STACK_DATA(sp, &pc, sizeof(pc));
+ pc = loongarch64_exception_pc(bt->tc->processor, pc);
+ if (!loongarch64_is_unwind_text(pc))
+ continue;
+
+ current->pc = pc;
+ current->sp = sp;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static int
loongarch64_orc_ip(ulong ip_entry_addr, ulong *ip)
{
@@ -1218,6 +1433,7 @@ loongarch64_stackframe_init(void)
MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus", "pr_reg");
STRUCT_SIZE_INIT(note_buf, "note_buf_t");
+ loongarch64_irq_stack_init();
}
/*
--
2.54.0

View File

@ -0,0 +1,51 @@
From a35fcc79393cbe84dbf00b663a3be705efb256e0 Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Tue, 16 Jun 2026 20:47:45 +0800
Subject: [PATCH 28/39] LoongArch64: avoid replacing a valid RA with stack
noise
The fallback prologue scanner may find a saved RA slot and read a word
from the stack. In corrupted, interrupted, or partially unwound frames,
that word is not guaranteed to be a kernel return address.
Do not blindly replace the current RA with the scanned stack value. Keep
the existing RA unless the scanned value is a kernel address, or unless
the current RA is already not a kernel address. This keeps the fallback
scanner from discarding a better caller candidate with obvious stack
noise.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/loongarch64.c b/loongarch64.c
index c25a3bbf6d6c..34e29c9f2d67 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -702,11 +702,17 @@ loongarch64_analyze_function(ulong start, ulong offset,
previous->sp = current->sp + spadjust;
- if (rapos && !readmem(rapos, KVADDR, &current->ra,
- sizeof(current->ra), "RA from stack",
- RETURN_ON_ERROR)) {
- error(FATAL, "Cannot read RA from stack %lx", rapos);
- return;
+ if (rapos) {
+ ulong ra;
+
+ if (!readmem(rapos, KVADDR, &ra, sizeof(ra), "RA from stack",
+ RETURN_ON_ERROR)) {
+ error(FATAL, "Cannot read RA from stack %lx", rapos);
+ return;
+ }
+
+ if (IS_KVADDR(ra) || !IS_KVADDR(current->ra))
+ current->ra = ra;
}
}
--
2.54.0

View File

@ -0,0 +1,43 @@
From 2946d9bfa656a59156c20641e743494c316b323c Mon Sep 17 00:00:00 2001
From: Ming Wang <wangming01@loongson.cn>
Date: Tue, 16 Jun 2026 20:47:46 +0800
Subject: [PATCH 29/39] LoongArch64: print exception registers only with bt -f
LoongArch64 exception frames include a full pt_regs dump. Printing that
register block in normal bt output makes the backtrace harder to scan and
visually separates the exception frame from the frames that follow it.
Keep normal bt output focused on stack frames and print the exception
register block only for full backtraces. The detailed data remains
available with bt -f, where crash already prints per-frame stack details.
Signed-off-by: Ming Wang <wangming01@loongson.cn>
---
loongarch64.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/loongarch64.c b/loongarch64.c
index 34e29c9f2d67..39a05b854832 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -755,13 +755,12 @@ loongarch64_dump_backtrace_entry(struct bt_info *bt, struct syment *sym,
fprintf(fp, " %s\n", buf);
}
- if (sym && loongarch64_is_exception_entry(sym)) {
- GET_STACK_DATA(current->sp, &pt_regs, SIZE(pt_regs));
- loongarch64_dump_exception_stack(bt, pt_regs);
- }
-
/* bt -f */
if (bt->flags & BT_FULL) {
+ if (sym && loongarch64_is_exception_entry(sym)) {
+ GET_STACK_DATA(current->sp, &pt_regs, SIZE(pt_regs));
+ loongarch64_dump_exception_stack(bt, pt_regs);
+ }
fprintf(fp, " "
"[PC: %016lx RA: %016lx SP: %016lx SIZE: %ld]\n",
current->pc, current->ra, current->sp,
--
2.54.0

View File

@ -0,0 +1,55 @@
From f6084e0dbf363d3d27075b3f3a193a9ae37c1df5 Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Mon, 22 Jun 2026 21:26:39 +0800
Subject: [PATCH 30/39] riscv64: Set VMEMMAP flag to fix spurious mem_map[]
warnings
RISC-V kernels use CONFIG_SPARSEMEM_VMEMMAP, and the crash utility
correctly reads VMEMMAP_START/VMEMMAP_END from VMCOREINFO. However,
riscv64.c never sets the VMEMMAP flag on machdep->flags, unlike arm64
and x86_64 which always set it.
This causes fill_mem_map_cache() in memory.c to emit spurious warnings
when it fails to read page structs from vmemmap holes:
WARNING: mem_map[] from ffff8d8002470000 to ffff8d8002471000 not accessible
With SPARSEMEM_VMEMMAP, unmapped holes in the vmemmap region are
expected -- a mem_section that has no corresponding physical memory
simply has no page struct mapped. The VMEMMAP flag tells crash that
such read failures are normal, suppressing the inappropriate warnings.
Set VMEMMAP in the PRE_GDB stage when vmemmap_vaddr is valid, and
display the flag in riscv64_dump_machdep_table() for debugging.
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
riscv64.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/riscv64.c b/riscv64.c
index f0783d570c7b..a43afbdcebd2 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -244,6 +244,8 @@ riscv64_dump_machdep_table(ulong arg)
fprintf(fp, "%sIRQ_STACKS", others++ ? "|" : "");
if (machdep->flags & OVERFLOW_STACKS)
fprintf(fp, "%sOVERFLOW_STACKS", others++ ? "|" : "");
+ if (machdep->flags & VMEMMAP)
+ fprintf(fp, "%sVMEMMAP", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
@@ -1749,6 +1751,9 @@ riscv64_init(int when)
machdep->show_interrupts = generic_show_interrupts;
machdep->get_irq_affinity = generic_get_irq_affinity;
machdep->init_kernel_pgd = NULL; /* pgd set by symbol_value("swapper_pg_dir") */
+
+ if (machdep->machspec->vmemmap_vaddr)
+ machdep->flags |= VMEMMAP;
break;
case POST_GDB:
--
2.54.0

View File

@ -0,0 +1,93 @@
From f5ce0c39b5b414727f8c2d7491714a80f8ff4396 Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Mon, 22 Jun 2026 21:26:40 +0800
Subject: [PATCH 31/39] riscv64: Guard verbose output in vtop page table walk
functions
The riscv64_vtop_{3,4,5}level_4k() functions print "PAGE: not present"
and "invalid for address" messages unconditionally, even when called
with verbose=0. This produces noisy output during internal address
translations such as kmem -p or dump_mem_map, where kvtop() is called
non-verbosely to check whether a page struct is mapped.
All other diagnostic output in these functions is already guarded by
"if (verbose)". Move the not-present and no_page messages inside
the verbose block to match.
Also fix the 5-level function which used printf() instead of
fprintf(fp, ...) for the !_PAGE_PRESENT debug message, and replace
it with the same verbose-guarded format used by the 3-level and
4-level functions.
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
riscv64.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/riscv64.c b/riscv64.c
index a43afbdcebd2..02210267862f 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -683,8 +683,8 @@ riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
if (verbose) {
fprintf(fp, "\n");
riscv64_translate_pte((ulong)pte_val, 0, 0);
+ fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
}
- fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
return FALSE;
}
@@ -697,7 +697,8 @@ riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
return TRUE;
no_page:
- fprintf(fp, "invalid for %lx address\n", vaddr);
+ if (verbose)
+ fprintf(fp, "invalid for %lx address\n", vaddr);
return FALSE;
}
@@ -1275,8 +1276,8 @@ riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
if (verbose) {
fprintf(fp, "\n");
riscv64_translate_pte((ulong)pte_val, 0, 0);
+ fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
}
- fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
return FALSE;
}
@@ -1289,7 +1290,8 @@ riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
return TRUE;
no_page:
- fprintf(fp, "invalid for %lx address\n", vaddr);
+ if (verbose)
+ fprintf(fp, "invalid for %lx address\n", vaddr);
return FALSE;
}
@@ -1364,8 +1366,8 @@ riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
if (verbose) {
fprintf(fp, "\n");
riscv64_translate_pte((ulong)pte_val, 0, 0);
+ fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
}
- printf("!_PAGE_PRESENT\n");
return FALSE;
}
@@ -1378,7 +1380,8 @@ riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
return TRUE;
no_page:
- fprintf(fp, "invalid for %lx address\n", vaddr);
+ if (verbose)
+ fprintf(fp, "invalid for %lx address\n", vaddr);
return FALSE;
}
--
2.54.0

View File

@ -0,0 +1,98 @@
From 18d6e9f4852f19fddcd17fe14b7dfadae92fbc70 Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Mon, 22 Jun 2026 21:26:41 +0800
Subject: [PATCH 32/39] riscv64: Add get_kvaddr_ranges callback for kernel
address ranges
Register an architecture-specific get_kvaddr_ranges callback so that
the search command can properly enumerate all kernel virtual address
regions including modules and vmemmap.
Without this callback, riscv64 falls back to generic_get_kvaddr_ranges()
which only knows about KVADDR_UNITY_MAP and KVADDR_VMALLOC, missing
the modules and vmemmap regions. While the generic VMALLOC range covers
these addresses (its end is -1), the type information is lost, making
it impossible for consumers to distinguish between different kernel
address space regions.
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
---
riscv64.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/riscv64.c b/riscv64.c
index 02210267862f..4dcecc2f6b2c 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -62,6 +62,7 @@ static void riscv64_set_process_stack(struct bt_info *);
static void riscv64_set_irq_stack(struct bt_info *);
static int riscv64_on_overflow_stack(int, ulong);
static void riscv64_set_overflow_stack(struct bt_info *);
+static int riscv64_get_kvaddr_ranges(struct vaddr_range *);
#define REG_FMT "%016lx"
#define SZ_2G 0x80000000
@@ -1757,6 +1758,7 @@ riscv64_init(int when)
if (machdep->machspec->vmemmap_vaddr)
machdep->flags |= VMEMMAP;
+ machdep->get_kvaddr_ranges = riscv64_get_kvaddr_ranges;
break;
case POST_GDB:
@@ -2018,6 +2020,52 @@ riscv64_eframe_search(struct bt_info *bt)
return count;
}
+static int
+compare_kvaddr(const void *v1, const void *v2)
+{
+ struct vaddr_range *r1, *r2;
+
+ r1 = (struct vaddr_range *)v1;
+ r2 = (struct vaddr_range *)v2;
+
+ return (r1->start < r2->start ? -1 :
+ r1->start == r2->start ? 0 : 1);
+}
+
+static int
+riscv64_get_kvaddr_ranges(struct vaddr_range *vrp)
+{
+ int cnt;
+
+ cnt = 0;
+
+ vrp[cnt].type = KVADDR_UNITY_MAP;
+ vrp[cnt].start = machdep->machspec->page_offset;
+ vrp[cnt++].end = vt->high_memory;
+
+ vrp[cnt].type = KVADDR_VMALLOC;
+ vrp[cnt].start = machdep->machspec->vmalloc_start_addr;
+ vrp[cnt++].end = last_vmalloc_address();
+
+ if (st->mods_installed) {
+ vrp[cnt].type = KVADDR_MODULES;
+ vrp[cnt].start = lowest_module_address();
+ vrp[cnt++].end = roundup(highest_module_address(),
+ PAGESIZE());
+ }
+
+ if (machdep->flags & VMEMMAP) {
+ vrp[cnt].type = KVADDR_VMEMMAP;
+ vrp[cnt].start = machdep->machspec->vmemmap_vaddr;
+ vrp[cnt++].end = vt->node_table[vt->numnodes-1].mem_map +
+ (vt->node_table[vt->numnodes-1].size * SIZE(page));
+ }
+
+ qsort(vrp, cnt, sizeof(struct vaddr_range), compare_kvaddr);
+
+ return cnt;
+}
+
#else /* !RISCV64 */
void
--
2.54.0

View File

@ -0,0 +1,184 @@
From 610e52023a425436d91168744dbb2ae047b63837 Mon Sep 17 00:00:00 2001
From: Aaron Tomlin <atomlin@atomlin.com>
Date: Sat, 4 Jul 2026 12:26:57 -0400
Subject: [PATCH 33/39] task: Introduce -I option to ps command to exclude idle
tasks
Adds a new option '-I' to the 'ps' command to filter out idle
threads/tasks from the output. This filters out the idle tasks from the
main ps list, CPU-specific block lists, and task time reports, while
enforcing mutual exclusivity with the summary (-S) option.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
defs.h | 1 +
help.c | 3 ++-
task.c | 32 ++++++++++++++++++++++++--------
3 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/defs.h b/defs.h
index bdb4eb3e0980..ed91a5bc8e91 100644
--- a/defs.h
+++ b/defs.h
@@ -5403,6 +5403,7 @@ extern long _ZOMBIE_;
#define PS_SUMMARY (0x40000)
#define PS_POLICY (0x80000)
#define PS_ACTIVE (0x100000)
+#define PS_EXCLUDE_IDLE (0x200000)
#define PS_EXCLUSIVE (PS_TGID_LIST|PS_ARGV_ENVP|PS_TIMES|PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN|PS_RLIMIT|PS_MSECS|PS_SUMMARY|PS_ACTIVE)
diff --git a/help.c b/help.c
index 5cd5c30e99a3..c88797e8ae70 100644
--- a/help.c
+++ b/help.c
@@ -1392,7 +1392,7 @@ NULL
char *help_ps[] = {
"ps",
"display process status information",
-"[-k|-u|-G|-y policy] [-s] [-p|-c|-t|-[l|m][-C cpu]|-a|-g|-r|-S|-A|-H]\n [pid | task | command] ...",
+"[-k|-u|-G|-I|-y policy] [-s] [-p|-c|-t|-[l|m][-C cpu]|-a|-g|-r|-S|-A|-H]\n [pid | task | command] ...",
" This command displays process status for selected, or all, processes" ,
" in the system. If no arguments are entered, the process data is",
" is displayed for all processes. Specific processes may be selected",
@@ -1409,6 +1409,7 @@ char *help_ps[] = {
" -k restrict the output to only kernel threads.",
" -u restrict the output to only user tasks.",
" -G display only the thread group leader in a thread group.",
+" -I exclude idle threads/tasks from the output.",
" -y policy restrict the output to tasks having a specified scheduling policy",
" expressed by its integer value or by its (case-insensitive) name;",
" multiple policies may be entered in a comma-separated list:",
diff --git a/task.c b/task.c
index d7f3c129a709..25b929d791b3 100644
--- a/task.c
+++ b/task.c
@@ -107,8 +107,8 @@ static void foreach_cleanup(void *);
static void ps_cleanup(void *);
static char *task_pointer_string(struct task_context *, ulong, char *);
static int panic_context_adjusted(struct task_context *tc);
-static void show_last_run(struct task_context *, struct psinfo *);
-static void show_milliseconds(struct task_context *, struct psinfo *);
+static void show_last_run(ulong, struct task_context *, struct psinfo *);
+static void show_milliseconds(ulong, struct task_context *, struct psinfo *);
static char *translate_nanoseconds(ulonglong, char *);
static int sort_by_last_run(const void *arg1, const void *arg2);
static void sort_context_array_by_last_run(void);
@@ -3534,7 +3534,7 @@ cmd_ps(void)
cpuspec = NULL;
flag = 0;
- while ((c = getopt(argcnt, args, "HASgstcpkuGlmarC:y:")) != EOF) {
+ while ((c = getopt(argcnt, args, "HAISgstcpkuGlmarC:y:")) != EOF) {
switch(c)
{
case 'k':
@@ -3645,6 +3645,10 @@ cmd_ps(void)
flag |= PS_ACTIVE;
break;
+ case 'I':
+ flag |= PS_EXCLUDE_IDLE;
+ break;
+
case 'H':
flag |= PS_NO_HEADER;
break;
@@ -3794,6 +3798,8 @@ show_ps_data(ulong flag, struct task_context *tc, struct psinfo *psi)
return;
if ((flag & PS_POLICY) && !has_sched_policy(tc->task, psi->policy))
return;
+ if (tc && (flag & PS_EXCLUDE_IDLE) && is_idle_thread(tc->task))
+ return;
if (flag & PS_GROUP) {
if (flag & (PS_LAST_RUN|PS_MSECS))
error(FATAL, "-G not supported with -%c option\n",
@@ -3827,11 +3833,11 @@ show_ps_data(ulong flag, struct task_context *tc, struct psinfo *psi)
return;
}
if (flag & (PS_LAST_RUN)) {
- show_last_run(tc, psi);
+ show_last_run(flag, tc, psi);
return;
}
if (flag & (PS_MSECS)) {
- show_milliseconds(tc, psi);
+ show_milliseconds(flag, tc, psi);
return;
}
if (flag & PS_ARGV_ENVP) {
@@ -3988,7 +3994,7 @@ show_ps_summary(ulong flag)
char string[3];
} ps_state[MAX_STATES];
- if (flag & (PS_USER|PS_KERNEL|PS_GROUP))
+ if (flag & (PS_USER|PS_KERNEL|PS_GROUP|PS_EXCLUDE_IDLE))
error(FATAL, "-S option cannot be used with other options\n");
for (s = 0; s < MAX_STATES; s++)
@@ -4023,7 +4029,7 @@ show_ps_summary(ulong flag)
* current state.
*/
static void
-show_last_run(struct task_context *tc, struct psinfo *psi)
+show_last_run(ulong flag, struct task_context *tc, struct psinfo *psi)
{
int i, c, others;
struct task_context *tcp;
@@ -4053,6 +4059,8 @@ show_last_run(struct task_context *tc, struct psinfo *psi)
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
if (tcp->processor != c)
continue;
+ if ((flag & PS_EXCLUDE_IDLE) && is_idle_thread(tcp->task))
+ continue;
fprintf(fp, format, task_last_run(tcp->task));
fprintf(fp, "[%s] ",
task_state_string(tcp->task, buf, !VERBOSE));
@@ -4066,6 +4074,8 @@ show_last_run(struct task_context *tc, struct psinfo *psi)
} else {
tcp = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
+ if ((flag & PS_EXCLUDE_IDLE) && is_idle_thread(tcp->task))
+ continue;
fprintf(fp, format, task_last_run(tcp->task));
fprintf(fp, "[%s] ", task_state_string(tcp->task, buf, !VERBOSE));
print_task_header(fp, tcp, FALSE);
@@ -4104,7 +4114,7 @@ translate_nanoseconds(ulonglong value, char *buf)
* sched_info.last_arrival and its current state.
*/
static void
-show_milliseconds(struct task_context *tc, struct psinfo *psi)
+show_milliseconds(ulong flag, struct task_context *tc, struct psinfo *psi)
{
int i, c, others, days, max_days;
struct task_context *tcp;
@@ -4154,6 +4164,8 @@ show_milliseconds(struct task_context *tc, struct psinfo *psi)
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
if (tcp->processor != c)
continue;
+ if ((flag & PS_EXCLUDE_IDLE) && is_idle_thread(tcp->task))
+ continue;
delta = rq_clock - task_last_run(tcp->task);
if (delta < 0)
delta = 0;
@@ -4188,6 +4200,8 @@ show_milliseconds(struct task_context *tc, struct psinfo *psi)
} else {
tcp = FIRST_CONTEXT();
for (i = 0; i < RUNNING_TASKS(); i++, tcp++) {
+ if ((flag & PS_EXCLUDE_IDLE) && is_idle_thread(tcp->task))
+ continue;
if ((kt->flags & SMP) && (kt->flags & PER_CPU_OFF))
runq = rq_sp->value +
kt->__per_cpu_offset[tcp->processor];
@@ -4562,6 +4576,8 @@ show_task_times(struct task_context *tcp, ulong flags)
continue;
if ((flags & PS_KERNEL) && !is_kernel_thread(tc->task))
continue;
+ if ((flags & PS_EXCLUDE_IDLE) && is_idle_thread(tc->task))
+ continue;
if (flags & PS_GROUP) {
tgid = task_tgid(tc->task);
if (tc->pid != tgid) {
--
2.54.0

View File

@ -0,0 +1,175 @@
From 97b7c0a6085212ccb2dcc5e1b302906db6080d51 Mon Sep 17 00:00:00 2001
From: Aaron Tomlin <atomlin@atomlin.com>
Date: Sat, 4 Jul 2026 14:14:50 -0400
Subject: [PATCH 34/39] task: Introduce -Y option to ps command to display
scheduling policy and priority
Adds a new option '-Y' to the 'ps' command to print scheduling policy
(e.g., NORMAL, FIFO, RR, etc.) and dynamic priority (prio) for each
task.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
defs.h | 3 ++-
help.c | 3 ++-
task.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/defs.h b/defs.h
index ed91a5bc8e91..d7bcdd083671 100644
--- a/defs.h
+++ b/defs.h
@@ -5404,8 +5404,9 @@ extern long _ZOMBIE_;
#define PS_POLICY (0x80000)
#define PS_ACTIVE (0x100000)
#define PS_EXCLUDE_IDLE (0x200000)
+#define PS_POLICY_DATA (0x400000)
-#define PS_EXCLUSIVE (PS_TGID_LIST|PS_ARGV_ENVP|PS_TIMES|PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN|PS_RLIMIT|PS_MSECS|PS_SUMMARY|PS_ACTIVE)
+#define PS_EXCLUSIVE (PS_TGID_LIST|PS_ARGV_ENVP|PS_TIMES|PS_CHILD_LIST|PS_PPID_LIST|PS_LAST_RUN|PS_RLIMIT|PS_MSECS|PS_SUMMARY|PS_ACTIVE|PS_POLICY_DATA)
#define MAX_PS_ARGS (100) /* maximum command-line specific requests */
diff --git a/help.c b/help.c
index c88797e8ae70..79bfa5ba0844 100644
--- a/help.c
+++ b/help.c
@@ -1392,7 +1392,7 @@ NULL
char *help_ps[] = {
"ps",
"display process status information",
-"[-k|-u|-G|-I|-y policy] [-s] [-p|-c|-t|-[l|m][-C cpu]|-a|-g|-r|-S|-A|-H]\n [pid | task | command] ...",
+"[-k|-u|-G|-I|-y policy] [-s] [-p|-c|-t|-[l|m][-C cpu]|-a|-g|-r|-S|-A|-H|-Y]\n [pid | task | command] ...",
" This command displays process status for selected, or all, processes" ,
" in the system. If no arguments are entered, the process data is",
" is displayed for all processes. Specific processes may be selected",
@@ -1473,6 +1473,7 @@ char *help_ps[] = {
" -S display a summary consisting of the number of tasks in a task state.",
" -A display only the active task on each cpu.",
" -H display no header line.",
+" -Y display the task's scheduling policy and priority.",
"\nEXAMPLES",
" Show the process status of all current tasks:\n",
" %s> ps",
diff --git a/task.c b/task.c
index 25b929d791b3..7dacb05b2406 100644
--- a/task.c
+++ b/task.c
@@ -118,6 +118,8 @@ static void parse_task_thread(int argcnt, char *arglist[], struct task_context *
static void stack_overflow_check_init(void);
static int has_sched_policy(ulong, ulong);
static ulong task_policy(ulong);
+static const char *task_policy_name(ulong);
+static int task_prio(ulong);
static ulong sched_policy_bit_from_str(const char *);
static ulong make_sched_policy(const char *);
void crash_get_current_task_info(unsigned long *, char **);
@@ -3534,7 +3536,7 @@ cmd_ps(void)
cpuspec = NULL;
flag = 0;
- while ((c = getopt(argcnt, args, "HAISgstcpkuGlmarC:y:")) != EOF) {
+ while ((c = getopt(argcnt, args, "HAISgstcpkuGlmarC:y:Y")) != EOF) {
switch(c)
{
case 'k':
@@ -3649,6 +3651,11 @@ cmd_ps(void)
flag |= PS_EXCLUDE_IDLE;
break;
+ case 'Y':
+ check_ps_exclusive(flag, PS_POLICY_DATA);
+ flag |= PS_POLICY_DATA;
+ break;
+
case 'H':
flag |= PS_NO_HEADER;
break;
@@ -3822,6 +3829,31 @@ show_ps_data(ulong flag, struct task_context *tc, struct psinfo *psi)
}
}
+ if (flag & PS_POLICY_DATA) {
+ task_active = is_task_active(tc->task);
+
+ if (task_active) {
+ if (hide_offline_cpu(tc->processor))
+ fprintf(fp, "- ");
+ else
+ fprintf(fp, "> ");
+ } else
+ fprintf(fp, " ");
+
+ fprintf(fp, "%7ld %7ld %3s %s %-12s %4d ",
+ tc->pid, task_to_pid(tc->ptask),
+ task_cpu(tc->processor, buf2, !VERBOSE),
+ task_pointer_string(tc, flag & PS_KSTACKP, buf3),
+ task_policy_name(tc->task),
+ task_prio(tc->task));
+
+ if (is_kernel_thread(tc->task))
+ fprintf(fp, "[%s]\n", tc->comm);
+ else
+ fprintf(fp, "%s\n", tc->comm);
+ return;
+ }
+
if (flag & PS_PPID_LIST) {
parent_list(tc->task);
fprintf(fp, "\n");
@@ -3902,6 +3934,14 @@ show_ps(ulong flag, struct psinfo *psi)
mkstring(buf, VADDR_PRLEN, CENTER|RJUST, "KSTACKP") :
mkstring(buf, VADDR_PRLEN, CENTER, "TASK"));
+ if ((flag & PS_POLICY_DATA) && !(flag & PS_NO_HEADER)) {
+ fprintf(fp,
+ " PID PPID CPU %s POLICY PRIO COMM\n",
+ flag & PS_KSTACKP ?
+ mkstring(buf, VADDR_PRLEN, CENTER|RJUST, "KSTACKP") :
+ mkstring(buf, VADDR_PRLEN, CENTER, "TASK"));
+ }
+
if (flag & PS_SHOW_ALL) {
if (flag & PS_TIMES) {
@@ -3994,7 +4034,7 @@ show_ps_summary(ulong flag)
char string[3];
} ps_state[MAX_STATES];
- if (flag & (PS_USER|PS_KERNEL|PS_GROUP|PS_EXCLUDE_IDLE))
+ if (flag & (PS_USER|PS_KERNEL|PS_GROUP|PS_EXCLUDE_IDLE|PS_POLICY_DATA))
error(FATAL, "-S option cannot be used with other options\n");
for (s = 0; s < MAX_STATES; s++)
@@ -6050,6 +6090,28 @@ task_policy(ulong task)
return policy;
}
+static const char *
+task_policy_name(ulong task)
+{
+ ulong policy_bit = task_policy(task);
+ struct sched_policy_info *info;
+
+ for (info = sched_policy_info; info->name; info++) {
+ if (policy_bit == (1UL << info->value))
+ return info->name;
+ }
+ return "UNKNOWN";
+}
+
+static int
+task_prio(ulong task)
+{
+ fill_task_struct(task);
+ if (!tt->last_task_read || INVALID_MEMBER(task_struct_prio))
+ return 0;
+ return INT(tt->task_struct + OFFSET(task_struct_prio));
+}
+
/*
* Return a task's tgid.
*/
--
2.54.0

View File

@ -0,0 +1,180 @@
From 82039b0f21de4231d1fc03e8819c868a84b213f8 Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Wed, 8 Jul 2026 02:45:50 +0000
Subject: [PATCH 35/39] Fix failure of "runq -g" option on Linux 7.2 and later
kernels
Kernel commit b8fea7af0e40 ("sched/fair: Allocate cfs_tg_state with
percpu allocator") changed task_group.cfs_rq from a pointer array to a
per-cpu variable allocated by the per-cpu allocator.
Without the patch, the "runq -g" option fails with the following error
on the first time, and with a segmentation fault on the second time.
crash> runq -g
CPU 0
CURRENT: PID: 5687 TASK: ffff8b0bc175a2c0 COMMAND: "bash"
ROOT_TASK_GROUP: ffffffff93a55600 CFS_RQ: 0
runq: invalid kernel virtual address: 58 type: "curr"
crash> runq -g
Segmentation fault (core dumped)
Since there is no way to determine whether it is a per-cpu variable,
check wthether it is a pointer array or not.
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
defs.h | 2 ++
kernel.c | 14 ++++++++++++++
symbols.c | 27 +++++++++++++++++++++++++++
task.c | 30 ++++++++++++++++++++++--------
4 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/defs.h b/defs.h
index d7bcdd083671..e3027e2b9141 100644
--- a/defs.h
+++ b/defs.h
@@ -691,6 +691,7 @@ struct new_utsname {
#define KMOD_PAX (0x100ULL)
#define KMOD_MEMORY (0x200ULL)
#define IRQ_DESC_TREE_MAPLE (0x400ULL)
+#define PER_CPU_CFS_RQ (0x800ULL)
#define XEN() (kt->flags & ARCH_XEN)
#define OPENVZ() (kt->flags & ARCH_OPENVZ)
@@ -5886,6 +5887,7 @@ void parse_for_member_extended(struct datatype_member *, ulong);
void add_to_downsized(char *);
int is_downsized(char *);
int is_string(char *, char *);
+int is_ptrptr(char *, char *);
struct syment *symbol_complete_match(const char *, struct syment *);
/*
diff --git a/kernel.c b/kernel.c
index eb9754c5e082..e53038d5d8db 100644
--- a/kernel.c
+++ b/kernel.c
@@ -400,6 +400,18 @@ kernel_init()
MEMBER_OFFSET_INIT(task_group_rt_rq, "task_group", "rt_rq");
MEMBER_OFFSET_INIT(task_group_parent, "task_group", "parent");
+ /*
+ * task_group.cfs_rq was changed from a pointer array to a per-cpu
+ * variable at Linux 7.2 (b8fea7af0e40). Since there is no way to
+ * determine it, we check whether it is a pointer array or not.
+ * - struct cfs_rq **cfs_rq;
+ * + struct cfs_rq __percpu *cfs_rq;
+ */
+ if (VALID_MEMBER(task_group_cfs_rq)) {
+ if (!is_ptrptr("task_group", "cfs_rq"))
+ kt->flags2 |= PER_CPU_CFS_RQ;
+ }
+
/*
* In 2.4, smp_send_stop() sets smp_num_cpus back to 1
* in some, but not all, architectures. So if a count
@@ -6362,6 +6374,8 @@ dump_kernel_table(int verbose)
fprintf(fp, "%sKMOD_PAX", others++ ? "|" : "");
if (kt->flags2 & KMOD_MEMORY)
fprintf(fp, "%sKMOD_MEMORY", others++ ? "|" : "");
+ if (kt->flags2 & PER_CPU_CFS_RQ)
+ fprintf(fp, "%sPER_CPU_CFS_RQ", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " stext: %lx\n", kt->stext);
diff --git a/symbols.c b/symbols.c
index 78e400ba3756..03511c8cbe8c 100644
--- a/symbols.c
+++ b/symbols.c
@@ -7933,6 +7933,33 @@ is_string(char *structure, char *member)
return retval;
}
+int
+is_ptrptr(char *structure, char *member)
+{
+ int retval;
+ char *t;
+ char buf[BUFSIZE];
+
+ retval = FALSE;
+ open_tmpfile();
+ whatis_datatype(structure, STRUCT_REQUEST, pc->tmpfile);
+ rewind(pc->tmpfile);
+ while (fgets(buf, BUFSIZE, pc->tmpfile)) {
+ if (!(t = strstr(buf, "**")))
+ continue;
+ t += 2;
+ if (t != strstr(t, member))
+ continue;
+ t += strlen(member);
+ if (*t == ';') {
+ retval = TRUE;
+ break;
+ }
+ }
+ close_tmpfile();
+
+ return retval;
+}
/*
* Generic function for dumping data structure declarations, with a small
diff --git a/task.c b/task.c
index 7dacb05b2406..b95d3d7fb2af 100644
--- a/task.c
+++ b/task.c
@@ -9542,8 +9542,12 @@ print_parent_task_group_fair(void *t, int cpu)
readmem(tgi->task_group + OFFSET(task_group_cfs_rq),
KVADDR, &cfs_rq_c, sizeof(ulong),
"task_group cfs_rq", FAULT_ON_ERROR);
- readmem(cfs_rq_c + cpu * sizeof(ulong), KVADDR, &cfs_rq_p,
- sizeof(ulong), "task_group cfs_rq", FAULT_ON_ERROR);
+
+ if (kt->flags2 & PER_CPU_CFS_RQ)
+ cfs_rq_p = cfs_rq_c + kt->__per_cpu_offset[cpu];
+ else
+ readmem(cfs_rq_c + cpu * sizeof(ulong), KVADDR, &cfs_rq_p,
+ sizeof(ulong), "task_group cfs_rq", FAULT_ON_ERROR);
print_group_header_fair(tgi->depth, cfs_rq_p, tgi);
tgi->use = 0;
@@ -9569,8 +9573,13 @@ dump_tasks_in_lower_dequeued_cfs_rq(int depth, ulong cfs_rq, int cpu,
readmem(tgi_array[i]->task_group + OFFSET(task_group_cfs_rq),
KVADDR, &cfs_rq_c, sizeof(ulong), "task_group cfs_rq",
FAULT_ON_ERROR);
- readmem(cfs_rq_c + cpu * sizeof(ulong), KVADDR, &cfs_rq_p,
- sizeof(ulong), "task_group cfs_rq", FAULT_ON_ERROR);
+
+ if (kt->flags2 & PER_CPU_CFS_RQ)
+ cfs_rq_p = cfs_rq_c + kt->__per_cpu_offset[cpu];
+ else
+ readmem(cfs_rq_c + cpu * sizeof(ulong), KVADDR, &cfs_rq_p,
+ sizeof(ulong), "task_group cfs_rq", FAULT_ON_ERROR);
+
if (cfs_rq == cfs_rq_p)
continue;
@@ -10433,10 +10442,15 @@ dump_tasks_by_task_group(void)
readmem(rt_rq + cpu * sizeof(ulong), KVADDR,
&rt_rq_p, sizeof(ulong), "task_group rt_rq",
FAULT_ON_ERROR);
- if (cfs_rq)
- readmem(cfs_rq + cpu * sizeof(ulong), KVADDR,
- &cfs_rq_p, sizeof(ulong), "task_group cfs_rq",
- FAULT_ON_ERROR);
+ if (cfs_rq) {
+ if (kt->flags2 & PER_CPU_CFS_RQ)
+ cfs_rq_p = cfs_rq + kt->__per_cpu_offset[cpu];
+ else
+ readmem(cfs_rq + cpu * sizeof(ulong), KVADDR,
+ &cfs_rq_p, sizeof(ulong),
+ "task_group cfs_rq", FAULT_ON_ERROR);
+ }
+
fprintf(fp, "%sCPU %d", displayed++ ? "\n" : "", cpu);
if (hide_offline_cpu(cpu)) {
--
2.54.0

View File

@ -0,0 +1,52 @@
From f336dfa79ed94895914e132c31f2db7e09bf4ab1 Mon Sep 17 00:00:00 2001
From: Kazuhito Hagio <k-hagio-ab@nec.com>
Date: Wed, 8 Jul 2026 02:45:50 +0000
Subject: [PATCH 36/39] Fix "runq -g" option to display task_group name on
Linux 6.15 and later
Kernel commit 633488947ef66 ("kernfs: Use RCU to access
kernfs_node::parent.") changed the "parent" member name in struct
kernfs_node to "__parent".
Without the patch, the "rung -g" option cannot display task_group's
name:
crash> runq -g
CPU 0
CURRENT: PID: 5687 TASK: ffff8b0bc175a2c0 COMMAND: "bash"
ROOT_TASK_GROUP: ffffffff93a55600 CFS_RQ: ffff8b0cf8232240
TASK_GROUP: ffff8b0b942a1e00 CFS_RQ: fffff1b5ffc0d540
TASK_GROUP: ffff8b0bc0f27900 CFS_RQ: fffff1b5ffc23f80
TASK_GROUP: ffff8b0b862fcc00 CFS_RQ: fffff1b5ffc29f00
[120] PID: 5687 TASK: ffff8b0bc175a2c0 COMMAND: "bash" [CURRENT]
With the patch:
crash> runq -g
CPU 0
CURRENT: PID: 5687 TASK: ffff8b0bc175a2c0 COMMAND: "bash"
ROOT_TASK_GROUP: ffffffff93a55600 CFS_RQ: ffff8b0cf8232240
TASK_GROUP: ffff8b0b942a1e00 CFS_RQ: fffff1b5ffc0d540 <user.slice>
TASK_GROUP: ffff8b0bc0f27900 CFS_RQ: fffff1b5ffc23f80 <user-0.slice>
TASK_GROUP: ffff8b0b862fcc00 CFS_RQ: fffff1b5ffc29f00 <session-2.scope>
[120] PID: 5687 TASK: ffff8b0bc175a2c0 COMMAND: "bash" [CURRENT]
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
task.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/task.c b/task.c
index b95d3d7fb2af..a4118766fb42 100644
--- a/task.c
+++ b/task.c
@@ -9879,6 +9879,8 @@ task_group_offset_init(void)
MEMBER_OFFSET_INIT(cgroup_kn, "cgroup", "kn");
MEMBER_OFFSET_INIT(kernfs_node_name, "kernfs_node", "name");
MEMBER_OFFSET_INIT(kernfs_node_parent, "kernfs_node", "parent");
+ if (INVALID_MEMBER(kernfs_node_parent))
+ MEMBER_OFFSET_INIT(kernfs_node_parent, "kernfs_node", "__parent");
MEMBER_OFFSET_INIT(task_group_siblings, "task_group", "siblings");
MEMBER_OFFSET_INIT(task_group_children, "task_group", "children");
--
2.54.0

View File

@ -0,0 +1,70 @@
From bfce25840cffabf0574825e796b0284ed36276bb Mon Sep 17 00:00:00 2001
From: Tao Liu <ltao@redhat.com>
Date: Tue, 4 Aug 2026 15:20:33 +1200
Subject: [PATCH 37/39] x86_64: Make ORC_REG_SP and ORC_REG_PREV_SP independent
from kernel version
Previously ORC_REG_SP and ORC_REG_PREV_SP will depend on kernel version
for their value, however this is fragile since distributions will
backport the upstream patch to a lower kernel version, thus break the
version assumption.
This patch fixes by checking the value of ORC_REG_PREV_SP, which can be
get from orc_fp_entry. ORC_REG_PREV_SP's value can work as the indicator
of whether the current kernel have applied the upstream patch 1735858caa4b
("objtool/x86: Reorder ORC register numbering").
Fixes: d0ee428664f9 ("x86_64: Fix "bt" command to use correct ORC register
values on Linux 7.1 and later")
Reviewed-by: HAGIO KAZUHITO <k-hagio-ab@nec.com>
Reviewed-by: MUKESH KUMAR PILANIYA <mpilaniy@redhat.com>
Reviewed-by: Dave Young <yangrr.2009@tsinghua.org.cn>
Signed-off-by: Tao Liu <ltao@redhat.com>
---
x86_64.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/x86_64.c b/x86_64.c
index 55648697baf3..62b27762a41f 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -6736,13 +6736,28 @@ x86_64_ORC_init(void)
if (orc->has_signal && !orc->has_end)
machdep->flags |= ORC_6_4;
- /* See kernel commit 1735858caa4b */
- if (THIS_KERNEL_VERSION >= LINUX(7,1,0)) {
- ORC_REG_SP = 3;
- ORC_REG_PREV_SP = 8;
- } else {
- ORC_REG_SP = 5;
- ORC_REG_PREV_SP = 1;
+ /* Try get ORC_REG_(PREV)_SP */
+ ORC_REG_SP = 5;
+ ORC_REG_PREV_SP = 1;
+
+ if (kernel_symbol_exists("orc_fp_entry")) {
+ /*
+ * kernel_orc_entry_6_4 & kernel_orc_entry have the same
+ * offset of bp_reg.
+ */
+ kernel_orc_entry_6_4 entry = {0};
+ if (try_get_symbol_data("orc_fp_entry", sizeof(entry), &entry)) {
+ /*
+ * orc_fp_entry.bp_reg = ORC_REG_PREV_SP
+ * See kernel commit 1735858caa4b. Use ORC_REG_PREV_SP
+ * as the indicator of the commit.
+ */
+ if (entry.bp_reg == 8) {
+ ORC_REG_SP = 3;
+ ORC_REG_PREV_SP = 8;
+ }
+ } else
+ error(WARNING, "Cannot get orc_fp_entry.bp_reg info");
}
machdep->flags |= ORC;
--
2.54.0

View File

@ -0,0 +1,88 @@
From 08e9d02d2c46011d1565a3bedf33c66f07cf8bde Mon Sep 17 00:00:00 2001
From: Rui Qi <qirui.001@bytedance.com>
Date: Mon, 10 Aug 2026 11:30:23 +0800
Subject: [PATCH 38/39] symbols: optimize symval_hash_init with O(1) tail
insertion
Replace the O(n) tail traversal with O(1) tail insertion using a
dynamically-allocated per-bucket tail tracking array. The original code
traversed the entire linked list on every insert to find the tail,
resulting in O(n^2) complexity for hash table initialization.
This reduces hash table initialization from O(n^2) to O(n).
Benchmark on an x86_64 machine (kernel 5.10.135, ~112k symbols):
Before: 5.54 s (mean, n=6, sigma=0.12)
After: 5.31 s (mean, n=6, sigma=0.12)
Speedup: 1.04x (-4.1%)
Benchmark on an ARM64 Neoverse-N2 machine (kernel 5.15.152 arm64,
~132k nm symbols):
Before: 3.4113 s (mean, n=48, sigma=0.0698)
After: 2.5073 s (mean, n=48, sigma=0.0394)
Speedup: 1.36x (-26.5%), with improved consistency.
User CPU: 4.1706 s -> 3.2768 s (-21.4%).
Benchmark retest on a RISC-V machine (kernel
6.12.13 riscv64, ~198k nm symbols):
$ printf 'q\n' | ./crash vmlinux /proc/kcore
Before: 11.934 s (mean, n=3, sigma=0.880; samples: 12.921, 11.231, 11.651)
After: 10.255 s (mean, n=3, sigma=0.835; samples: 11.131, 9.467, 10.168)
Speedup: 1.16x (-14.1%)
Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Reviewed-by: Dave Young <yangrr.2009@tsinghua.org.cn>
---
symbols.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/symbols.c b/symbols.c
index 03511c8cbe8c..a7ccdb101033 100644
--- a/symbols.c
+++ b/symbols.c
@@ -1085,12 +1085,16 @@ symbol_value_from_proc_kallsyms(char *symname)
/*
* Install all static kernel symbol values into the symval_hash.
+ * Uses a dynamically-allocated tails[] array for O(1) tail insertion.
*/
static void
symval_hash_init(void)
{
int index;
- struct syment *sp, *sph;
+ struct syment *sp, **tails;
+
+ tails = (struct syment **)GETBUF(SYMVAL_HASH * sizeof(struct syment *));
+ BZERO(tails, SYMVAL_HASH * sizeof(struct syment *));
for (sp = st->symtable; sp < st->symend; sp++) {
index = SYMVAL_HASH_INDEX(sp->value);
@@ -1098,15 +1102,12 @@ symval_hash_init(void)
if (st->symval_hash[index].val_hash_head == NULL) {
st->symval_hash[index].val_hash_head = sp;
st->symval_hash[index].val_hash_last = sp;
- continue;
- }
-
- sph = st->symval_hash[index].val_hash_head;
- while (sph->val_hash_next)
- sph = sph->val_hash_next;
-
- sph->val_hash_next = sp;
+ } else
+ tails[index]->val_hash_next = sp;
+ tails[index] = sp;
}
+
+ FREEBUF(tails);
}
/*
--
2.54.0

View File

@ -0,0 +1,74 @@
From 6bee9c7021df3d1d610ad50a45ad803bbcb9a724 Mon Sep 17 00:00:00 2001
From: Tao Liu <ltao@redhat.com>
Date: Wed, 29 Jul 2026 22:42:42 +1200
Subject: [PATCH 39/39] Prevent out-of-bounds access of note_buf
Add a boundary check for note_buf in case any protential
data access outside of the buffer.
Signed-off-by: Tao Liu <ltao@redhat.com>
---
diskdump.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/diskdump.c b/diskdump.c
index 20f8b19f1514..7263e2c3199c 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -338,16 +338,20 @@ process_elf32_notes(void *note_buf, unsigned long size_note)
int num = 0;
int vmcoredd_num = 0;
int qemu_num = 0;
+ unsigned long left;
for (index = 0; index < size_note; index += len) {
nt = note_buf + index;
+ left = size_note - index;
- if (nt->n_type == NT_PRSTATUS) {
+ if (nt->n_type == NT_PRSTATUS && num < NR_CPUS) {
dd->nt_prstatus_percpu[num] = nt;
num++;
}
len = sizeof(Elf32_Nhdr);
- if (STRNEQ((char *)nt + len, "QEMU")) {
+ if ((left >= sizeof(Elf32_Nhdr) + MAX(nt->n_namesz + sizeof(ulong), 4)) &&
+ STRNEQ((char *)nt + len, "QEMU") &&
+ qemu_num < NR_CPUS) {
ulong *ptr =
(ulong *)((char *)nt + sizeof(Elf32_Nhdr) + nt->n_namesz);
dd->nt_qemucs_percpu[qemu_num] =
@@ -395,22 +399,27 @@ process_elf64_notes(void *note_buf, unsigned long size_note)
int num = 0;
int vmcoredd_num = 0;
int qemu_num = 0;
+ unsigned long left;
for (index = 0; index < size_note; index += len) {
nt = note_buf + index;
+ left = size_note - index;
- if (nt->n_type == NT_PRSTATUS) {
+ if (nt->n_type == NT_PRSTATUS && num < NR_CPUS) {
dd->nt_prstatus_percpu[num] = nt;
num++;
}
- if ((nt->n_type == NT_TASKSTRUCT) &&
+ if ((nt->n_type == NT_TASKSTRUCT) &&
+ (left >= sizeof(Elf64_Nhdr) + MAX(nt->n_namesz + sizeof(ulong), 4)) &&
(STRNEQ((char *)nt + sizeof(Elf64_Nhdr), "SNAP"))) {
pc->flags2 |= (LIVE_DUMP|SNAP);
dd->snapshot_task =
*((ulong *)((char *)nt + sizeof(Elf64_Nhdr) + nt->n_namesz));
}
len = sizeof(Elf64_Nhdr);
- if (STRNEQ((char *)nt + len, "QEMU")) {
+ if ((left >= sizeof(Elf64_Nhdr) + MAX(nt->n_namesz + sizeof(ulong), 4)) &&
+ STRNEQ((char *)nt + len, "QEMU") &&
+ qemu_num < NR_CPUS) {
ulong *ptr =
(ulong *)((char *)nt + sizeof(Elf64_Nhdr) + nt->n_namesz);
dd->nt_qemucs_percpu[qemu_num] =
--
2.54.0

View File

@ -1,5 +1,5 @@
--- crash-9.0.1/Makefile.orig
+++ crash-9.0.1/Makefile
--- crash-9.0.2/Makefile.orig
+++ crash-9.0.2/Makefile
@@ -204,7 +204,7 @@ GDB_FLAGS=
# TARGET_CFLAGS will be configured automatically by configure
TARGET_CFLAGS=
@ -18,8 +18,8 @@
@echo "../../${PROGRAM} ../../${PROGRAM}lib.a" > ${GDB}/gdb/mergeobj
@rm -f ${PROGRAM}
@if [ ! -f ${GDB}/config.status ]; then \
--- crash-9.0.1/configure.c.orig
+++ crash-9.0.1/configure.c
--- crash-9.0.2/configure.c.orig
+++ crash-9.0.2/configure.c
@@ -810,7 +810,8 @@ build_configure(struct supported_gdb_version *sp)
fprintf(fp2, "%s\n", sp->GDB);
sprintf(target_data.gdb_version, "%s", &sp->GDB[4]);

View File

@ -3,7 +3,7 @@
#
Summary: Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles
Name: crash
Version: 9.0.1
Version: 9.0.2
Release: 1%{?dist}.alma.1
License: GPL-3.0-only
Source0: https://github.com/crash-utility/crash/archive/crash-%{version}.tar.gz
@ -19,6 +19,44 @@ Provides: bundled(libiberty)
Provides: bundled(gdb) = 16.2
Patch0: lzo_snappy_zstd.patch
Patch1: crash-9.0.0_build.patch
Patch3: 0002-Mark-start-of-9.0.3-development-phase-with-version-9.patch
Patch4: 0003-RISCV64-introduce-riscv64_VTOP-and-riscv64_PTOV.patch
Patch5: 0004-Fix-wrong-detection-of-LIVEPATCH-on-old-kernels.patch
Patch6: 0005-Fix-unwinding-with-32k-stacks-in-ppc64le.patch
Patch7: 0006-arm64-fix-gdb-register-feeding-for-exception-frames-.patch
Patch8: 0007-RISCV64-fix-pmd-address-calculation-in-riscv64_vtop_.patch
Patch9: 0008-symbols-optimize-symname_hash-with-larger-table-and-.patch
Patch10: 0009-RISCV64-add-mapping-symbol-filter-in-riscv64_verify_.patch
Patch11: 0010-Fix-get_xtime-for-kernel-6.13-and-higher.patch
Patch12: 0011-symbols-Add-support-for-mod-symtab-with-combined-GPL.patch
Patch13: 0012-add-folio_order-function.patch
Patch14: 0013-xarray-add-large-folio-support.patch
Patch15: 0014-add-files-n-command-for-an-inode.patch
Patch16: 0015-x86_64-Fix-bt-command-for-noreturn-functions.patch
Patch17: 0016-x86_64-Fix-bt-command-to-use-correct-ORC-register-va.patch
Patch18: 0017-Fix-kmem-i-option-to-display-swap-usage-on-Linux-6.1.patch
Patch19: 0018-Fix-swap-command-on-Linux-7.1-and-later.patch
Patch20: 0019-LoongArch64-Fix-CPU-registers-reading-from-dump-note.patch
Patch21: 0020-LoongArch64-Fix-pt_regs-initialization-for-active-ta.patch
Patch22: 0021-LoongArch64-Support-backtracing-across-exception-bou.patch
Patch23: 0022-LoongArch64-Fix-stack-frame-loop-bounds-for-exceptio.patch
Patch24: 0023-LoongArch64-Add-dummy-eframe_search-to-avoid-bt-e-se.patch
Patch25: 0024-LoongArch64-print-exception-return-address-as-ERA.patch
Patch26: 0025-LoongArch64-resolve-relocated-exception-vector-addre.patch
Patch27: 0026-LoongArch64-add-initial-ORC-unwinder-support.patch
Patch28: 0027-LoongArch64-unwind-dumpfile-active-tasks-from-IRQ-st.patch
Patch29: 0028-LoongArch64-avoid-replacing-a-valid-RA-with-stack-no.patch
Patch30: 0029-LoongArch64-print-exception-registers-only-with-bt-f.patch
Patch31: 0030-riscv64-Set-VMEMMAP-flag-to-fix-spurious-mem_map-war.patch
Patch32: 0031-riscv64-Guard-verbose-output-in-vtop-page-table-walk.patch
Patch33: 0032-riscv64-Add-get_kvaddr_ranges-callback-for-kernel-ad.patch
Patch34: 0033-task-Introduce-I-option-to-ps-command-to-exclude-idl.patch
Patch35: 0034-task-Introduce-Y-option-to-ps-command-to-display-sch.patch
Patch36: 0035-Fix-failure-of-runq-g-option-on-Linux-7.2-and-later-.patch
Patch37: 0036-Fix-runq-g-option-to-display-task_group-name-on-Linu.patch
Patch38: 0037-x86_64-Make-ORC_REG_SP-and-ORC_REG_PREV_SP-independe.patch
Patch39: 0038-symbols-optimize-symval_hash_init-with-O-1-tail-inse.patch
Patch40: 0039-Prevent-out-of-bounds-access-of-note_buf.patch
%description
The core analysis suite is a self-contained tool that can be used to
@ -40,6 +78,44 @@ offered by Mission Critical Linux, or the LKCD kernel patch.
%setup -n %{name}-%{version} -q
%patch -P 0 -p1 -b lzo_snappy_zstd.patch
%patch -P 1 -p1
%patch -P 3 -p1
%patch -P 4 -p1
%patch -P 5 -p1
%patch -P 6 -p1
%patch -P 7 -p1
%patch -P 8 -p1
%patch -P 9 -p1
%patch -P 10 -p1
%patch -P 11 -p1
%patch -P 12 -p1
%patch -P 13 -p1
%patch -P 14 -p1
%patch -P 15 -p1
%patch -P 16 -p1
%patch -P 17 -p1
%patch -P 18 -p1
%patch -P 19 -p1
%patch -P 20 -p1
%patch -P 21 -p1
%patch -P 22 -p1
%patch -P 23 -p1
%patch -P 24 -p1
%patch -P 25 -p1
%patch -P 26 -p1
%patch -P 27 -p1
%patch -P 28 -p1
%patch -P 29 -p1
%patch -P 30 -p1
%patch -P 31 -p1
%patch -P 32 -p1
%patch -P 33 -p1
%patch -P 34 -p1
%patch -P 35 -p1
%patch -P 36 -p1
%patch -P 37 -p1
%patch -P 38 -p1
%patch -P 39 -p1
%patch -P 40 -p1
%build
@ -65,9 +141,12 @@ cp -p defs.h %{buildroot}%{_includedir}/crash
%{_includedir}/*
%changelog
* Tue Nov 25 2025 Eduard Abdullin <eabdullin@almalinux.org> - 9.0.1-1.alma.1
* Wed Aug 12 2026 Eduard Abdullin <eabdullin@almalinux.org> - 9.0.2-1.alma.1
- Debrand for AlmaLinux
* Tue Aug 11 2026 Tao Liu <ltao@redhat.com> - 9.0.1-2
- Rebase to upstream crash 9.0.2
* Mon Nov 24 2025 Tao Liu <ltao@redhat.com> - 9.0.1-1
- Rebase to upstream crash 9.0.1

View File

@ -1,6 +0,0 @@
--- !Policy
product_versions:
- rhel-10
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: kernel-qe.kernel-ci.general-kdump.tier0.functional}

View File

@ -1,5 +1,5 @@
--- crash-9.0.1/Makefile.orig
+++ crash-9.0.1/Makefile
--- crash-9.0.2/Makefile.orig
+++ crash-9.0.2/Makefile
@@ -256,7 +256,7 @@ all: make_configure
gdb_merge: force
@if [ ! -f ${GDB}/README ]; then \
@ -9,8 +9,8 @@
@echo "../../${PROGRAM} ../../${PROGRAM}lib.a" > ${GDB}/gdb/mergeobj
@rm -f ${PROGRAM}
@if [ ! -f ${GDB}/config.status ]; then \
--- crash-9.0.1/diskdump.c.orig
+++ crash-9.0.1/diskdump.c
--- crash-9.0.2/diskdump.c.orig
+++ crash-9.0.2/diskdump.c
@@ -23,6 +23,9 @@
* GNU General Public License for more details.
*/

14
plans/ci.fmf Normal file
View File

@ -0,0 +1,14 @@
plan:
import:
url: https://gitlab.cee.redhat.com/kernel-qe/test-plans.git
/RHEL8-KDUMP_T0:
plan+:
import+:
name: /plans/kdump/T0/RHEL8-KDUMP_T0
/RHEL9-KDUMP_Crash_Plugins_T0:
plan+:
import+:
name: /plans/kdump/T0/RHEL9-KDUMP_Crash_Plugins_T0

View File

@ -1,2 +1,2 @@
SHA512 (crash-9.0.1.tar.gz) = 3f49d9ce6ab82265f35e3ed539d0c310caa1fbfb68bd5edc84c033a2ccd5a48447eabd5663e5ea0a45100eee792f8867182ad4e4cae5ea4de3fc3abeda0e82b2
SHA512 (gdb-16.2.tar.gz) = c44a6cf6cdd6e92f1f4dda7d888ce3e7fdde65ca8736092e2b0a4c369ea09b499824a1d078f668f276fa6d00d311e9ec485231cb7b841479a4fabbfd65778084
SHA512 (crash-9.0.2.tar.gz) = c51164065ad7c65e8fda91c01b514b464e38197674e738f496e32669f749c8a34c60c1e65f435cfc23b7b035f5f8022c7a3ef39fb4de8cb42adb3f0537b1f72d