From patchwork Tue Dec 19 02:22:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: chenqiwu X-Patchwork-Id: 13497796 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id BFD92C35274 for ; Tue, 19 Dec 2023 02:25:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:Cc :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=IINOYtRUfzjghqjhTWx/zTjVTNB+CUyZOux2qHEyI7s=; b=unwXKcAygyiRvV 9eeIT1w7r6x/rcoRm0No8IN69djv+IYkwvwE9lOWfWxWwViVep8BLt1qvwuh2Uvogq2/0Khi0VMgZ q/4JUt+E0dzxhRtFTxQXRnczFaws3Ayva0q3Xr0N9HIl9k2s2zDJUqEMX6K8go4Gd2HARN2XtqdrN hTkpS/Ew+txhG4SyrDv21wrmvVMWPhY17s6ntSuKnZaPVvySQ/s0783l/WmAUn3Y1WeloBANs6aGX meR8b22NThUuFTZS79WtGhaIAHeqJ336/ezB2HLuXp3ApqO49O3Yt6yqm5KbJ+QIzbvM0l7r/b7im ExWcG1bD++9hDnCLWzLg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1rFPmr-00CZpS-29; Tue, 19 Dec 2023 02:24:49 +0000 Received: from mail-pg1-x52e.google.com ([2607:f8b0:4864:20::52e]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1rFPkl-00CZfB-2Z for linux-arm-kernel@lists.infradead.org; Tue, 19 Dec 2023 02:22:41 +0000 Received: by mail-pg1-x52e.google.com with SMTP id 41be03b00d2f7-5c690c3d113so3073963a12.1 for ; Mon, 18 Dec 2023 18:22:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702952555; x=1703557355; darn=lists.infradead.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=9p0315uqpc+n1pOToNiw0hz0iRBomEp4zEeHMgnV+bg=; b=JT94XWyMV1tRiaiS2BBvXnHHrZcCdkUySMpiDv7sqNmsDoZG5yy90kk7aDgpjV+WCS 7+mo1sT6h0d3hBlPm7/npLVVPXREAYmQubVaTj5aFn1sJKmSA2lwTH1XjWTgl2EaXtUc zN/WgoedkYOH9JbDPzpI4uAQ9jkKzTQwFFxUPFzlwIef7NZKTxTmIWcZMgrCEJV7dzWi NUKLQ8yAHJFtKAoSf4pxU8Jo9XksHaGlSYmBZUOpxwKNRFll4dm2S5m3Zx+KCR6dn73d rc0ofoAHY9rJ8A14t52j8gJ5Umscqr2eXCg68G6GdpRqvdJpVRIS7sDQchbG1530++C+ aLXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702952555; x=1703557355; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=9p0315uqpc+n1pOToNiw0hz0iRBomEp4zEeHMgnV+bg=; b=YU5IVZsLzoxH7ZYWD82Q7rmZG0KTRlDTnDheMy2gcmjxtKJD2ZGeopF4zv3KKUnfDX 68HkknRUY8dYr+sX7yKOqHrIzFKsrxVgsJooyoPXNnxI7QBipI9Au0NFemGnoIjEdW6J KWEFk2Pzmtmi1Il53+vpj/t5X+bo2Moe2gpbyVs+EcbssRElIA6g7Vy9tImMD2AdPVJB dMhKBM7Cgw2StueXs0n2qcu9Gk0OyXBmVVatoErbOJSin7Q86PtpJhhN07XJMwZS6wV2 7jTovE2J1kNGvmDFuIPnSGjJLpwdd3KzlWKUMhg1a9h1P6MJHmK29x5g/UyW+3ZHE8uQ Xf9w== X-Gm-Message-State: AOJu0Yz+/UGS7sKR1MQJYhqBPOb0yRoGAr7gvLzNrJL81BwCwGHaWJnh pRg03Z4t17s8cj6dbwAMIZUS+sAlMSg= X-Google-Smtp-Source: AGHT+IEsWG1wWSv2Bht8XTIBKQwnu8RaFftFhLMdkyvRSBMXzm3jAmCm4ziRmR77h7OPqQigyVXDNg== X-Received: by 2002:a05:6a20:72a0:b0:191:3733:ebaa with SMTP id o32-20020a056a2072a000b001913733ebaamr15853829pzk.50.1702952554945; Mon, 18 Dec 2023 18:22:34 -0800 (PST) Received: from localhost ([107.155.12.245]) by smtp.gmail.com with ESMTPSA id q26-20020a62ae1a000000b006d36b6c6591sm4827946pff.39.2023.12.18.18.22.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 18 Dec 2023 18:22:34 -0800 (PST) From: chenqiwu X-Google-Original-From: chenqiwu To: catalin.marinas@arm.com, will@kernel.org, peterz@infradead.org, mingo@redhat.com, mark.rutland@arm.com, jolsa@kernel.org, namhyung@kernel.org, irogers@google.com, adrian.hunter@intel.com, kaleshsingh@google.com, ardb@kernel.org Cc: alexander.shishkin@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-perf-users@vger.kernel.org, chenqiwu Subject: [RESEND v3] arm64: Add USER_STACKTRACE support Date: Tue, 19 Dec 2023 10:22:29 +0800 Message-Id: <20231219022229.10230-1-qiwu.chen@transsion.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20231218_182239_863611_69B6701D X-CRM114-Status: GOOD ( 21.45 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Currently, userstacktrace is unsupported for ftrace and uprobe tracers on arm64. This patch uses the perf_callchain_user() code as blueprint to implement the arch_stack_walk_user() which add userstacktrace support on arm64. Meanwhile, we can use arch_stack_walk_user() to simplify the implementation of perf_callchain_user(). This patch is tested pass with ftrace, uprobe and perf tracers profiling userstacktrace cases. changes in v3: - update perf_callchain_user() to use arch_stack_walk_user() and delete the redundant code as Mark's suggestion in v2. - update the commit message. Tested-by: chenqiwu Signed-off-by: chenqiwu --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/perf_callchain.c | 118 +--------------------------- arch/arm64/kernel/stacktrace.c | 120 +++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 114 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7b071a00425d..4c5066f88dd2 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -255,6 +255,7 @@ config ARM64 select TRACE_IRQFLAGS_SUPPORT select TRACE_IRQFLAGS_NMI_SUPPORT select HAVE_SOFTIRQ_ON_OWN_STACK + select USER_STACKTRACE_SUPPORT help ARM 64-bit (AArch64) Linux support. diff --git a/arch/arm64/kernel/perf_callchain.c b/arch/arm64/kernel/perf_callchain.c index 6d157f32187b..e8ed5673f481 100644 --- a/arch/arm64/kernel/perf_callchain.c +++ b/arch/arm64/kernel/perf_callchain.c @@ -10,94 +10,12 @@ #include -struct frame_tail { - struct frame_tail __user *fp; - unsigned long lr; -} __attribute__((packed)); - -/* - * Get the return address for a single stackframe and return a pointer to the - * next frame tail. - */ -static struct frame_tail __user * -user_backtrace(struct frame_tail __user *tail, - struct perf_callchain_entry_ctx *entry) -{ - struct frame_tail buftail; - unsigned long err; - unsigned long lr; - - /* Also check accessibility of one struct frame_tail beyond */ - if (!access_ok(tail, sizeof(buftail))) - return NULL; - - pagefault_disable(); - err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); - pagefault_enable(); - - if (err) - return NULL; - - lr = ptrauth_strip_user_insn_pac(buftail.lr); - - perf_callchain_store(entry, lr); - - /* - * Frame pointers should strictly progress back up the stack - * (towards higher addresses). - */ - if (tail >= buftail.fp) - return NULL; - - return buftail.fp; -} - -#ifdef CONFIG_COMPAT -/* - * The registers we're interested in are at the end of the variable - * length saved register structure. The fp points at the end of this - * structure so the address of this struct is: - * (struct compat_frame_tail *)(xxx->fp)-1 - * - * This code has been adapted from the ARM OProfile support. - */ -struct compat_frame_tail { - compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ - u32 sp; - u32 lr; -} __attribute__((packed)); - -static struct compat_frame_tail __user * -compat_user_backtrace(struct compat_frame_tail __user *tail, - struct perf_callchain_entry_ctx *entry) +static bool callchain_trace(void *data, unsigned long pc) { - struct compat_frame_tail buftail; - unsigned long err; - - /* Also check accessibility of one struct frame_tail beyond */ - if (!access_ok(tail, sizeof(buftail))) - return NULL; - - pagefault_disable(); - err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); - pagefault_enable(); - - if (err) - return NULL; - - perf_callchain_store(entry, buftail.lr); - - /* - * Frame pointers should strictly progress back up the stack - * (towards higher addresses). - */ - if (tail + 1 >= (struct compat_frame_tail __user *) - compat_ptr(buftail.fp)) - return NULL; + struct perf_callchain_entry_ctx *entry = data; - return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; + return perf_callchain_store(entry, pc) == 0; } -#endif /* CONFIG_COMPAT */ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) @@ -107,35 +25,7 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, return; } - perf_callchain_store(entry, regs->pc); - - if (!compat_user_mode(regs)) { - /* AARCH64 mode */ - struct frame_tail __user *tail; - - tail = (struct frame_tail __user *)regs->regs[29]; - - while (entry->nr < entry->max_stack && - tail && !((unsigned long)tail & 0x7)) - tail = user_backtrace(tail, entry); - } else { -#ifdef CONFIG_COMPAT - /* AARCH32 compat mode */ - struct compat_frame_tail __user *tail; - - tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; - - while ((entry->nr < entry->max_stack) && - tail && !((unsigned long)tail & 0x3)) - tail = compat_user_backtrace(tail, entry); -#endif - } -} - -static bool callchain_trace(void *data, unsigned long pc) -{ - struct perf_callchain_entry_ctx *entry = data; - return perf_callchain_store(entry, pc) == 0; + arch_stack_walk_user(callchain_trace, entry, regs); } void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 17f66a74c745..7f9ab5a37096 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -240,3 +240,123 @@ void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) dump_backtrace(NULL, tsk, loglvl); barrier(); } + +/* + * The struct defined for userspace stack frame in AARCH64 mode. + */ +struct frame_tail { + struct frame_tail __user *fp; + unsigned long lr; +} __attribute__((packed)); + +/* + * Get the return address for a single stackframe and return a pointer to the + * next frame tail. + */ +static struct frame_tail __user * +unwind_user_frame(struct frame_tail __user *tail, void *cookie, + stack_trace_consume_fn consume_entry) +{ + struct frame_tail buftail; + unsigned long err; + unsigned long lr; + + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(tail, sizeof(buftail))) + return NULL; + + pagefault_disable(); + err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); + pagefault_enable(); + + if (err) + return NULL; + + lr = ptrauth_strip_user_insn_pac(buftail.lr); + + if (!consume_entry(cookie, lr)) + return NULL; + + /* + * Frame pointers should strictly progress back up the stack + * (towards higher addresses). + */ + if (tail >= buftail.fp) + return NULL; + + return buftail.fp; +} + +#ifdef CONFIG_COMPAT +/* + * The registers we're interested in are at the end of the variable + * length saved register structure. The fp points at the end of this + * structure so the address of this struct is: + * (struct compat_frame_tail *)(xxx->fp)-1 + * + * This code has been adapted from the ARM OProfile support. + */ +struct compat_frame_tail { + compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ + u32 sp; + u32 lr; +} __attribute__((packed)); + +static struct compat_frame_tail __user * +unwind_compat_user_frame(struct compat_frame_tail __user *tail, void *cookie, + stack_trace_consume_fn consume_entry) +{ + struct compat_frame_tail buftail; + unsigned long err; + + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(tail, sizeof(buftail))) + return NULL; + + pagefault_disable(); + err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); + pagefault_enable(); + + if (err) + return NULL; + + if (!consume_entry(cookie, buftail.lr)) + return NULL; + + /* + * Frame pointers should strictly progress back up the stack + * (towards higher addresses). + */ + if (tail + 1 >= (struct compat_frame_tail __user *) + compat_ptr(buftail.fp)) + return NULL; + + return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; +} +#endif /* CONFIG_COMPAT */ + + +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, + const struct pt_regs *regs) +{ + if (!consume_entry(cookie, regs->pc)) + return; + + if (!compat_user_mode(regs)) { + /* AARCH64 mode */ + struct frame_tail __user *tail; + + tail = (struct frame_tail __user *)regs->regs[29]; + while (tail && !((unsigned long)tail & 0x7)) + tail = unwind_user_frame(tail, cookie, consume_entry); + } else { +#ifdef CONFIG_COMPAT + /* AARCH32 compat mode */ + struct compat_frame_tail __user *tail; + + tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; + while (tail && !((unsigned long)tail & 0x3)) + tail = unwind_compat_user_frame(tail, cookie, consume_entry); +#endif + } +}