mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +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 |  * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||||||
|  * mistake. |  * 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; | 	struct ksignal ksig; | ||||||
| 
 | 
 | ||||||
| 	if (get_signal(&ksig)) { | 	if (has_signal && get_signal(&ksig)) { | ||||||
| 		/* Whee! Actually deliver the signal.  */ | 		/* Whee! Actually deliver the signal.  */ | ||||||
| 		handle_signal(&ksig, regs); | 		handle_signal(&ksig, regs); | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
|  | @ -37,6 +37,10 @@ | ||||||
| # define _TIF_UPROBE			(0) | # define _TIF_UPROBE			(0) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifndef _TIF_NOTIFY_SIGNAL | ||||||
|  | # define _TIF_NOTIFY_SIGNAL		(0) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * TIF flags handled in syscall_enter_from_usermode() |  * TIF flags handled in syscall_enter_from_usermode() | ||||||
|  */ |  */ | ||||||
|  | @ -69,7 +73,7 @@ | ||||||
| 
 | 
 | ||||||
| #define EXIT_TO_USER_MODE_WORK						\ | #define EXIT_TO_USER_MODE_WORK						\ | ||||||
| 	(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE |		\ | 	(_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) | 	 ARCH_EXIT_TO_USER_MODE_WORK) | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -226,12 +230,13 @@ static __always_inline void arch_exit_to_user_mode(void) { } | ||||||
| #endif | #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 |  * @regs:	Pointer to currents pt_regs | ||||||
|  |  * @has_signal:	actual signal to handle | ||||||
|  * |  * | ||||||
|  * Invoked from exit_to_user_mode_loop(). |  * 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() |  * arch_syscall_exit_tracehook - Wrapper around tracehook_report_syscall_exit() | ||||||
|  |  | ||||||
|  | @ -11,8 +11,8 @@ | ||||||
| # define ARCH_XFER_TO_GUEST_MODE_WORK	(0) | # define ARCH_XFER_TO_GUEST_MODE_WORK	(0) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define XFER_TO_GUEST_MODE_WORK					\ | #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) | 	 _TIF_NOTIFY_RESUME | ARCH_XFER_TO_GUEST_MODE_WORK) | ||||||
| 
 | 
 | ||||||
| struct kvm_vcpu; | 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) | 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); | 	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) | static inline void restore_saved_sigmask_unless(bool interrupted) | ||||||
| { | { | ||||||
| 	if (interrupted) | 	if (interrupted) | ||||||
| 		WARN_ON(!test_thread_flag(TIF_SIGPENDING)); | 		WARN_ON(!signal_pending(current)); | ||||||
| 	else | 	else | ||||||
| 		restore_saved_sigmask(); | 		restore_saved_sigmask(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -198,4 +198,31 @@ static inline void tracehook_notify_resume(struct pt_regs *regs) | ||||||
| 	blkcg_maybe_throttle_current(); | 	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> */ | #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 */ | /* 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, | static unsigned long exit_to_user_mode_loop(struct pt_regs *regs, | ||||||
| 					    unsigned long ti_work) | 					    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) | 		if (ti_work & _TIF_PATCH_PENDING) | ||||||
| 			klp_update_patch_state(current); | 			klp_update_patch_state(current); | ||||||
| 
 | 
 | ||||||
| 		if (ti_work & _TIF_SIGPENDING) | 		if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) | ||||||
| 			arch_do_signal(regs); | 			handle_signal_work(regs, ti_work); | ||||||
| 
 | 
 | ||||||
| 		if (ti_work & _TIF_NOTIFY_RESUME) { | 		if (ti_work & _TIF_NOTIFY_RESUME) { | ||||||
| 			clear_thread_flag(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 { | 	do { | ||||||
| 		int ret; | 		int ret; | ||||||
| 
 | 
 | ||||||
|  | 		if (ti_work & _TIF_NOTIFY_SIGNAL) | ||||||
|  | 			tracehook_notify_signal(); | ||||||
|  | 
 | ||||||
| 		if (ti_work & _TIF_SIGPENDING) { | 		if (ti_work & _TIF_SIGPENDING) { | ||||||
| 			kvm_handle_signal_exit(vcpu); | 			kvm_handle_signal_exit(vcpu); | ||||||
| 			return -EINTR; | 			return -EINTR; | ||||||
|  |  | ||||||
|  | @ -2529,6 +2529,20 @@ bool get_signal(struct ksignal *ksig) | ||||||
| 	struct signal_struct *signal = current->signal; | 	struct signal_struct *signal = current->signal; | ||||||
| 	int signr; | 	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())) | 	if (unlikely(uprobe_deny_signal())) | ||||||
| 		return false; | 		return false; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jens Axboe
						Jens Axboe