[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240627081209.3511918-2-ruanjinjie@huawei.com>
Date: Thu, 27 Jun 2024 16:12:07 +0800
From: Jinjie Ruan <ruanjinjie@...wei.com>
To: <catalin.marinas@....com>, <will@...nel.org>, <oleg@...hat.com>,
<tglx@...utronix.de>, <peterz@...radead.org>, <luto@...nel.org>,
<kees@...nel.org>, <wad@...omium.org>, <ruanjinjie@...wei.com>,
<rostedt@...dmis.org>, <arnd@...db.de>, <ardb@...nel.org>,
<broonie@...nel.org>, <mark.rutland@....com>, <rick.p.edgecombe@...el.com>,
<leobras@...hat.com>, <linux-kernel@...r.kernel.org>,
<linux-arm-kernel@...ts.infradead.org>
Subject: [PATCH v2 1/3] entry: Add some arch funcs to support arm64 to use generic entry
Add some arch functions to support arm64 to use generic entry, which do not
affect existing architectures that use generic entry:
- arch_prepare/post_report_syscall_entry/exit().
- arch_enter_from_kernel_mode(), arch_exit_to_kernel_mode_prepare().
- arch_irqentry_exit_need_resched() to support architecture-related
need_resched() check logic.
Also make report_single_step() and syscall_exit_work() not static, which
can be used by arm64 later.
Signed-off-by: Jinjie Ruan <ruanjinjie@...wei.com>
---
v2:
- Fix a bug that not call arch_post_report_syscall_entry() in
syscall_trace_enter() if ptrace_report_syscall_entry() return not zero.
- Update the commit message.
---
include/linux/entry-common.h | 51 ++++++++++++++++++++++++++++++++++++
kernel/entry/common.c | 48 ++++++++++++++++++++++++++++-----
2 files changed, 93 insertions(+), 6 deletions(-)
diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
index b0fb775a600d..1be4c3d91995 100644
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -84,6 +84,18 @@ static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs);
static __always_inline void arch_enter_from_user_mode(struct pt_regs *regs) {}
#endif
+static __always_inline void arch_enter_from_kernel_mode(struct pt_regs *regs);
+
+#ifndef arch_enter_from_kernel_mode
+static __always_inline void arch_enter_from_kernel_mode(struct pt_regs *regs) {}
+#endif
+
+static __always_inline void arch_exit_to_kernel_mode_prepare(struct pt_regs *regs);
+
+#ifndef arch_exit_to_kernel_mode_prepare
+static __always_inline void arch_exit_to_kernel_mode_prepare(struct pt_regs *regs) {}
+#endif
+
/**
* enter_from_user_mode - Establish state when coming from user mode
*
@@ -298,6 +310,42 @@ static __always_inline void arch_exit_to_user_mode(void) { }
*/
void arch_do_signal_or_restart(struct pt_regs *regs);
+/**
+ * arch_irqentry_exit_need_resched - Architecture specific need resched function
+ */
+bool arch_irqentry_exit_need_resched(void);
+
+/**
+ * arch_prepare_report_syscall_entry - Architecture specific report_syscall_entry()
+ * prepare function
+ */
+unsigned long arch_prepare_report_syscall_entry(struct pt_regs *regs);
+
+/**
+ * arch_post_report_syscall_entry - Architecture specific report_syscall_entry()
+ * post function
+ */
+void arch_post_report_syscall_entry(struct pt_regs *regs, unsigned long saved_reg);
+
+/**
+ * arch_prepare_report_syscall_exit - Architecture specific report_syscall_exit()
+ * prepare function
+ */
+unsigned long arch_prepare_report_syscall_exit(struct pt_regs *regs, unsigned long work);
+
+/**
+ * arch_post_report_syscall_exit - Architecture specific report_syscall_exit()
+ * post function
+ */
+void arch_post_report_syscall_exit(struct pt_regs *regs, unsigned long saved_reg,
+ unsigned long work);
+
+/**
+ * arch_forget_syscall - Architecture specific function called if
+ * ptrace_report_syscall_entry() return nonzero
+ */
+void arch_forget_syscall(struct pt_regs *regs);
+
/**
* exit_to_user_mode_loop - do any pending work before leaving to user space
*/
@@ -552,4 +600,7 @@ irqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs);
*/
void noinstr irqentry_nmi_exit(struct pt_regs *regs, irqentry_state_t irq_state);
+bool report_single_step(unsigned long work);
+void syscall_exit_work(struct pt_regs *regs, unsigned long work);
+
#endif
diff --git a/kernel/entry/common.c b/kernel/entry/common.c
index 90843cc38588..625b63e947cb 100644
--- a/kernel/entry/common.c
+++ b/kernel/entry/common.c
@@ -25,9 +25,14 @@ static inline void syscall_enter_audit(struct pt_regs *regs, long syscall)
}
}
+unsigned long __weak arch_prepare_report_syscall_entry(struct pt_regs *regs) { return 0; }
+void __weak arch_post_report_syscall_entry(struct pt_regs *regs, unsigned long saved_reg) { }
+void __weak arch_forget_syscall(struct pt_regs *regs) { };
+
long syscall_trace_enter(struct pt_regs *regs, long syscall,
unsigned long work)
{
+ unsigned long saved_reg;
long ret = 0;
/*
@@ -42,8 +47,13 @@ long syscall_trace_enter(struct pt_regs *regs, long syscall,
/* Handle ptrace */
if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
+ saved_reg = arch_prepare_report_syscall_entry(regs);
ret = ptrace_report_syscall_entry(regs);
- if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
+ if (ret)
+ arch_forget_syscall(regs);
+
+ arch_post_report_syscall_entry(regs, saved_reg);
+ if (ret || work & SYSCALL_WORK_SYSCALL_EMU)
return -1L;
}
@@ -138,7 +148,7 @@ __always_inline unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
* SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall
* instruction has been already reported in syscall_enter_from_user_mode().
*/
-static inline bool report_single_step(unsigned long work)
+inline bool report_single_step(unsigned long work)
{
if (work & SYSCALL_WORK_SYSCALL_EMU)
return false;
@@ -146,8 +156,22 @@ static inline bool report_single_step(unsigned long work)
return work & SYSCALL_WORK_SYSCALL_EXIT_TRAP;
}
-static void syscall_exit_work(struct pt_regs *regs, unsigned long work)
+unsigned long __weak arch_prepare_report_syscall_exit(struct pt_regs *regs,
+ unsigned long work)
+{
+ return 0;
+}
+
+void __weak arch_post_report_syscall_exit(struct pt_regs *regs,
+ unsigned long saved_reg,
+ unsigned long work)
+{
+
+}
+
+void syscall_exit_work(struct pt_regs *regs, unsigned long work)
{
+ unsigned long saved_reg;
bool step;
/*
@@ -169,8 +193,11 @@ static void syscall_exit_work(struct pt_regs *regs, unsigned long work)
trace_sys_exit(regs, syscall_get_return_value(current, regs));
step = report_single_step(work);
- if (step || work & SYSCALL_WORK_SYSCALL_TRACE)
+ if (step || work & SYSCALL_WORK_SYSCALL_TRACE) {
+ saved_reg = arch_prepare_report_syscall_exit(regs, work);
ptrace_report_syscall_exit(regs, step);
+ arch_post_report_syscall_exit(regs, saved_reg, work);
+ }
}
/*
@@ -244,6 +271,8 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
return ret;
}
+ arch_enter_from_kernel_mode(regs);
+
/*
* If this entry hit the idle task invoke ct_irq_enter() whether
* RCU is watching or not.
@@ -300,6 +329,8 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
return ret;
}
+bool __weak arch_irqentry_exit_need_resched(void) { return true; }
+
void raw_irqentry_exit_cond_resched(void)
{
if (!preempt_count()) {
@@ -307,7 +338,7 @@ void raw_irqentry_exit_cond_resched(void)
rcu_irq_exit_check_preempt();
if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
WARN_ON_ONCE(!on_thread_stack());
- if (need_resched())
+ if (need_resched() && arch_irqentry_exit_need_resched())
preempt_schedule_irq();
}
}
@@ -332,7 +363,12 @@ noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state)
/* Check whether this returns to user mode */
if (user_mode(regs)) {
irqentry_exit_to_user_mode(regs);
- } else if (!regs_irqs_disabled(regs)) {
+ return;
+ }
+
+ arch_exit_to_kernel_mode_prepare(regs);
+
+ if (!regs_irqs_disabled(regs)) {
/*
* If RCU was not watching on entry this needs to be done
* carefully and needs the same ordering of lockdep/tracing
--
2.34.1
Powered by blists - more mailing lists