Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Jens Axboe and committed by
Thomas Gleixner
12db8b69 5c251e9d

+77 -11
+2 -2
arch/x86/kernel/signal.c
··· 804 804 * want to handle. Thus you cannot kill init even with a SIGKILL even by 805 805 * mistake. 806 806 */ 807 - void arch_do_signal(struct pt_regs *regs) 807 + void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal) 808 808 { 809 809 struct ksignal ksig; 810 810 811 - if (get_signal(&ksig)) { 811 + if (has_signal && get_signal(&ksig)) { 812 812 /* Whee! Actually deliver the signal. */ 813 813 handle_signal(&ksig, regs); 814 814 return;
+8 -3
include/linux/entry-common.h
··· 37 37 # define _TIF_UPROBE (0) 38 38 #endif 39 39 40 + #ifndef _TIF_NOTIFY_SIGNAL 41 + # define _TIF_NOTIFY_SIGNAL (0) 42 + #endif 43 + 40 44 /* 41 45 * TIF flags handled in syscall_enter_from_usermode() 42 46 */ ··· 73 69 74 70 #define EXIT_TO_USER_MODE_WORK \ 75 71 (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ 76 - _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | \ 72 + _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | \ 77 73 ARCH_EXIT_TO_USER_MODE_WORK) 78 74 79 75 /** ··· 230 226 #endif 231 227 232 228 /** 233 - * arch_do_signal - Architecture specific signal delivery function 229 + * arch_do_signal_or_restart - Architecture specific signal delivery function 234 230 * @regs: Pointer to currents pt_regs 231 + * @has_signal: actual signal to handle 235 232 * 236 233 * Invoked from exit_to_user_mode_loop(). 237 234 */ 238 - void arch_do_signal(struct pt_regs *regs); 235 + void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal); 239 236 240 237 /** 241 238 * arch_syscall_exit_tracehook - Wrapper around tracehook_report_syscall_exit()
+2 -2
include/linux/entry-kvm.h
··· 11 11 # define ARCH_XFER_TO_GUEST_MODE_WORK (0) 12 12 #endif 13 13 14 - #define XFER_TO_GUEST_MODE_WORK \ 15 - (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ 14 + #define XFER_TO_GUEST_MODE_WORK \ 15 + (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL | \ 16 16 _TIF_NOTIFY_RESUME | ARCH_XFER_TO_GUEST_MODE_WORK) 17 17 18 18 struct kvm_vcpu;
+10 -1
include/linux/sched/signal.h
··· 360 360 361 361 static inline int signal_pending(struct task_struct *p) 362 362 { 363 + #if defined(TIF_NOTIFY_SIGNAL) 364 + /* 365 + * TIF_NOTIFY_SIGNAL isn't really a signal, but it requires the same 366 + * behavior in terms of ensuring that we break out of wait loops 367 + * so that notify signal callbacks can be processed. 368 + */ 369 + if (unlikely(test_tsk_thread_flag(p, TIF_NOTIFY_SIGNAL))) 370 + return 1; 371 + #endif 363 372 return task_sigpending(p); 364 373 } 365 374 ··· 516 507 static inline void restore_saved_sigmask_unless(bool interrupted) 517 508 { 518 509 if (interrupted) 519 - WARN_ON(!test_thread_flag(TIF_SIGPENDING)); 510 + WARN_ON(!signal_pending(current)); 520 511 else 521 512 restore_saved_sigmask(); 522 513 }
+27
include/linux/tracehook.h
··· 198 198 blkcg_maybe_throttle_current(); 199 199 } 200 200 201 + /* 202 + * called by exit_to_user_mode_loop() if ti_work & _TIF_NOTIFY_SIGNAL. This 203 + * is currently used by TWA_SIGNAL based task_work, which requires breaking 204 + * wait loops to ensure that task_work is noticed and run. 205 + */ 206 + static inline void tracehook_notify_signal(void) 207 + { 208 + #if defined(TIF_NOTIFY_SIGNAL) 209 + clear_thread_flag(TIF_NOTIFY_SIGNAL); 210 + smp_mb__after_atomic(); 211 + if (current->task_works) 212 + task_work_run(); 213 + #endif 214 + } 215 + 216 + /* 217 + * Called when we have work to process from exit_to_user_mode_loop() 218 + */ 219 + static inline void set_notify_signal(struct task_struct *task) 220 + { 221 + #if defined(TIF_NOTIFY_SIGNAL) 222 + if (!test_and_set_tsk_thread_flag(task, TIF_NOTIFY_SIGNAL) && 223 + !wake_up_state(task, TASK_INTERRUPTIBLE)) 224 + kick_process(task); 225 + #endif 226 + } 227 + 201 228 #endif /* <linux/tracehook.h> */
+11 -3
kernel/entry/common.c
··· 109 109 } 110 110 111 111 /* Workaround to allow gradual conversion of architecture code */ 112 - void __weak arch_do_signal(struct pt_regs *regs) { } 112 + void __weak arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal) { } 113 + 114 + static void handle_signal_work(struct pt_regs *regs, unsigned long ti_work) 115 + { 116 + if (ti_work & _TIF_NOTIFY_SIGNAL) 117 + tracehook_notify_signal(); 118 + 119 + arch_do_signal_or_restart(regs, ti_work & _TIF_SIGPENDING); 120 + } 113 121 114 122 static unsigned long exit_to_user_mode_loop(struct pt_regs *regs, 115 123 unsigned long ti_work) ··· 139 131 if (ti_work & _TIF_PATCH_PENDING) 140 132 klp_update_patch_state(current); 141 133 142 - if (ti_work & _TIF_SIGPENDING) 143 - arch_do_signal(regs); 134 + if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) 135 + handle_signal_work(regs, ti_work); 144 136 145 137 if (ti_work & _TIF_NOTIFY_RESUME) { 146 138 clear_thread_flag(TIF_NOTIFY_RESUME);
+3
kernel/entry/kvm.c
··· 8 8 do { 9 9 int ret; 10 10 11 + if (ti_work & _TIF_NOTIFY_SIGNAL) 12 + tracehook_notify_signal(); 13 + 11 14 if (ti_work & _TIF_SIGPENDING) { 12 15 kvm_handle_signal_exit(vcpu); 13 16 return -EINTR;
+14
kernel/signal.c
··· 2529 2529 struct signal_struct *signal = current->signal; 2530 2530 int signr; 2531 2531 2532 + /* 2533 + * For non-generic architectures, check for TIF_NOTIFY_SIGNAL so 2534 + * that the arch handlers don't all have to do it. If we get here 2535 + * without TIF_SIGPENDING, just exit after running signal work. 2536 + */ 2537 + #ifdef TIF_NOTIFY_SIGNAL 2538 + if (!IS_ENABLED(CONFIG_GENERIC_ENTRY)) { 2539 + if (test_thread_flag(TIF_NOTIFY_SIGNAL)) 2540 + tracehook_notify_signal(); 2541 + if (!task_sigpending(current)) 2542 + return false; 2543 + } 2544 + #endif 2545 + 2532 2546 if (unlikely(uprobe_deny_signal())) 2533 2547 return false; 2534 2548