Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81783786 authored by Al Viro's avatar Al Viro Committed by Russell King
Browse files

ARM: 7473/1: deal with handlerless restarts without leaving the kernel

parent 0a267fa6
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -54,7 +54,11 @@ work_pending:
	mov	r0, sp				@ 'regs'
	mov	r2, why				@ 'syscall'
	bl	do_work_pending
	b	no_work_pending
	tst	r0, #1
	beq	no_work_pending
	ldmia	sp, {r0 - r6}			@ have to reload r0 - r6
	b	local_restart			@ ... and off we go

/*
 * "slow" syscall return path.  "why" tells us if this was a real syscall.
 */
@@ -396,6 +400,7 @@ ENTRY(vector_swi)
	eor	scno, scno, #__NR_SYSCALL_BASE	@ check OS number
#endif

local_restart:
	ldr	r10, [tsk, #TI_FLAGS]		@ check for syscall tracing
	stmdb	sp!, {r4, r5}			@ push fifth and sixth args

+29 −21
Original line number Diff line number Diff line
@@ -569,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
 * the kernel can handle, and then we build all the user-level signal handling
 * stack-frames in one go after that.
 */
static void do_signal(struct pt_regs *regs, int syscall)
static int do_signal(struct pt_regs *regs, int syscall)
{
	unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
	struct k_sigaction ka;
	siginfo_t info;
	int signr;
	int restart = 0;

	/*
	 * If we were from a system call, check for system call restarting...
@@ -589,10 +590,12 @@ static void do_signal(struct pt_regs *regs, int syscall)
		 * debugger will see the already changed PSW.
		 */
		switch (retval) {
		case -ERESTART_RESTARTBLOCK:
			restart++;
		case -ERESTARTNOHAND:
		case -ERESTARTSYS:
		case -ERESTARTNOINTR:
		case -ERESTART_RESTARTBLOCK:
			restart++;
			regs->ARM_r0 = regs->ARM_ORIG_r0;
			regs->ARM_pc = restart_addr;
			break;
@@ -604,13 +607,15 @@ static void do_signal(struct pt_regs *regs, int syscall)
	 * point the debugger may change all our registers ...
	 */
	signr = get_signal_to_deliver(&info, &ka, regs, NULL);
	if (signr > 0) {
	/*
	 * Depending on the signal settings we may need to revert the
	 * decision to restart the system call.  But skip this if a
	 * debugger has chosen to restart at a different PC.
	 */
		if (regs->ARM_pc == restart_addr) {
	if (regs->ARM_pc != restart_addr)
		restart = 0;
	if (signr > 0) {
		if (unlikely(restart)) {
			if (retval == -ERESTARTNOHAND ||
			    retval == -ERESTART_RESTARTBLOCK
			    || (retval == -ERESTARTSYS
@@ -618,28 +623,23 @@ static void do_signal(struct pt_regs *regs, int syscall)
				regs->ARM_r0 = -EINTR;
				regs->ARM_pc = continue_addr;
			}
			clear_thread_flag(TIF_SYSCALL_RESTARTSYS);
		}

		handle_signal(signr, &ka, &info, regs);
		return;
		return 0;
	}

	if (syscall) {
		/*
		 * Handle restarting a different system call.  As above,
		 * if a debugger has chosen to restart at a different PC,
		 * ignore the restart.
		 */
		if (retval == -ERESTART_RESTARTBLOCK
		    && regs->ARM_pc == restart_addr)
	if (unlikely(restart)) {
		if (restart > 1)
			set_thread_flag(TIF_SYSCALL_RESTARTSYS);
		regs->ARM_pc = continue_addr;
	}

	restore_saved_sigmask();
	return restart;
}

asmlinkage void
asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{
	do {
@@ -647,10 +647,17 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
			schedule();
		} else {
			if (unlikely(!user_mode(regs)))
				return;
				return 0;
			local_irq_enable();
			if (thread_flags & _TIF_SIGPENDING) {
				do_signal(regs, syscall);
				if (unlikely(do_signal(regs, syscall))) {
					/*
					 * Restart without handlers.
					 * Deal with it without leaving
					 * the kernel space.
					 */
					return 1;
				}
				syscall = 0;
			} else {
				clear_thread_flag(TIF_NOTIFY_RESUME);
@@ -660,4 +667,5 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
		local_irq_disable();
		thread_flags = current_thread_info()->flags;
	} while (thread_flags & _TIF_WORK_MASK);
	return 0;
}