Commit 12db8b69 authored by Jens Axboe's avatar Jens Axboe Committed by Thomas Gleixner
Browse files

entry: Add support for TIF_NOTIFY_SIGNAL



Add TIF_NOTIFY_SIGNAL handling in the generic entry code, which if set,
will return true if signal_pending() is used in a wait loop. That causes an
exit of the loop so that notify_signal tracehooks can be run. If the wait
loop is currently inside a system call, the system call is restarted once
task_work has been processed.

In preparation for only having arch_do_signal() handle syscall restarts if
_TIF_SIGPENDING isn't set, rename it to arch_do_signal_or_restart().  Pass
in a boolean that tells the architecture specific signal handler if it
should attempt to get a signal, or just process a potential syscall
restart.

For !CONFIG_GENERIC_ENTRY archs, add the TIF_NOTIFY_SIGNAL handling to
get_signal(). This is done to minimize the needed architecture changes to
support this feature.

Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarOleg Nesterov <oleg@redhat.com>
Link: https://lore.kernel.org/r/20201026203230.386348-3-axboe@kernel.dk
parent 5c251e9d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -804,11 +804,11 @@ static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
 * want to handle. Thus you cannot kill init even with a SIGKILL even by
 * mistake.
 */
void arch_do_signal(struct pt_regs *regs)
void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
{
	struct ksignal ksig;

	if (get_signal(&ksig)) {
	if (has_signal && get_signal(&ksig)) {
		/* Whee! Actually deliver the signal.  */
		handle_signal(&ksig, regs);
		return;
+8 −3
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@
# define _TIF_UPROBE			(0)
#endif

#ifndef _TIF_NOTIFY_SIGNAL
# define _TIF_NOTIFY_SIGNAL		(0)
#endif

/*
 * TIF flags handled in syscall_enter_from_usermode()
 */
@@ -69,7 +73,7 @@

#define EXIT_TO_USER_MODE_WORK						\
	(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE |		\
	 _TIF_NEED_RESCHED | _TIF_PATCH_PENDING |			\
	 _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL |	\
	 ARCH_EXIT_TO_USER_MODE_WORK)

/**
@@ -226,12 +230,13 @@ static __always_inline void arch_exit_to_user_mode(void) { }
#endif

/**
 * arch_do_signal -  Architecture specific signal delivery function
 * arch_do_signal_or_restart -  Architecture specific signal delivery function
 * @regs:	Pointer to currents pt_regs
 * @has_signal:	actual signal to handle
 *
 * Invoked from exit_to_user_mode_loop().
 */
void arch_do_signal(struct pt_regs *regs);
void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal);

/**
 * arch_syscall_exit_tracehook - Wrapper around tracehook_report_syscall_exit()
+2 −2
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@
#endif

#define XFER_TO_GUEST_MODE_WORK						\
	(_TIF_NEED_RESCHED | _TIF_SIGPENDING |			\
	(_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL |	\
	 _TIF_NOTIFY_RESUME | ARCH_XFER_TO_GUEST_MODE_WORK)

struct kvm_vcpu;
+10 −1
Original line number Diff line number Diff line
@@ -360,6 +360,15 @@ static inline int task_sigpending(struct task_struct *p)

static inline int signal_pending(struct task_struct *p)
{
#if defined(TIF_NOTIFY_SIGNAL)
	/*
	 * TIF_NOTIFY_SIGNAL isn't really a signal, but it requires the same
	 * behavior in terms of ensuring that we break out of wait loops
	 * so that notify signal callbacks can be processed.
	 */
	if (unlikely(test_tsk_thread_flag(p, TIF_NOTIFY_SIGNAL)))
		return 1;
#endif
	return task_sigpending(p);
}

@@ -507,7 +516,7 @@ extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
static inline void restore_saved_sigmask_unless(bool interrupted)
{
	if (interrupted)
		WARN_ON(!test_thread_flag(TIF_SIGPENDING));
		WARN_ON(!signal_pending(current));
	else
		restore_saved_sigmask();
}
+27 −0
Original line number Diff line number Diff line
@@ -198,4 +198,31 @@ static inline void tracehook_notify_resume(struct pt_regs *regs)
	blkcg_maybe_throttle_current();
}

/*
 * called by exit_to_user_mode_loop() if ti_work & _TIF_NOTIFY_SIGNAL. This
 * is currently used by TWA_SIGNAL based task_work, which requires breaking
 * wait loops to ensure that task_work is noticed and run.
 */
static inline void tracehook_notify_signal(void)
{
#if defined(TIF_NOTIFY_SIGNAL)
	clear_thread_flag(TIF_NOTIFY_SIGNAL);
	smp_mb__after_atomic();
	if (current->task_works)
		task_work_run();
#endif
}

/*
 * Called when we have work to process from exit_to_user_mode_loop()
 */
static inline void set_notify_signal(struct task_struct *task)
{
#if defined(TIF_NOTIFY_SIGNAL)
	if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_SIGNAL) &&
	    !wake_up_state(task, TASK_INTERRUPTIBLE))
		kick_process(task);
#endif
}

#endif	/* <linux/tracehook.h> */
Loading