mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-09 06:04:14 +08:00
In preparation for a major rewrite of this code, provide a data structure for rseq management. Put all the rseq related data into it (except for the debug part), which allows to simplify fork/execve by using memset() and memcpy() instead of adding new fields to initialize over and over. Create a storage struct for event management as well and put the sched_switch event and a indicator for RSEQ on a task into it as a start. That uses a union, which allows to mask and clear the whole lot efficiently. The indicators are explicitly not a bit field. Bit fields generate abysmal code. The boolean members are defined as u8 as that actually guarantees that it fits. There seem to be strange architecture ABIs which need more than 8 bits for a boolean. The has_rseq member is redundant vs. task::rseq, but it turns out that boolean operations and quick checks on the union generate better code than fiddling with separate entities and data types. This struct will be extended over time to carry more information. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Link: https://patch.msgid.link/20251027084306.527086690@linutronix.de
96 lines
2.7 KiB
C
96 lines
2.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
|
#ifndef _LINUX_RSEQ_H
|
|
#define _LINUX_RSEQ_H
|
|
|
|
#ifdef CONFIG_RSEQ
|
|
#include <linux/sched.h>
|
|
|
|
void __rseq_handle_notify_resume(struct ksignal *sig, struct pt_regs *regs);
|
|
|
|
static inline void rseq_handle_notify_resume(struct pt_regs *regs)
|
|
{
|
|
if (current->rseq.event.has_rseq)
|
|
__rseq_handle_notify_resume(NULL, regs);
|
|
}
|
|
|
|
static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs)
|
|
{
|
|
if (current->rseq.event.has_rseq) {
|
|
current->rseq.event.sched_switch = true;
|
|
__rseq_handle_notify_resume(ksig, regs);
|
|
}
|
|
}
|
|
|
|
static inline void rseq_sched_switch_event(struct task_struct *t)
|
|
{
|
|
if (t->rseq.event.has_rseq) {
|
|
t->rseq.event.sched_switch = true;
|
|
set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
|
|
}
|
|
}
|
|
|
|
static __always_inline void rseq_exit_to_user_mode(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
|
|
if (WARN_ON_ONCE(current->rseq.event.has_rseq &&
|
|
current->rseq.event.events))
|
|
current->rseq.event.events = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* KVM/HYPERV invoke resume_user_mode_work() before entering guest mode,
|
|
* which clears TIF_NOTIFY_RESUME. To avoid updating user space RSEQ in
|
|
* that case just to do it eventually again before returning to user space,
|
|
* the entry resume_user_mode_work() invocation is ignored as the register
|
|
* argument is NULL.
|
|
*
|
|
* After returning from guest mode, they have to invoke this function to
|
|
* re-raise TIF_NOTIFY_RESUME if necessary.
|
|
*/
|
|
static inline void rseq_virt_userspace_exit(void)
|
|
{
|
|
if (current->rseq.event.sched_switch)
|
|
set_tsk_thread_flag(current, TIF_NOTIFY_RESUME);
|
|
}
|
|
|
|
static inline void rseq_reset(struct task_struct *t)
|
|
{
|
|
memset(&t->rseq, 0, sizeof(t->rseq));
|
|
}
|
|
|
|
static inline void rseq_execve(struct task_struct *t)
|
|
{
|
|
rseq_reset(t);
|
|
}
|
|
|
|
/*
|
|
* If parent process has a registered restartable sequences area, the
|
|
* child inherits. Unregister rseq for a clone with CLONE_VM set.
|
|
*/
|
|
static inline void rseq_fork(struct task_struct *t, u64 clone_flags)
|
|
{
|
|
if (clone_flags & CLONE_VM)
|
|
rseq_reset(t);
|
|
else
|
|
t->rseq = current->rseq;
|
|
}
|
|
|
|
#else /* CONFIG_RSEQ */
|
|
static inline void rseq_handle_notify_resume(struct pt_regs *regs) { }
|
|
static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs) { }
|
|
static inline void rseq_sched_switch_event(struct task_struct *t) { }
|
|
static inline void rseq_virt_userspace_exit(void) { }
|
|
static inline void rseq_fork(struct task_struct *t, u64 clone_flags) { }
|
|
static inline void rseq_execve(struct task_struct *t) { }
|
|
static inline void rseq_exit_to_user_mode(void) { }
|
|
#endif /* !CONFIG_RSEQ */
|
|
|
|
#ifdef CONFIG_DEBUG_RSEQ
|
|
void rseq_syscall(struct pt_regs *regs);
|
|
#else /* CONFIG_DEBUG_RSEQ */
|
|
static inline void rseq_syscall(struct pt_regs *regs) { }
|
|
#endif /* !CONFIG_DEBUG_RSEQ */
|
|
|
|
#endif /* _LINUX_RSEQ_H */
|