From nobody Fri Sep 25 10:37:47 2026 Received: from out30-119.freemail.mail.aliyun.com (out30-119.freemail.mail.aliyun.com [115.124.30.119]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 89E2D265620; Mon, 14 Sep 2026 09:27:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.119 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378028; cv=none; b=lG9RScKJUPmfCNIzE3N/fG4mOgdSDnMyqm5R22U9uC9siBc2i0CVDvZxg5PuK9yeEBYbKAh45ic7lHdznltCg+FZEzS/eRRyzomkx2/iJ3E6qhfbDMGBY6jBsuPpjo43cLKB/m7BVD8Sn/q5yhXDCGpxXfk32Qbj59Z/oyfCvRc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378028; c=relaxed/simple; bh=imAnoRy6Qfp+u+MeU7ec9oQhfrpDgAOwcuWgPdcIJic=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=H8upteZOKZ6yo2b3uWpv17ALyfIQfAoVJd9heNjTfn+J4wgOnLoRJ9G6aPVuITohBuaFF1QOkt0LXZWE/kJ65xOiLWXG3TLMFM/awzZNZWEei3d+u4POI7ama5lAaIPNUSOCSJ4MjirIsmsdFfD32p0usXUFifdt0ks0koL9qUc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=r5MBAWgF; arc=none smtp.client-ip=115.124.30.119 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="r5MBAWgF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378018; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=B4vbpoyhuDIkjmvV9Tlt1HaH95gfu3av5/l1QGxlK7U=; b=r5MBAWgFbChXQM7a1y3B0mg5ZBtUcW0RdbhxENSD8vnzxUcjFytyHLfk+8KriJ/ScAZz6Eva53PFgQmdVVrRNWQPa+r0gd4/55oD5p5Qzkqa/Qp/HacJWeOSbXWMZrwGDfI1KRhyTjBfirV0qDUIC9+yTdOm37gr5QVwHqtaaPc= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R641e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033037026112;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-m4_1789378012; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-m4_1789378012 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:26:54 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 1/7] riscv: stacktrace: Add frame record metadata Date: Mon, 14 Sep 2026 17:26:42 +0800 Message-Id: <20260914092648.51254-2-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han Reliable frame-pointer unwinding needs an explicit way to identify exception boundaries and the final entry frame. The existing unwinder infers those boundaries from return addresses, which is too loose for a future reliable unwinder. Add a small metadata frame record to pt_regs and initialize it on exception entry, kernel stack overflow, kernel thread fork, user fork, and early idle task setup. The record uses a zero {fp, ra} sentinel plus a type field so a later unwinder can distinguish a final user-to-kernel boundary from a nested kernel pt_regs boundary. This follows the arm64 metadata frame-record model, adapted to the RISC-V {fp, ra} frame record convention. The metadata is established at the RISC-V entry boundaries that need an explicit unwind marker: * exception entry clears the metadata {fp, ra} pair and uses SPP (or MPP in M-mode) to record whether the pt_regs frame is the final user-to-kernel boundary or a nested kernel boundary; * the kernel stack overflow path builds a nested pt_regs metadata record on the overflow stack so an unwinder can resume from the pre-overflow s0 saved in PT_S0; * _start_kernel builds the init task's final metadata record, while the secondary CPU path sets up s0 before smp_callin() so idle-task unwinding does not inherit an undefined caller frame; * copy_thread creates matching final metadata records for new kernel and user tasks, and keeps s0 available for the frame-pointer chain. Keep the embedded metadata-record field offsets distinct from the s0-relative STACKFRAME_* offsets used by call_on_irq_stack(), because the latter describe a frame record relative to s0 rather than to the record base. These changes keep s0 reserved for the frame-pointer chain at task and exception boundaries. Reviewed-by: Shuai Xue Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- arch/riscv/include/asm/ptrace.h | 9 ++++ arch/riscv/include/asm/stacktrace/frame.h | 53 +++++++++++++++++++++++ arch/riscv/kernel/asm-offsets.c | 6 +++ arch/riscv/kernel/entry.S | 39 ++++++++++++++++- arch/riscv/kernel/head.S | 23 ++++++++++ arch/riscv/kernel/process.c | 33 +++++++++++++- 6 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 arch/riscv/include/asm/stacktrace/frame.h diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrac= e.h index addc8188152f7..4b9b0f2792143 100644 --- a/arch/riscv/include/asm/ptrace.h +++ b/arch/riscv/include/asm/ptrace.h @@ -8,6 +8,7 @@ =20 #include #include +#include #include =20 #ifndef __ASSEMBLER__ @@ -53,6 +54,14 @@ struct pt_regs { unsigned long cause; /* a0 value before the syscall */ unsigned long orig_a0; + + /* + * This frame record is entirely zeroed on exception entry, allowing the + * unwinder to identify exception boundaries. The type field encodes + * whether the exception was taken from user (FINAL) or kernel (PT_REGS) + * mode. + */ + struct frame_record_meta stackframe; }; =20 #define PTRACE_SYSEMU 0x1f diff --git a/arch/riscv/include/asm/stacktrace/frame.h b/arch/riscv/include= /asm/stacktrace/frame.h new file mode 100644 index 0000000000000..5720a6c65fe88 --- /dev/null +++ b/arch/riscv/include/asm/stacktrace/frame.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_RISCV_STACKTRACE_FRAME_H +#define __ASM_RISCV_STACKTRACE_FRAME_H + +/* + * See: arch/arm64/include/asm/stacktrace/frame.h for the reference + * implementation. + */ + +/* + * - FRAME_META_TYPE_NONE + * + * This value is reserved. + * + * - FRAME_META_TYPE_FINAL + * + * The record is the last entry on the stack. + * Unwinding should terminate successfully. + * + * - FRAME_META_TYPE_PT_REGS + * + * The record is embedded within a struct pt_regs, recording the registe= rs at + * an arbitrary point in time. + * Unwinding should consume pt_regs::epc, followed by pt_regs::ra. + * + * Note: all other values are reserved and should result in unwinding + * terminating with an error. + */ +#define FRAME_META_TYPE_NONE 0 +#define FRAME_META_TYPE_FINAL 1 +#define FRAME_META_TYPE_PT_REGS 2 + +#ifndef __ASSEMBLER__ +/* + * A standard RISC-V frame record. + */ +struct frame_record { + unsigned long fp; + unsigned long ra; +}; + +/* + * A metadata frame record indicating a special unwind. + * The record::{fp,ra} fields must be zero to indicate the presence of + * metadata. + */ +struct frame_record_meta { + struct frame_record record; + unsigned long type; +}; +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_RISCV_STACKTRACE_FRAME_H */ diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offset= s.c index a75f0cfea1e9f..d0eaf2c902fb2 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -131,6 +131,9 @@ void asm_offsets(void) OFFSET(PT_BADADDR, pt_regs, badaddr); OFFSET(PT_CAUSE, pt_regs, cause); =20 + DEFINE(S_STACKFRAME, offsetof(struct pt_regs, stackframe)); + DEFINE(S_STACKFRAME_TYPE, offsetof(struct pt_regs, stackframe.type)); + OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); =20 OFFSET(HIBERN_PBE_ADDR, pbe, address); @@ -503,6 +506,9 @@ void asm_offsets(void) DEFINE(STACKFRAME_SIZE_ON_STACK, ALIGN(sizeof(struct stackframe), STACK_A= LIGN)); DEFINE(STACKFRAME_FP, offsetof(struct stackframe, fp) - sizeof(struct sta= ckframe)); DEFINE(STACKFRAME_RA, offsetof(struct stackframe, ra) - sizeof(struct sta= ckframe)); + DEFINE(STACKFRAME_RECORD_SIZE, sizeof(struct frame_record)); + OFFSET(FRAME_RECORD_FP, frame_record, fp); + OFFSET(FRAME_RECORD_RA, frame_record, ra); #ifdef CONFIG_FUNCTION_TRACER DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func)); #ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index d799c4e56f804..588d5bbe74f56 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -198,6 +199,27 @@ SYM_CODE_START(handle_exception) REG_S s4, PT_CAUSE(sp) REG_S s5, PT_TP(sp) =20 + /* + * Create a metadata frame record. The unwinder will use this to + * identify and unwind exception boundaries. + */ + REG_S zero, (S_STACKFRAME + FRAME_RECORD_FP)(sp) /* stackframe.record.fp = =3D 0 */ + REG_S zero, (S_STACKFRAME + FRAME_RECORD_RA)(sp) /* stackframe.record.ra = =3D 0 */ +#ifdef CONFIG_RISCV_M_MODE + li t0, SR_MPP + and t0, s1, t0 +#else + andi t0, s1, SR_SPP +#endif + bnez t0, 1f + li t0, FRAME_META_TYPE_FINAL + j 2f +1: + li t0, FRAME_META_TYPE_PT_REGS +2: + REG_S t0, S_STACKFRAME_TYPE(sp) + addi s0, sp, S_STACKFRAME + STACKFRAME_RECORD_SIZE + /* * Set the scratch register to 0, so that if a recursive exception * occurs, the exception vector knows it came from the kernel @@ -354,6 +376,19 @@ SYM_CODE_START_LOCAL(handle_kernel_stack_overflow) REG_S s3, PT_BADADDR(sp) REG_S s4, PT_CAUSE(sp) REG_S s5, PT_TP(sp) + + /* + * Create a metadata frame record for the overflow pt_regs. The + * overflow path is entered from kernel context, so this is a nested + * pt_regs boundary and the unwinder can resume from the pre-overflow + * frame pointer saved in PT_S0. + */ + REG_S zero, (S_STACKFRAME + FRAME_RECORD_FP)(sp) + REG_S zero, (S_STACKFRAME + FRAME_RECORD_RA)(sp) + li t0, FRAME_META_TYPE_PT_REGS + REG_S t0, S_STACKFRAME_TYPE(sp) + addi s0, sp, S_STACKFRAME + STACKFRAME_RECORD_SIZE + move a0, sp tail handle_bad_stack SYM_CODE_END(handle_kernel_stack_overflow) @@ -362,8 +397,8 @@ ASM_NOKPROBE(handle_kernel_stack_overflow) =20 SYM_CODE_START(ret_from_fork_kernel_asm) call schedule_tail - move a0, s1 /* fn_arg */ - move a1, s0 /* fn */ + move a0, s3 /* fn_arg */ + move a1, s2 /* fn */ move a2, sp /* pt_regs */ call ret_from_fork_kernel j ret_from_exception diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index f6a8ca49e6277..341b2d3facbc0 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "efi-header.S" =20 @@ -177,6 +178,14 @@ secondary_start_sbi: REG_S a0, (a1) 1: #endif + + /* + * Set up the frame pointer for the secondary idle task so reliable + * stack unwinding terminates at the metadata frame in task_pt_regs(). + * Without this, the first frame records can inherit an undefined caller + * fp and unwind past smp_callin() into .Lsecondary_park. + */ + addi s0, sp, S_STACKFRAME + STACKFRAME_RECORD_SIZE scs_load_current call smp_callin #endif /* CONFIG_SMP */ @@ -305,6 +314,20 @@ SYM_CODE_START(_start_kernel) la tp, init_task la sp, init_thread_union + THREAD_SIZE addi sp, sp, -PT_SIZE_ON_STACK + + /* + * Set up a metadata frame record for the init task so that + * the unwinder can identify the outermost frame by its + * {fp, ra} =3D {0, 0} sentinel at the bottom of pt_regs. + * fp/s0 points above the metadata record (RISC-V + * convention). + */ + REG_S zero, (S_STACKFRAME + FRAME_RECORD_FP)(sp) + REG_S zero, (S_STACKFRAME + FRAME_RECORD_RA)(sp) + li t0, FRAME_META_TYPE_FINAL + REG_S t0, S_STACKFRAME_TYPE(sp) + addi s0, sp, S_STACKFRAME + STACKFRAME_RECORD_SIZE + #if defined(CONFIG_RISCV_SBI) && defined(CONFIG_RISCV_USER_CFI) li a7, SBI_EXT_FWFT li a6, SBI_EXT_FWFT_SET diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index b2df7f72241a5..0dc90bf7a6524 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -258,8 +258,23 @@ int copy_thread(struct task_struct *p, const struct ke= rnel_clone_args *args) /* Supervisor/Machine, irqs on: */ childregs->status =3D SR_PP | SR_PIE; =20 - p->thread.s[0] =3D (unsigned long)args->fn; - p->thread.s[1] =3D (unsigned long)args->fn_arg; + /* + * Set up a metadata frame record at the bottom of the + * stack for the unwinder. Use FRAME_META_TYPE_FINAL + * since this is the outermost kernel entry for the new + * task. The frame_record::{fp,ra} are already zero from + * memset(). + * + * fp/s0 points above the metadata record (RISC-V + * convention). fn and fn_arg are passed via s2/s3, + * keeping s0 available for the frame pointer chain. + */ + childregs->stackframe.type =3D FRAME_META_TYPE_FINAL; + + p->thread.s[0] =3D (unsigned long)(&childregs->stackframe) + + sizeof(struct frame_record); + p->thread.s[2] =3D (unsigned long)args->fn; + p->thread.s[3] =3D (unsigned long)args->fn_arg; p->thread.ra =3D (unsigned long)ret_from_fork_kernel_asm; } else { /* allocate new shadow stack if needed. In case of CLONE_VM we have to */ @@ -278,6 +293,20 @@ int copy_thread(struct task_struct *p, const struct ke= rnel_clone_args *args) if (clone_flags & CLONE_SETTLS) childregs->tp =3D tls; childregs->a0 =3D 0; /* Return value of fork() */ + + /* + * Set up the unwind boundary: ensure the metadata + * frame record has its {fp,ra} sentinel zeroed and + * point fp/s0 above the metadata record. Mark it as + * FINAL since this is the outermost kernel entry for + * the new task. + */ + childregs->stackframe.record.fp =3D 0; + childregs->stackframe.record.ra =3D 0; + childregs->stackframe.type =3D FRAME_META_TYPE_FINAL; + p->thread.s[0] =3D (unsigned long)(&childregs->stackframe) + + sizeof(struct frame_record); + p->thread.ra =3D (unsigned long)ret_from_fork_user_asm; } p->thread.riscv_v_flags =3D 0; --=20 2.39.3 From nobody Fri Sep 25 10:37:47 2026 Received: from out30-118.freemail.mail.aliyun.com (out30-118.freemail.mail.aliyun.com [115.124.30.118]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C112312834; Mon, 14 Sep 2026 09:27:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378029; cv=none; b=OHEc5C5wGQuCF5vUP1TnnH0u/d5rv+TLq84nfhrePFRb/vweKyFY7K0nizS74fJksEt0n/e/LN5G5iDhy2yPcVkbMoiDGFljViCYuz2tyZ5hlgLGBn0gm/XJ5BJu2/66OB+tAc+SaVSii2dXVgUNHcsRWb5Wi2KkHQLQgRsFTVI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378029; c=relaxed/simple; bh=Xoe2g6PaoXjWDLYmYUO8CH8/g5+83BgHMb3DCEelxa8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YRkdaNQtN3HRrCQWDCHpaui44tN1WXBsNNwdbekYkJps3IT/j5e75hPsXbdRNoolO9EQG8yIEBr4qXDasjaCIHLgl4/Qtxk84NVpUK+/JQliS5iXbgCZJKVfCDn3e/AVWESuy8g2T8d51m7y/eXtWt1mllvxqM3qvSs4gn89riU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=LHt4O1Qs; arc=none smtp.client-ip=115.124.30.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="LHt4O1Qs" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378018; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=LEtjDojkW0StONXmozHIm/bJ7xi86aarazNel8UTP5g=; b=LHt4O1QsC2b/10u+LhnwEZyR6Uyo4IdKotw28c6E8PTzpYfV2TP+d4kVTb434ueAg02MWz44gK8ucC9fHcjmRmJeFqPpQ2hA7Pu9wLngassgNLctEi2c0niKszXC0E5Ng7EZEqvL5GhFs5POROjvGMf+fOMZ9XS1ltJsSNGQvFo= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R791e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033045133197;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-n8_1789378014; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-n8_1789378014 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:26:56 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 2/7] riscv: stacktrace: disable KASAN and KCOV instrumentation for stacktrace.o Date: Mon, 14 Sep 2026 17:26:43 +0800 Message-Id: <20260914092648.51254-3-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han KASAN records stack traces for every alloc/free, which means it walks the unwinder very frequently. Instrumenting the stack trace collection code itself adds substantial overhead and makes the traces themselves noisier. KCOV instruments every basic-block edge. The unwinder is a hot path, especially with KASAN enabled, so KCOV instrumentation has the same kind of cost and noise problem here. Mark stacktrace.o as not KASAN- or KCOV-instrumented, matching the x86 treatment of its stack unwinding code. RISC-V keeps the relevant unwinder code in stacktrace.o, so a single translation-unit annotation covers the equivalent scope. This is a prerequisite preference for the upcoming reliable unwinder, but the change is valid on its own. Reviewed-by: Shuai Xue Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- arch/riscv/kernel/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index ebe1c3588177b..bf4645bb52403 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -44,6 +44,12 @@ CFLAGS_REMOVE_return_address.o =3D $(CC_FLAGS_FTRACE) CFLAGS_REMOVE_sbi_ecall.o =3D $(CC_FLAGS_FTRACE) endif =20 +# When KASAN is enabled, a stack trace is recorded for every alloc/free, w= hich +# can significantly impact performance. Avoid instrumenting the stack trace +# collection code to minimize this impact. +KASAN_SANITIZE_stacktrace.o :=3D n +KCOV_INSTRUMENT_stacktrace.o :=3D n + always-$(KBUILD_BUILTIN) +=3D vmlinux.lds =20 obj-y +=3D head.o --=20 2.39.3 From nobody Fri Sep 25 10:37:47 2026 Received: from out30-131.freemail.mail.aliyun.com (out30-131.freemail.mail.aliyun.com [115.124.30.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2A2F341DEC9; Mon, 14 Sep 2026 09:27:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378034; cv=none; b=e0nPosGGzRFbfYcxP42fxjWLhwCWJ/BdhDnnIrXxKQdBZgJcR+Cgb3SIb+R7iR+9ogzaOdTq8qjK1gkCtBYo0skm7IzEPH715/OFwMzRJMFOLqB57uxlS33XwzZSsZBt9csjAvNWqYtxOI1zwTh+likS8gJy6kN9IjBHu5a0G0c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378034; c=relaxed/simple; bh=/BKgtVOuQ2qgw22qpzAOkHztTTsjeX3prCxWtKLlTac=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=N/jzfalkXErtPM0tVi5Mtw68YOCZYO+2w6Z9yl+RK0n37L+VtIVO7ioLZ8RFnlKVJA45nITHThdPIA+HLh9N4NfHsxa+t35yjtVUlKTG95oF+35KjqjrTmm8P+rPII0HobgLe5c53XEdldR0Z8xHF7JiX0hqEalZVc32NupJyv8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=i8r8UpHA; arc=none smtp.client-ip=115.124.30.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="i8r8UpHA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378021; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=jYjFQWHS7zv+XprgIgV3x6FJiEuRmhHyfb5LEr80eCs=; b=i8r8UpHA7n83OVKbEGQW69I7yiO3HbNaB53aua/mFu9SXmjZEEVo4DoHzvTomW+Tz6tW/irMUFLiaDCro+aKZVPhpHBa56Bm6WLXtPc1G51ffLoS83cWx81hVb+L6TForVLerHfCdN/jMytBzln1m7kTiP/NbQ6RjTIGGv0oHyM= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R111e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033032089153;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-nk_1789378017; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-nk_1789378017 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:26:58 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 3/7] riscv: ftrace: always preserve s0 in dynamic ftrace register frame Date: Mon, 14 Sep 2026 17:26:44 +0800 Message-Id: <20260914092648.51254-4-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han struct __arch_ftrace_regs declares s0 unconditionally, and both ftrace_regs_get_frame_pointer() and ftrace_partial_regs() read it unconditionally. But the SAVE_ABI_REGS / RESTORE_ABI_REGS macros in mcount-dyn.S only stored s0 under HAVE_FUNCTION_GRAPH_FP_TEST (CONFIG_FUNCTION_GRAPH_TRACER && CONFIG_FRAME_POINTER). With CONFIG_FRAME_POINTER=3Dn the slot held whatever was on the stack before, so any callback going through ftrace_partial_regs() saw a garbage regs->s0. RISC-V kernels default to FRAME_POINTER=3Dy, which is why this has not bitten in practice. Save and restore s0 unconditionally in the dynamic ftrace ABI register frame. This fixes the latent garbage-s0 case, brings the dynamic ftrace path in line with the static _mcount path (mcount.S SAVE_ABI_STATE already saves s0 unconditionally), and matches the frame layout already documented in the comment above SAVE_ABI_REGS. It is also a prerequisite for the upcoming reliable unwinder, which reads ftrace_regs_get_frame_pointer(fregs) directly. The cost is one extra REG_S/REG_L pair per traced call, negligible compared to the overall ftrace cost; the existing FREGS_SIZE_ON_STACK already reserved the slot, so no extra stack space is used. Reviewed-by: Shuai Xue Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- arch/riscv/kernel/mcount-dyn.S | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/riscv/kernel/mcount-dyn.S b/arch/riscv/kernel/mcount-dyn.S index 082fe0b0e3c08..26c55fba8fec7 100644 --- a/arch/riscv/kernel/mcount-dyn.S +++ b/arch/riscv/kernel/mcount-dyn.S @@ -85,9 +85,7 @@ addi sp, sp, -FREGS_SIZE_ON_STACK REG_S t0, FREGS_EPC(sp) REG_S x1, FREGS_RA(sp) -#ifdef HAVE_FUNCTION_GRAPH_FP_TEST REG_S x8, FREGS_S0(sp) -#endif REG_S x6, FREGS_T1(sp) #ifdef CONFIG_CC_IS_CLANG REG_S x7, FREGS_T2(sp) @@ -113,9 +111,7 @@ .macro RESTORE_ABI_REGS REG_L t0, FREGS_EPC(sp) REG_L x1, FREGS_RA(sp) -#ifdef HAVE_FUNCTION_GRAPH_FP_TEST REG_L x8, FREGS_S0(sp) -#endif REG_L x6, FREGS_T1(sp) #ifdef CONFIG_CC_IS_CLANG REG_L x7, FREGS_T2(sp) --=20 2.39.3 From nobody Fri Sep 25 10:37:47 2026 Received: from out30-99.freemail.mail.aliyun.com (out30-99.freemail.mail.aliyun.com [115.124.30.99]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 67378426D13; Mon, 14 Sep 2026 09:27:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.99 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378034; cv=none; b=svPP/JzeeJpqf6PLjvNwrSx4sP2LFSttS9ZhdESgOF5j8bqO/5/GiRAImvdBz9zKb/gcVbP26e8TUWKu2LPWLJujuCtMNfftSPE3PwH4W3kHe/Mk665gNUI7AzyHY9ZRJotOhnjBU3yf2CWLWsPdx1evYLlnXv8+jaDZKCuvh0k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378034; c=relaxed/simple; bh=wCUJtu8nvhUif8bfK10o0kVFbDPsjt1KiXszm1rB2F4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=g7RNqL/IAMwiW97SZiLnTdYFSPVPDhViZmx0hFGA0q1z3b5vrUSARyl2FH1EtOLrINT9e9rVSw45Lhrzip8R9GSnrosgybsxQhOzM8Ng99YSuOWgEXxKUMj5E57YP47IL8Pl2CYMVJhR6tU91I78GI5gbkZGoz5b5WRBiSVDzN8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=VMCwis0D; arc=none smtp.client-ip=115.124.30.99 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="VMCwis0D" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378023; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=+43XU7yf7PEQpqiYtJXs0E7cxv0tV90frGvpPtvvpDc=; b=VMCwis0DBzVuzmRGA6bSpDLiFXIXKZt0z17cNB8529ewAKw/WAjG8GVCaN0E++R7GsFakd2lYHDyXnVhm/Z2P3p7XwfktF1NUbitbxUr90RiUwKg81q7I91T5O13GYA4PLwndhyecaHhwUYj1/H/vTOk9W+Kj1XPTsyoi5mg7Io= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R831e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033032089153;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-oF_1789378019; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-oF_1789378019 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:27:01 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 4/7] riscv: stacktrace: introduce stack-bound tracking helpers Date: Mon, 14 Sep 2026 17:26:45 +0800 Message-Id: <20260914092648.51254-5-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han A reliable unwinder needs to validate that every frame record it reads is fully contained in a known kernel stack, and it needs to refuse to walk back into a stack it has already left. Add the building blocks for that: * struct stack_info / struct unwind_state in a new asm/stacktrace/common.h, modelled on the arm64 reference implementation. * stackinfo_get_irq() / stackinfo_get_task() / stackinfo_get_overflow() plus the corresponding on_*_stack() predicates in asm/stacktrace.h, so callers can ask "is this object on stack X?" by stack kind rather than open-coded address arithmetic. * unwind_init_common(), unwind_find_stack() and unwind_consume_stack() helpers that enforce the forward-progress-only invariant required for reliability. No existing user is wired up to these helpers in this commit; the unwinder switch comes in a follow-up. The header changes leave on_thread_stack() with the same semantics as before, just expressed in terms of the new helpers. Reviewed-by: Shuai Xue Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- arch/riscv/include/asm/stacktrace.h | 67 ++++++++- arch/riscv/include/asm/stacktrace/common.h | 159 +++++++++++++++++++++ 2 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 arch/riscv/include/asm/stacktrace/common.h diff --git a/arch/riscv/include/asm/stacktrace.h b/arch/riscv/include/asm/s= tacktrace.h index b1495a7e06ce6..df4d022f64304 100644 --- a/arch/riscv/include/asm/stacktrace.h +++ b/arch/riscv/include/asm/stacktrace.h @@ -3,8 +3,13 @@ #ifndef _ASM_RISCV_STACKTRACE_H #define _ASM_RISCV_STACKTRACE_H =20 +#include #include +#include + +#include #include +#include =20 struct stackframe { unsigned long fp; @@ -16,14 +21,72 @@ extern void notrace walk_stackframe(struct task_struct = *task, struct pt_regs *re extern void dump_backtrace(struct pt_regs *regs, struct task_struct *task, const char *loglvl); =20 -static inline bool on_thread_stack(void) +/* + * IRQ stack accessors + */ +#ifdef CONFIG_IRQ_STACKS +static inline struct stack_info stackinfo_get_irq(void) +{ + unsigned long low =3D (unsigned long)raw_cpu_read(irq_stack_ptr); + unsigned long high =3D low + IRQ_STACK_SIZE; + + return (struct stack_info) { + .low =3D low, + .high =3D high, + }; +} + +static inline bool on_irq_stack(unsigned long sp, unsigned long size) +{ + struct stack_info info =3D stackinfo_get_irq(); + + return stackinfo_on_stack(&info, sp, size); +} +#endif /* CONFIG_IRQ_STACKS */ + +/* + * Task stack accessors + */ +static inline struct stack_info stackinfo_get_task(const struct task_struc= t *tsk) { - return !(((unsigned long)(current->stack) ^ current_stack_pointer) & ~(TH= READ_SIZE - 1)); + unsigned long low =3D (unsigned long)task_stack_page(tsk); + unsigned long high =3D low + THREAD_SIZE; + + return (struct stack_info) { + .low =3D low, + .high =3D high, + }; +} + +static inline bool on_task_stack(const struct task_struct *tsk, + unsigned long sp, unsigned long size) +{ + struct stack_info info =3D stackinfo_get_task(tsk); + + return stackinfo_on_stack(&info, sp, size); } =20 +/* + * Cast is necessary since current->stack is an opaque ptr. + */ +#define on_thread_stack() (on_task_stack(current, current_stack_pointer, 1= )) =20 +/* + * Overflow stack accessors + */ #ifdef CONFIG_VMAP_STACK DECLARE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow= _stack); + +static inline struct stack_info stackinfo_get_overflow(void) +{ + unsigned long low =3D (unsigned long)raw_cpu_ptr(overflow_stack); + unsigned long high =3D low + OVERFLOW_STACK_SIZE; + + return (struct stack_info) { + .low =3D low, + .high =3D high, + }; +} #endif /* CONFIG_VMAP_STACK */ =20 #endif /* _ASM_RISCV_STACKTRACE_H */ diff --git a/arch/riscv/include/asm/stacktrace/common.h b/arch/riscv/includ= e/asm/stacktrace/common.h new file mode 100644 index 0000000000000..360a26e34349c --- /dev/null +++ b/arch/riscv/include/asm/stacktrace/common.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * RISC-V common stack unwinder types and helpers. + * + * See: arch/arm64/include/asm/stacktrace/common.h for the reference + * implementation. + * + * Copyright (C) 2026 + */ +#ifndef __ASM_RISCV_STACKTRACE_COMMON_H +#define __ASM_RISCV_STACKTRACE_COMMON_H + +#include +#include +#include + +#include + +/** + * struct stack_info - describes the bounds of a stack. + * + * @low: The lowest valid address on the stack. + * @high: The highest valid address on the stack. + */ +struct stack_info { + unsigned long low; + unsigned long high; +}; + +/** + * struct unwind_state - state used for robust unwinding. + * + * @fp: The fp value in the frame record (or the real fp). + * @pc: The ra value in the frame record (or the real ra). + * + * @stack: The stack currently being unwound. + * @stacks: An array of stacks which can be unwound. + * @nr_stacks: The number of stacks in @stacks. + */ +struct unwind_state { + unsigned long fp; + unsigned long pc; + + struct stack_info stack; + struct stack_info *stacks; + int nr_stacks; +}; + +/** + * stackinfo_get_unknown() - Get an unknown stack_info. + * + * Return: a stack_info with low and high set to 0. + */ +static inline struct stack_info stackinfo_get_unknown(void) +{ + return (struct stack_info) { + .low =3D 0, + .high =3D 0, + }; +} + +/** + * stackinfo_on_stack() - Check whether an object is fully within a stack. + * + * @info: The stack to check against. + * @sp: The base address of the object. + * @size: The size of the object. + * + * Return: true if the object is fully contained within the stack. + */ +static inline bool stackinfo_on_stack(const struct stack_info *info, + unsigned long sp, unsigned long size) +{ + if (!info->low) + return false; + + if (sp < info->low || sp + size < sp || sp + size > info->high) + return false; + + return true; +} + +/** + * unwind_init_common() - Initialize the common parts of the unwind state. + * + * @state: the unwind state to initialize. + */ +static inline void unwind_init_common(struct unwind_state *state) +{ + state->stack =3D stackinfo_get_unknown(); +} + +/** + * unwind_find_stack() - Find the accessible stack which entirely contains= an + * object. + * + * @state: the current unwind state. + * @sp: the base address of the object. + * @size: the size of the object. + * + * Return: a pointer to the relevant stack_info if found; NULL otherwise. + */ +static inline struct stack_info *unwind_find_stack(struct unwind_state *st= ate, + unsigned long sp, + unsigned long size) +{ + struct stack_info *info =3D &state->stack; + + if (stackinfo_on_stack(info, sp, size)) + return info; + + for (int i =3D 0; i < state->nr_stacks; i++) { + info =3D &state->stacks[i]; + if (stackinfo_on_stack(info, sp, size)) + return info; + } + + return NULL; +} + +/** + * unwind_consume_stack() - Update stack boundaries so that future unwind = steps + * cannot consume this object again. + * + * @state: the current unwind state. + * @info: the stack_info of the stack containing the object. + * @sp: the base address of the object. + * @size: the size of the object. + * + * Stack transitions are strictly one-way, and once we've + * transitioned from one stack to another, it's never valid to + * unwind back to the old stack. + * + * Note that stacks can nest in several valid orders, e.g. + * + * TASK -> IRQ -> OVERFLOW + * + * ... so we do not check the specific order of stack + * transitions. + */ +static inline void unwind_consume_stack(struct unwind_state *state, + struct stack_info *info, + unsigned long sp, + unsigned long size) +{ + struct stack_info tmp; + + tmp =3D *info; + *info =3D stackinfo_get_unknown(); + state->stack =3D tmp; + + /* + * Future unwind steps can only consume stack above this frame record. + * Update the current stack to start immediately above it. + */ + state->stack.low =3D sp + size; +} + +#endif /* __ASM_RISCV_STACKTRACE_COMMON_H */ --=20 2.39.3 From nobody Fri Sep 25 10:37:47 2026 Received: from out30-113.freemail.mail.aliyun.com (out30-113.freemail.mail.aliyun.com [115.124.30.113]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B830A42E401; Mon, 14 Sep 2026 09:27:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.113 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378043; cv=none; b=LbSNGNHfGFYlCU48EpLlZee7iKnAevTvBDv0J+Oup8SPwDZtiaSKXy5cZgmWg0CO75e/QPaSJOZyLJrcFjMkX39ufmmzFxETAWB6vNoyjdRw4CAWZdaIoG8G8X4n757kYGCw9CPn/fjdPw5u0t0rZY24EENN9q7NhgJyi1YoaJs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378043; c=relaxed/simple; bh=ANNRCwPD6zhf87eScOPy4QwWeFvcd/OOgssUjry83L8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=PpqvI9mQ5g0rmAJE/Uyg3G8SvKeDCpPhr6A6WaoE0/zAIeNt2lHHg5j157qnfkDdhIIe6hSAU+1ff6pR8LcjoCqw1nNvLRe97GMm2mzyEhedSmg3qD6eZRzcYJNZRt8xO+pg4+Y4aE/Ym1uhqT46ssFBd4lHb8fLgpuuv25a5gk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=BxWdBcIE; arc=none smtp.client-ip=115.124.30.113 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="BxWdBcIE" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378027; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=AQFhP47l8rFzilFqOvieFgth2jRNrpzFYg9Ga06WTho=; b=BxWdBcIE9K/ROxCOZh4occTBMgQIoBYmxYFdvdcCdZ4nBgeqwrf0XOTqsOangeJ6sbZO8kjoLyPgupWZOcSZsIL58D0KCltNplm+QDi2kex5qorcWYXXH7VhSHrJHdjgyj9IeY1LajS+FI4ZHf8fAAS+fpTYf4NHr6Vu8uYJKWM= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R141e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033037026112;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-pA_1789378022; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-pA_1789378022 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:27:03 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 5/7] riscv: stacktrace: switch to frame-pointer based unwinder Date: Mon, 14 Sep 2026 17:26:46 +0800 Message-Id: <20260914092648.51254-6-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han Replace the open-coded frame-pointer walker in arch_stack_walk() with a robust kunwind state machine, modelled on arch/arm64/kernel/stacktrace.c and retargeted to the RISC-V {fp, ra} frame record convention. The new walker tracks stack bounds, consumes frame records monotonically, understands the metadata pt_regs records added in the previous frame record metadata patch, and recovers return addresses replaced by function graph tracing and kretprobes. This commit introduces arch_stack_walk_reliable() but does not yet select HAVE_RELIABLE_STACKTRACE; that is done in a follow-up Kconfig patch so this commit can be reviewed and bisected as a pure unwinder replacement. Until that Kconfig change lands, livepatch is not yet enabled and arch_stack_walk_reliable() has no in-tree caller. Three related callers are updated to keep the same frame-record assumptions everywhere: * Function graph tracing: the old RISC-V unwinder matched function graph return-stack entries by the saved return-address slot. That was consistent with the static mcount path, but not with the dynamic ftrace path where the parent slot is ftrace_regs::ra. Use the architectural frame pointer as the function graph return-address cookie, matching the kunwind walker. * Perf callchains: route kernel callchain collection through arch_stack_walk() so perf sees the same frame-pointer unwind behaviour as dump_stack() and the upcoming livepatch path. * dump_backtrace() / __get_wchan() / show_stack(): these now go through arch_stack_walk(); the explicit "Call Trace:" header is moved into dump_backtrace() to preserve the original output. The non-frame-pointer fallback walker is kept untouched for !CONFIG_FRAME_POINTER builds. Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- arch/riscv/kernel/ftrace.c | 6 +- arch/riscv/kernel/perf_callchain.c | 2 +- arch/riscv/kernel/stacktrace.c | 561 ++++++++++++++++++++++++----- 3 files changed, 473 insertions(+), 96 deletions(-) diff --git a/arch/riscv/kernel/ftrace.c b/arch/riscv/kernel/ftrace.c index be8b685144174..0facd824c911d 100644 --- a/arch/riscv/kernel/ftrace.c +++ b/arch/riscv/kernel/ftrace.c @@ -245,7 +245,8 @@ void prepare_ftrace_return(unsigned long *parent, unsig= ned long self_addr, */ old =3D *parent; =20 - if (!function_graph_enter(old, self_addr, frame_pointer, parent)) + if (!function_graph_enter(old, self_addr, frame_pointer, + (void *)frame_pointer)) *parent =3D return_hooker; } =20 @@ -267,7 +268,8 @@ void ftrace_graph_func(unsigned long ip, unsigned long = parent_ip, */ old =3D *parent; =20 - if (!function_graph_enter_regs(old, ip, frame_pointer, parent, fregs)) + if (!function_graph_enter_regs(old, ip, frame_pointer, + (void *)frame_pointer, fregs)) *parent =3D return_hooker; } #endif /* CONFIG_DYNAMIC_FTRACE */ diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_ca= llchain.c index b465bc9eb870e..436af96ea59ca 100644 --- a/arch/riscv/kernel/perf_callchain.c +++ b/arch/riscv/kernel/perf_callchain.c @@ -44,5 +44,5 @@ void perf_callchain_kernel(struct perf_callchain_entry_ct= x *entry, return; } =20 - walk_stackframe(NULL, regs, fill_callchain, entry); + arch_stack_walk(fill_callchain, entry, NULL, regs); } diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index c7555447149bf..0ce0cf0da6060 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -11,98 +11,16 @@ #include #include #include +#include +#include =20 #include =20 -#ifdef CONFIG_FRAME_POINTER - /* - * This disables KASAN checking when reading a value from another task's s= tack, - * since the other task could be running on another CPU and could have poi= soned - * the stack in the meantime. + * Non-frame-pointer fallback unwinder. + * Only compiled when CONFIG_FRAME_POINTER is not enabled. */ -#define READ_ONCE_TASK_STACK(task, x) \ -({ \ - unsigned long val; \ - unsigned long addr =3D x; \ - if ((task) =3D=3D current) \ - val =3D READ_ONCE(addr); \ - else \ - val =3D READ_ONCE_NOCHECK(addr); \ - val; \ -}) - -extern asmlinkage void handle_exception(void); -extern unsigned long ret_from_exception_end; - -static inline int fp_is_valid(unsigned long fp, unsigned long sp) -{ - unsigned long low, high; - - low =3D sp + sizeof(struct stackframe); - high =3D ALIGN(sp, THREAD_SIZE); - - return !(fp < low || fp > high || fp & 0x07); -} - -void notrace walk_stackframe(struct task_struct *task, struct pt_regs *reg= s, - bool (*fn)(void *, unsigned long), void *arg) -{ - unsigned long fp, sp, pc; - int graph_idx =3D 0; - int level =3D 0; - - if (regs) { - fp =3D frame_pointer(regs); - sp =3D user_stack_pointer(regs); - pc =3D instruction_pointer(regs); - } else if (task =3D=3D NULL || task =3D=3D current) { - fp =3D (unsigned long)__builtin_frame_address(0); - sp =3D current_stack_pointer; - pc =3D (unsigned long)walk_stackframe; - level =3D -1; - } else { - /* task blocked in __switch_to */ - fp =3D task->thread.s[0]; - sp =3D task->thread.sp; - pc =3D task->thread.ra; - } - - for (;;) { - struct stackframe *frame; - - if (unlikely(!__kernel_text_address(pc) || (level++ >=3D 0 && !fn(arg, p= c)))) - break; - - if (unlikely(!fp_is_valid(fp, sp))) - break; - - /* Unwind stack frame */ - frame =3D (struct stackframe *)fp - 1; - sp =3D fp; - if (regs && (regs->epc =3D=3D pc) && fp_is_valid(frame->ra, sp)) { - /* We hit function where ra is not saved on the stack */ - fp =3D frame->ra; - pc =3D regs->ra; - } else { - fp =3D READ_ONCE_TASK_STACK(task, frame->fp); - pc =3D READ_ONCE_TASK_STACK(task, frame->ra); - pc =3D ftrace_graph_ret_addr(task, &graph_idx, pc, - &frame->ra); - if (pc >=3D (unsigned long)handle_exception && - pc < (unsigned long)&ret_from_exception_end) { - if (unlikely(!fn(arg, pc))) - break; - - pc =3D ((struct pt_regs *)sp)->epc; - fp =3D ((struct pt_regs *)sp)->s0; - } - } - - } -} - -#else /* !CONFIG_FRAME_POINTER */ +#ifndef CONFIG_FRAME_POINTER =20 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, bool (*fn)(void *, unsigned long), void *arg) @@ -133,7 +51,12 @@ void notrace walk_stackframe(struct task_struct *task, } } =20 -#endif /* CONFIG_FRAME_POINTER */ +#endif /* !CONFIG_FRAME_POINTER */ + +/* + * Common trace helpers. + * These are used by both the FP (kunwind) and non-FP (walk_stackframe) pa= ths. + */ =20 static bool print_trace_address(void *arg, unsigned long pc) { @@ -146,12 +69,12 @@ static bool print_trace_address(void *arg, unsigned lo= ng pc) noinline void dump_backtrace(struct pt_regs *regs, struct task_struct *tas= k, const char *loglvl) { - walk_stackframe(task, regs, print_trace_address, (void *)loglvl); + printk("%sCall Trace:\n", loglvl); + arch_stack_walk(print_trace_address, (void *)loglvl, task, regs); } =20 void show_stack(struct task_struct *task, unsigned long *sp, const char *l= oglvl) { - pr_cont("%sCall Trace:\n", loglvl); dump_backtrace(NULL, task, loglvl); } =20 @@ -171,17 +94,469 @@ unsigned long __get_wchan(struct task_struct *task) =20 if (!try_get_task_stack(task)) return 0; - walk_stackframe(task, NULL, save_wchan, &pc); + arch_stack_walk(save_wchan, &pc, task, NULL); put_task_stack(task); return pc; } =20 -noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry= , void *cookie, - struct task_struct *task, struct pt_regs *regs) +/* + * Frame-pointer-based kernel unwind infrastructure. + * Only compiled when CONFIG_FRAME_POINTER is enabled. + * + * See: arch/arm64/kernel/stacktrace.c for the reference implementation. + */ +#ifdef CONFIG_FRAME_POINTER + +/* + * Per-cpu stacks are only accessible when unwinding the current task in a + * non-preemptible context. + */ +#define STACKINFO_CPU(task, name) \ + ({ \ + (((task) =3D=3D current) && !preemptible()) \ + ? stackinfo_get_##name() \ + : stackinfo_get_unknown(); \ + }) + +enum kunwind_source { + KUNWIND_SOURCE_UNKNOWN, + KUNWIND_SOURCE_FRAME, + KUNWIND_SOURCE_CALLER, + KUNWIND_SOURCE_TASK, + KUNWIND_SOURCE_REGS_PC, +}; + +union unwind_flags { + unsigned long all; + struct { + unsigned long fgraph : 1, + kretprobe : 1; + }; +}; + +/* + * Kernel unwind state + * + * @common: Common unwind state. + * @task: The task being unwound. + * @graph_idx: Used by ftrace_graph_ret_addr() for optimized stack unwindi= ng. + * @kr_cur: When KRETPROBES is selected, holds the kretprobe instance + * associated with the most recently encountered replacement ra + * value. + */ +struct kunwind_state { + struct unwind_state common; + struct task_struct *task; + int graph_idx; +#ifdef CONFIG_KRETPROBES + struct llist_node *kr_cur; +#endif + enum kunwind_source source; + union unwind_flags flags; + struct pt_regs *regs; +}; + +static __always_inline void +kunwind_init(struct kunwind_state *state, + struct task_struct *task) +{ + unwind_init_common(&state->common); + state->task =3D task; + state->source =3D KUNWIND_SOURCE_UNKNOWN; + state->flags.all =3D 0; + state->regs =3D NULL; +} + +/* + * Start an unwind from a pt_regs. + * + * The unwind will begin at the PC within the regs. + * + * The regs must be on a stack currently owned by the calling task. + */ +static __always_inline void +kunwind_init_from_regs(struct kunwind_state *state, + struct pt_regs *regs) +{ + kunwind_init(state, current); + + state->regs =3D regs; + state->common.fp =3D frame_pointer(regs); + state->common.pc =3D instruction_pointer(regs); + state->source =3D KUNWIND_SOURCE_REGS_PC; +} + +/* + * Start an unwind from a caller. + * + * The unwind will begin at the caller of whichever function this is inlin= ed + * into. + * + * The function which invokes this must be noinline. + */ +static __always_inline void +kunwind_init_from_caller(struct kunwind_state *state) +{ + unsigned long fp =3D (unsigned long)__builtin_frame_address(0); + struct frame_record *record =3D (struct frame_record *)fp - 1; + + kunwind_init(state, current); + + state->common.fp =3D READ_ONCE(record->fp); + state->common.pc =3D READ_ONCE(record->ra); + state->source =3D KUNWIND_SOURCE_CALLER; +} + +/* + * Start an unwind from a blocked task. + * + * The unwind will begin at the blocked task's saved PC (i.e. the caller of + * __switch_to). + * + * The caller should ensure the task is blocked in __switch_to for the + * duration of the unwind, or the unwind will be bogus. It is never valid = to + * call this for the current task. + */ +static __always_inline void +kunwind_init_from_task(struct kunwind_state *state, + struct task_struct *task) +{ + kunwind_init(state, task); + + state->common.fp =3D task->thread.s[0]; + state->common.pc =3D task->thread.ra; + state->source =3D KUNWIND_SOURCE_TASK; +} + +static __always_inline int +kunwind_recover_return_address(struct kunwind_state *state) +{ +#ifdef CONFIG_FUNCTION_GRAPH_TRACER + if (state->task->ret_stack && + state->common.pc =3D=3D (unsigned long)return_to_handler) { + unsigned long orig_pc; + + orig_pc =3D ftrace_graph_ret_addr(state->task, &state->graph_idx, + state->common.pc, + (void *)state->common.fp); + if (state->common.pc =3D=3D orig_pc) { + WARN_ON_ONCE(state->task =3D=3D current); + return -EINVAL; + } + state->common.pc =3D orig_pc; + state->flags.fgraph =3D 1; + } +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ + +#ifdef CONFIG_KRETPROBES + if (is_kretprobe_trampoline(state->common.pc)) { + unsigned long orig_pc; + + orig_pc =3D kretprobe_find_ret_addr(state->task, + (void *)state->common.fp, + &state->kr_cur); + if (!orig_pc) + return -EINVAL; + state->common.pc =3D orig_pc; + state->flags.kretprobe =3D 1; + } +#endif /* CONFIG_KRETPROBES */ + + return 0; +} + +/* + * When we reach an exception boundary marked by a metadata frame record, + * extract pt_regs from the stack and continue unwinding from the saved + * context (epc and s0/fp). + * + * On RISC-V, fp points above the metadata record, so the record's + * frame_record portion is at fp - sizeof(struct frame_record). + */ +static __always_inline int +kunwind_next_regs_pc(struct kunwind_state *state) +{ + struct stack_info *info; + unsigned long fp =3D state->common.fp; + struct pt_regs *regs; + + regs =3D container_of((unsigned long *)(fp - sizeof(struct frame_record)), + struct pt_regs, stackframe.record.fp); + + info =3D unwind_find_stack(&state->common, (unsigned long)regs, + sizeof(*regs)); + if (!info) + return -EINVAL; + + unwind_consume_stack(&state->common, info, (unsigned long)regs, + sizeof(*regs)); + + state->regs =3D regs; + state->common.pc =3D regs->epc; + state->common.fp =3D frame_pointer(regs); + state->source =3D KUNWIND_SOURCE_REGS_PC; + return 0; +} + +/* + * Handle a metadata frame record embedded in pt_regs. + * + * On RISC-V, fp points above the record (fp =3D metadata + 16), so the + * frame_record_meta starts at fp - sizeof(struct frame_record). + * + * FRAME_META_TYPE_FINAL: This is the outermost exception entry + * (user -> kernel). Unwinding terminates successfully. + * FRAME_META_TYPE_PT_REGS: This is a nested exception entry + * (kernel -> kernel). Continue unwinding from the saved context. + */ +static __always_inline int +kunwind_next_frame_record_meta(struct kunwind_state *state) +{ + struct task_struct *tsk =3D state->task; + unsigned long fp =3D state->common.fp; + unsigned long meta_base =3D fp - sizeof(struct frame_record); + struct frame_record_meta *meta; + struct stack_info *info; + + info =3D unwind_find_stack(&state->common, meta_base, sizeof(*meta)); + if (!info) + return -EINVAL; + + meta =3D (struct frame_record_meta *)meta_base; + switch (READ_ONCE(meta->type)) { + case FRAME_META_TYPE_FINAL: + if (meta =3D=3D &task_pt_regs(tsk)->stackframe) + return -ENOENT; + WARN_ON_ONCE(tsk =3D=3D current); + return -EINVAL; + case FRAME_META_TYPE_PT_REGS: + return kunwind_next_regs_pc(state); + default: + WARN_ON_ONCE(tsk =3D=3D current); + return -EINVAL; + } +} + +/* + * Unwind from one frame record to the next. + * + * On RISC-V, the frame record sits at fp - sizeof(struct frame_record), + * immediately below the address pointed to by fp/s0. This applies to both + * normal frame records and metadata frame records (embedded in pt_regs). + * + * A metadata record is identified by both fp and ra being zero in the + * frame_record portion, with a type value following at fp + 16. + */ +static __always_inline int +kunwind_next_frame_record(struct kunwind_state *state) +{ + unsigned long fp =3D state->common.fp; + struct frame_record *record; + struct stack_info *info; + unsigned long new_fp, new_pc; + unsigned long record_base; + + if (fp & 0x7) + return -EINVAL; + + record_base =3D fp - sizeof(*record); + + info =3D unwind_find_stack(&state->common, record_base, sizeof(*record)); + if (!info) + return -EINVAL; + + record =3D (struct frame_record *)record_base; + new_fp =3D READ_ONCE(record->fp); + new_pc =3D READ_ONCE(record->ra); + + if (!new_fp && !new_pc) + return kunwind_next_frame_record_meta(state); + + unwind_consume_stack(&state->common, info, record_base, + sizeof(*record)); + + state->common.fp =3D new_fp; + state->common.pc =3D new_pc; + state->source =3D KUNWIND_SOURCE_FRAME; + + return 0; +} + +/* + * Unwind from one frame record (A) to the next frame record (B). + * + * We terminate early if the location of B indicates a malformed chain of = frame + * records (e.g. a cycle), determined based on the location and fp value o= f A + * and the location (but not the fp value) of B. + */ +static __always_inline int +kunwind_next(struct kunwind_state *state) +{ + int err; + + state->flags.all =3D 0; + + switch (state->source) { + case KUNWIND_SOURCE_FRAME: + case KUNWIND_SOURCE_CALLER: + case KUNWIND_SOURCE_TASK: + case KUNWIND_SOURCE_REGS_PC: + err =3D kunwind_next_frame_record(state); + break; + default: + err =3D -EINVAL; + } + + if (err) + return err; + + return kunwind_recover_return_address(state); +} + +typedef bool (*kunwind_consume_fn)(const struct kunwind_state *state, void= *cookie); + +static __always_inline int +do_kunwind(struct kunwind_state *state, kunwind_consume_fn consume_state, + void *cookie) +{ + int ret; + + ret =3D kunwind_recover_return_address(state); + if (ret) + return ret; + + while (1) { + if (!consume_state(state, cookie)) + return -EINVAL; + ret =3D kunwind_next(state); + if (ret =3D=3D -ENOENT) + return 0; + if (ret < 0) + return ret; + } +} + +static __always_inline int +kunwind_stack_walk(kunwind_consume_fn consume_state, + void *cookie, struct task_struct *task, + struct pt_regs *regs) +{ + struct task_struct *tsk =3D task ?: current; + struct stack_info stacks[] =3D { + stackinfo_get_task(tsk), +#ifdef CONFIG_IRQ_STACKS + STACKINFO_CPU(tsk, irq), +#endif +#ifdef CONFIG_VMAP_STACK + STACKINFO_CPU(tsk, overflow), +#endif + }; + struct kunwind_state state =3D { + .common =3D { + .stacks =3D stacks, + .nr_stacks =3D ARRAY_SIZE(stacks), + }, + }; + + if (regs) { + if (tsk !=3D current) + return -EINVAL; + kunwind_init_from_regs(&state, regs); + } else if (tsk =3D=3D current) { + kunwind_init_from_caller(&state); + } else { + kunwind_init_from_task(&state, tsk); + } + + return do_kunwind(&state, consume_state, cookie); +} + +struct kunwind_consume_entry_data { + stack_trace_consume_fn consume_entry; + void *cookie; +}; + +static __always_inline bool +arch_kunwind_consume_entry(const struct kunwind_state *state, void *cookie) +{ + struct kunwind_consume_entry_data *data =3D cookie; + + return data->consume_entry(data->cookie, state->common.pc); +} + +static __always_inline bool +arch_reliable_kunwind_consume_entry(const struct kunwind_state *state, voi= d *cookie) +{ + /* + * At an exception boundary we can reliably consume the saved PC. We do + * not know whether ra was live when the exception was taken, and + * so we cannot perform the next unwind step reliably. + * + * All that matters is whether the *entire* unwind is reliable, so give + * up as soon as we hit an exception boundary. + */ + if (state->source =3D=3D KUNWIND_SOURCE_REGS_PC) + return false; + + return arch_kunwind_consume_entry(state, cookie); +} + +#endif /* CONFIG_FRAME_POINTER */ + +/* + * arch_stack_walk - dual implementation. + * + * When CONFIG_FRAME_POINTER is enabled, uses the kunwind infrastructure f= or + * robust frame-pointer-based unwinding, consistent with arch_stack_walk_r= eliable. + * + * When CONFIG_FRAME_POINTER is disabled, falls back to the simple stack s= can + * in walk_stackframe(). + */ +#ifdef CONFIG_FRAME_POINTER + +noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, + void *cookie, struct task_struct *task, + struct pt_regs *regs) +{ + struct kunwind_consume_entry_data data =3D { + .consume_entry =3D consume_entry, + .cookie =3D cookie, + }; + + kunwind_stack_walk(arch_kunwind_consume_entry, &data, task, regs); +} + +#else + +noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry, + void *cookie, struct task_struct *task, + struct pt_regs *regs) { walk_stackframe(task, regs, consume_entry, cookie); } =20 +#endif /* CONFIG_FRAME_POINTER */ + +/* + * Reliable stack walk for livepatch (CONFIG_FRAME_POINTER only). + */ +#ifdef CONFIG_FRAME_POINTER + +noinline noinstr int arch_stack_walk_reliable(stack_trace_consume_fn consu= me_entry, + void *cookie, + struct task_struct *task) +{ + struct kunwind_consume_entry_data data =3D { + .consume_entry =3D consume_entry, + .cookie =3D cookie, + }; + + return kunwind_stack_walk(arch_reliable_kunwind_consume_entry, &data, + task, NULL); +} + +#endif /* CONFIG_FRAME_POINTER */ + /* * Get the return address for a single stackframe and return a pointer to = the * next frame tail. --=20 2.39.3 From nobody Fri Sep 25 10:37:47 2026 Received: from out30-113.freemail.mail.aliyun.com (out30-113.freemail.mail.aliyun.com [115.124.30.113]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D742842317C; Mon, 14 Sep 2026 09:27:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.113 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378041; cv=none; b=mjb/b+extsksx6Oa6W0pQC5kB8l0mUuQgTpur5/NvCFggwb5rxmX799wiax4J7ueXWQd/O9pYnoJt0iPNbD6rNHM5tyvpELpxqtodrw855FLNgQyiaEeSI6lALXvDJnHmY2/XRtan9odM9YCYItz+A5TuZkn/x1K+dV9eogENN0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378041; c=relaxed/simple; bh=/ZQbdnmxcErDup+LlZ5V4pM9sbuyxJWvREKMQbkSvR8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=h+S6Xuz1kyuOhVvCkGhJCSSta9NvCwwoKnc3yNFOchLtIcoCUTaVzi8XGQayzpV6mHybI2N487RL4IGG3ipjrsli2ZRZi8FYvcs3G50ECTD1NJgR+DCfVKQkqLHjVFuYhJyhuPc5+zqZ5CQllKtnWzD6bDOKQzwWLZ9ozr5CN4k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=SnXulPUu; arc=none smtp.client-ip=115.124.30.113 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="SnXulPUu" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378028; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=66ryLj95jczt6uzzPGSUF0kyBXvHX3cDcmK3PRg8KLA=; b=SnXulPUuVYwmKV13QdMsjlRwkWrOTC8lBpBUIeAs2MK/ivhFOMUTV5RFB8EBN2c1TVPHcQSkNFcb36LlznBbkodjU4LElRfFY8qs60aiPmOLbTdnVumrGouTdSTF6ilsuW00Qke8E7kCLSPmNIeffrqKlApC/iNvm5V1aMcwoDI= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R941e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033045098064;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-pl_1789378024; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-pl_1789378024 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:27:06 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 6/7] riscv: Kconfig: enable HAVE_RELIABLE_STACKTRACE and HAVE_LIVEPATCH Date: Mon, 14 Sep 2026 17:26:47 +0800 Message-Id: <20260914092648.51254-7-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han Now that the metadata frame records, the kunwind state machine and arch_stack_walk_reliable() are all in place, advertise the capability to the rest of the kernel: * select HAVE_RELIABLE_STACKTRACE under FRAME_POINTER && 64BIT, so only the configurations with the tested metadata records and FP-based reliable walker enable it. * select HAVE_LIVEPATCH under the same condition and source kernel/livepatch/Kconfig so the livepatch menu is reachable from the RISC-V configuration. The 64BIT dependency is conservative scoping rather than a hard technical requirement: the metadata frame record, kunwind state machine and arch_stack_walk_reliable() also build on RV32, and the IRQ-stack frame-record adjustment fixes a latent RV32 issue. However, the syscall livepatch selftest and module relocation path have only been exercised on RV64 QEMU virt so far. The 64BIT gate can be relaxed in a follow-up once RV32 has equivalent coverage. This is split out from the unwinder change so the policy decision and the implementation can be reviewed and reverted independently. Reviewed-by: Shuai Xue Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- arch/riscv/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index bf2e81d258c4d..b92e02481198f 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -186,6 +186,7 @@ config RISCV select HAVE_KRETPROBES # https://github.com/ClangBuiltLinux/linux/issues/1881 select HAVE_LD_DEAD_CODE_DATA_ELIMINATION if !LD_IS_LLD + select HAVE_LIVEPATCH if FRAME_POINTER && 64BIT select HAVE_MOVE_PMD select HAVE_MOVE_PUD select HAVE_PAGE_SIZE_4KB @@ -196,6 +197,7 @@ config RISCV select HAVE_POSIX_CPU_TIMERS_TASK_WORK select HAVE_PREEMPT_DYNAMIC_KEY select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_RELIABLE_STACKTRACE if FRAME_POINTER && 64BIT select HAVE_RETHOOK select HAVE_RSEQ select HAVE_RUST if RUSTC_SUPPORTS_RISCV && CC_IS_CLANG @@ -1411,3 +1413,5 @@ endmenu # "CPU Power Management" source "arch/riscv/kvm/Kconfig" =20 source "drivers/acpi/Kconfig" + +source "kernel/livepatch/Kconfig" --=20 2.39.3 From nobody Fri Sep 25 10:37:47 2026 Received: from out30-97.freemail.mail.aliyun.com (out30-97.freemail.mail.aliyun.com [115.124.30.97]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D92B442E437; Mon, 14 Sep 2026 09:27:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.97 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378040; cv=none; b=nZ+RElt61oHgaLmgFTU/pVGvgMxOotCoi13v/uR66lx9TVHDNmjeg7tP97o5Aor42zI1Cm1Nr+fyb8Bhf4a6ytBv4MKbhJDVL3U23hSc4GzxuF37VRRbEq0wJ0EyaxQbwYc2Bh0yIuUdMscphmd/e9ldZUqI36xTkxR2lJWJcMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789378040; c=relaxed/simple; bh=36lNwUWi8r8zm0WG6WwG/ADrpK9nPjx3a/smgDapwcY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sMQ98YIrfP9fl107tj/sK7YHAMJoe+QJaFZpovn5IYIhhMXELseC8OnhvjIshd9jRID4l9RMGOU5FPGF3QAmCu9ZujCvh8/cNpaVs2wE25eFFSECV+rIW/ijH2lUOOqtDQtmWafhGXrj+uBdGMktjaLK2/v0Wwo1AuIqex7zvjU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=w0mlITW2; arc=none smtp.client-ip=115.124.30.97 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="w0mlITW2" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789378030; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=PEnkvP/MMeE/AyrREVwFeM/SBbfr0Ph1OGEBAs/dnV0=; b=w0mlITW2M1K65cmqt+MBPGmvtuwHyx9T+mgAaBSChYXw06KLJ8IjyblTQtbWfFxcS5KdvCExGAivllJxSnKd9urZRKqLzauBlqTYXFBktW+26lLHqRBabBG9KPx6W+9NT0DLPFL1DkKPzBJ719gsV3stqjeBYu5VSiecpPPzWjc= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R961e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033037033178;MF=xueshuai@linux.alibaba.com;NM=1;PH=DS;RN=34;SR=0;TI=SMTPD_---0XAuO-qg_1789378027; Received: from localhost.localdomain(mailfrom:xueshuai@linux.alibaba.com fp:SMTPD_---0XAuO-qg_1789378027 cluster:ay36) by smtp.aliyun-inc.com; Mon, 14 Sep 2026 17:27:08 +0800 From: Shuai Xue To: palmer@dabbelt.com, pjw@kernel.org, aou@eecs.berkeley.edu Cc: alex@ghiti.fr, linux-riscv@lists.infradead.org, oleg@redhat.com, rostedt@goodmis.org, mhiramat@kernel.org, mark.rutland@arm.com, peterz@infradead.org, mingo@redhat.com, acme@kernel.org, namhyung@kernel.org, alexander.shishkin@linux.intel.com, jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com, james.clark@linaro.org, jpoimboe@kernel.org, jikos@kernel.org, mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com, shuah@kernel.org, mpdesouza@suse.com, oliver.yang@linux.alibaba.com, zhuo.song@linux.alibaba.com, xueshuai@linux.alibaba.com, jkchen@linux.alibaba.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, live-patching@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 7/7] selftests/livepatch: Add RISC-V syscall wrapper prefix Date: Mon, 14 Sep 2026 17:26:48 +0800 Message-Id: <20260914092648.51254-8-xueshuai@linux.alibaba.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260914092648.51254-1-xueshuai@linux.alibaba.com> References: <20260914092648.51254-1-xueshuai@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Wang Han The syscall livepatch selftest resolves and patches a syscall wrapper symbol. To use that test for RISC-V livepatch validation, add the RISC-V FN_PREFIX definition for ARCH_HAS_SYSCALL_WRAPPER. Without this macro, the syscall livepatch selftest cannot resolve the RISC-V target symbol, and the syscall-related livepatch test fails on RISC-V. Reviewed-by: Marcos Paulo de Souza Reviewed-by: Shuai Xue Acked-by: Miroslav Benes Assisted-by: Qoder:Qwen-3.8-MAX-Preview Signed-off-by: Wang Han Signed-off-by: Shuai Xue --- .../testing/selftests/livepatch/test_modules/test_klp_syscall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_syscal= l.c b/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c index 08aacc0e14de9..9baa2a5f84c95 100644 --- a/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_syscall.c @@ -24,6 +24,8 @@ #define FN_PREFIX __s390x_ #elif defined(__aarch64__) #define FN_PREFIX __arm64_ + #elif defined(__riscv) + #define FN_PREFIX __riscv_ #elif defined(__powerpc__) #define FN_PREFIX #else --=20 2.39.3