int do_signal()

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;
}