mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 00:28:52 +02:00 
			
		
		
		
	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: Jens Axboe <axboe@kernel.dk> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Oleg Nesterov <oleg@redhat.com> Link: https://lore.kernel.org/r/20201026203230.386348-3-axboe@kernel.dk
This commit is contained in:
		
							parent
							
								
									5c251e9dc0
								
							
						
					
					
						commit
						12db8b6900
					
				
					 8 changed files with 77 additions and 11 deletions
				
			
		|  | @ -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; | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -11,8 +11,8 @@ | |||
| # define ARCH_XFER_TO_GUEST_MODE_WORK	(0) | ||||
| #endif | ||||
| 
 | ||||
| #define XFER_TO_GUEST_MODE_WORK					\ | ||||
| 	(_TIF_NEED_RESCHED | _TIF_SIGPENDING |			\ | ||||
| #define XFER_TO_GUEST_MODE_WORK						\ | ||||
| 	(_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL |	\ | ||||
| 	 _TIF_NOTIFY_RESUME | ARCH_XFER_TO_GUEST_MODE_WORK) | ||||
| 
 | ||||
| struct kvm_vcpu; | ||||
|  |  | |||
|  | @ -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(); | ||||
| } | ||||
|  |  | |||
|  | @ -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> */ | ||||
|  |  | |||
|  | @ -109,7 +109,15 @@ static __always_inline void exit_to_user_mode(void) | |||
| } | ||||
| 
 | ||||
| /* Workaround to allow gradual conversion of architecture code */ | ||||
| void __weak arch_do_signal(struct pt_regs *regs) { } | ||||
| void __weak arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal) { } | ||||
| 
 | ||||
| static void handle_signal_work(struct pt_regs *regs, unsigned long ti_work) | ||||
| { | ||||
| 	if (ti_work & _TIF_NOTIFY_SIGNAL) | ||||
| 		tracehook_notify_signal(); | ||||
| 
 | ||||
| 	arch_do_signal_or_restart(regs, ti_work & _TIF_SIGPENDING); | ||||
| } | ||||
| 
 | ||||
| static unsigned long exit_to_user_mode_loop(struct pt_regs *regs, | ||||
| 					    unsigned long ti_work) | ||||
|  | @ -131,8 +139,8 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs, | |||
| 		if (ti_work & _TIF_PATCH_PENDING) | ||||
| 			klp_update_patch_state(current); | ||||
| 
 | ||||
| 		if (ti_work & _TIF_SIGPENDING) | ||||
| 			arch_do_signal(regs); | ||||
| 		if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) | ||||
| 			handle_signal_work(regs, ti_work); | ||||
| 
 | ||||
| 		if (ti_work & _TIF_NOTIFY_RESUME) { | ||||
| 			clear_thread_flag(TIF_NOTIFY_RESUME); | ||||
|  |  | |||
|  | @ -8,6 +8,9 @@ static int xfer_to_guest_mode_work(struct kvm_vcpu *vcpu, unsigned long ti_work) | |||
| 	do { | ||||
| 		int ret; | ||||
| 
 | ||||
| 		if (ti_work & _TIF_NOTIFY_SIGNAL) | ||||
| 			tracehook_notify_signal(); | ||||
| 
 | ||||
| 		if (ti_work & _TIF_SIGPENDING) { | ||||
| 			kvm_handle_signal_exit(vcpu); | ||||
| 			return -EINTR; | ||||
|  |  | |||
|  | @ -2529,6 +2529,20 @@ bool get_signal(struct ksignal *ksig) | |||
| 	struct signal_struct *signal = current->signal; | ||||
| 	int signr; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * For non-generic architectures, check for TIF_NOTIFY_SIGNAL so | ||||
| 	 * that the arch handlers don't all have to do it. If we get here | ||||
| 	 * without TIF_SIGPENDING, just exit after running signal work. | ||||
| 	 */ | ||||
| #ifdef TIF_NOTIFY_SIGNAL | ||||
| 	if (!IS_ENABLED(CONFIG_GENERIC_ENTRY)) { | ||||
| 		if (test_thread_flag(TIF_NOTIFY_SIGNAL)) | ||||
| 			tracehook_notify_signal(); | ||||
| 		if (!task_sigpending(current)) | ||||
| 			return false; | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	if (unlikely(uprobe_deny_signal())) | ||||
| 		return false; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jens Axboe
						Jens Axboe