in kernel/signal.c [225:288]
int do_signal(struct pt_regs *regs, int syscall)
{
struct ksignal ksig;
unsigned long continue_addr = 0;
unsigned long restart_addr = 0;
unsigned long retval = 0;
int restart = 0;
if (syscall) {
continue_addr = regs->pc;
restart_addr = continue_addr - 4;
retval = regs->gpr[11];
/*
* Setup syscall restart here so that a debugger will
* see the already changed PC.
*/
switch (retval) {
case -ERESTART_RESTARTBLOCK:
restart = -2;
fallthrough;
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
restart++;
regs->gpr[11] = regs->orig_gpr11;
regs->pc = restart_addr;
break;
}
}
/*
* Get the signal to deliver. During the call to get_signal the
* debugger may change all our registers so we may need to revert
* the decision to restart the syscall; specifically, if the PC is
* changed, don't restart the syscall.
*/
if (get_signal(&ksig)) {
if (unlikely(restart) && regs->pc == restart_addr) {
if (retval == -ERESTARTNOHAND ||
retval == -ERESTART_RESTARTBLOCK
|| (retval == -ERESTARTSYS
&& !(ksig.ka.sa.sa_flags & SA_RESTART))) {
/* No automatic restart */
regs->gpr[11] = -EINTR;
regs->pc = continue_addr;
}
}
handle_signal(&ksig, regs);
} else {
/* no handler */
restore_saved_sigmask();
/*
* Restore pt_regs PC as syscall restart will be handled by
* kernel without return to userspace
*/
if (unlikely(restart) && regs->pc == restart_addr) {
regs->pc = continue_addr;
return restart;
}
}
return 0;
}