[RFC] Attempt to clean up sigsuspend et al

Subsystems: linux for powerpc (32-bit and 64-bit), the rest

10 messages, 2 authors, 2005-11-15 · open the first message on its own page

[RFC] Attempt to clean up sigsuspend et al

From: David Woodhouse <dwmw2@infradead.org>
Date: 2005-11-11 21:36:39

We currently play tricks with assembly wrappers for various syscalls, in
case a signal is delivered. We need to make sure the registers are all
saved for the benefit of ptrace, and we need to return to userspace
without clobbering r4 and r5, as the normal syscall exit path would.

As usual, these tricks differ subtly between ppc32 and ppc64 too.

This is an attempt to get rid of it all, motivated mostly because I want
to add two _new_ functions which would need such treatment (pselect,
ppoll).

We define a new thread flag TIF_RESTOREALL which is set by the syscall
which needs this treatment, and make the syscall return path honour it.
This seems to work as far as I've tested it (i.e. the system boots) for
ppc64, but I haven't even attempted to compile the 32-bit version yet.

I've also removed the explicit calls to ptrace_notify() which were in
#ifdef CONFIG_PPC64 -- if we're now stopping on the way back to
userspace, we don't need to do that any more. I'm going to need to go
through some of the sigsuspend/signal cases with a fine-tooth comb and
make sure we're still getting a ptrace stop (and only _one_ ptrace stop)
in all cases where that's appropriate.

I also tried to clean up entry_64.S so it only checks the thread flags
in the fast path _once_. This means that for ptrace we'll disable
interrupts, then check the flags and enable interrupts again before
calling do_syscall_trace_leave(). That makes the slow path match the
PPC32 code and makes the fast path smaller.

Comments?
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 2e99ae4..59dc2e7 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -222,25 +222,25 @@ ret_from_syscall:
 	bl	do_show_syscall_exit
 #endif
 	mr	r6,r3
-	li	r11,-_LAST_ERRNO
-	cmplw	0,r3,r11
+	lwz	r11,_CCR(r1)			/* Load CR */
+	li	r10,-_LAST_ERRNO
+	cmplw	0,r3,r10
 	rlwinm	r12,r1,0,0,(31-THREAD_SHIFT)	/* current_thread_info() */
 	blt+	30f
-	lbz	r11,TI_SC_NOERR(r12)
-	cmpwi	r11,0
+	lbz	r10,TI_SC_NOERR(r12)
+	cmpwi	r10,0
 	bne	30f
 	neg	r3,r3
-	lwz	r10,_CCR(r1)	/* Set SO bit in CR */
-	oris	r10,r10,0x1000
-	stw	r10,_CCR(r1)
+	oris	r11,r11,0x1000	/* Set SO bit in CR */
 
 	/* disable interrupts so current_thread_info()->flags can't change */
 30:	LOAD_MSR_KERNEL(r10,MSR_KERNEL)	/* doesn't include MSR_EE */
 	SYNC
 	MTMSRD(r10)
 	lwz	r9,TI_FLAGS(r12)
-	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED)
+	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL)
 	bne-	syscall_exit_work
+	stw	r11,_CCR(r1)
 syscall_exit_cont:
 #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
 	/* If the process has its own DBCR0 value, load it up.  The single
@@ -292,9 +292,13 @@ syscall_dotrace:
 	b	syscall_dotrace_cont
 
 syscall_exit_work:
+	andi.	r0,r9,_TIF_RESTOREALL
+	bne-	3f
+
 	stw	r6,RESULT(r1)	/* Save result */
 	stw	r3,GPR3(r1)	/* Update return value */
-	andi.	r0,r9,_TIF_SYSCALL_T_OR_A
+	stw	r11,_CCR(r1)
+3:	andi.	r0,r9,_TIF_SYSCALL_T_OR_A
 	beq	5f
 	ori	r10,r10,MSR_EE
 	SYNC
@@ -407,22 +411,6 @@ show_syscalls_task:
  * to save all the nonvolatile registers (r13 - r31) before calling
  * the C code.
  */
-	.globl	ppc_sigsuspend
-ppc_sigsuspend:
-	SAVE_NVGPRS(r1)
-	lwz	r0,_TRAP(r1)
-	rlwinm	r0,r0,0,0,30		/* clear LSB to indicate full */
-	stw	r0,_TRAP(r1)		/* register set saved */
-	b	sys_sigsuspend
-
-	.globl	ppc_rt_sigsuspend
-ppc_rt_sigsuspend:
-	SAVE_NVGPRS(r1)
-	lwz	r0,_TRAP(r1)
-	rlwinm	r0,r0,0,0,30
-	stw	r0,_TRAP(r1)
-	b	sys_rt_sigsuspend
-
 	.globl	ppc_fork
 ppc_fork:
 	SAVE_NVGPRS(r1)
@@ -447,14 +435,6 @@ ppc_clone:
 	stw	r0,_TRAP(r1)		/* register set saved */
 	b	sys_clone
 
-	.globl	ppc_swapcontext
-ppc_swapcontext:
-	SAVE_NVGPRS(r1)
-	lwz	r0,_TRAP(r1)
-	rlwinm	r0,r0,0,0,30		/* clear LSB to indicate full */
-	stw	r0,_TRAP(r1)		/* register set saved */
-	b	sys_swapcontext
-
 /*
  * Top-level page fault handling.
  * This is in assembler because if do_page_fault tells us that
@@ -626,16 +606,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_601)
 	.long	ret_from_except
 #endif
 
-	.globl	sigreturn_exit
-sigreturn_exit:
-	subi	r1,r3,STACK_FRAME_OVERHEAD
-	rlwinm	r12,r1,0,0,(31-THREAD_SHIFT)	/* current_thread_info() */
-	lwz	r9,TI_FLAGS(r12)
-	andi.	r0,r9,_TIF_SYSCALL_T_OR_A
-	beq+	ret_from_except_full
-	bl	do_syscall_trace_leave
-	/* fall through */
-
 	.globl	ret_from_except_full
 ret_from_except_full:
 	REST_NVGPRS(r1)
@@ -658,7 +628,7 @@ user_exc_return:		/* r10 contains MSR_KE
 	/* Check current_thread_info()->flags */
 	rlwinm	r9,r1,0,0,(31-THREAD_SHIFT)
 	lwz	r9,TI_FLAGS(r9)
-	andi.	r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED)
+	andi.	r0,r9,(_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL)
 	bne	do_work
 
 restore_user:
@@ -876,6 +846,8 @@ load_dbcr0:
 #endif /* !(CONFIG_4xx || CONFIG_BOOKE) */
 
 do_work:			/* r10 contains MSR_KERNEL here */
+	andi.	r0,r9,_TIF_RESTOREALL
+	bne-	do_clear_restoreall	
 	andi.	r0,r9,_TIF_NEED_RESCHED
 	beq	do_user_signal
 
@@ -890,6 +862,8 @@ recheck:
 	MTMSRD(r10)		/* disable interrupts */
 	rlwinm	r9,r1,0,0,(31-THREAD_SHIFT)
 	lwz	r9,TI_FLAGS(r9)
+	andi.	r0,r9,_TIF_RESTOREALL
+	bne-	do_clear_restoreall
 	andi.	r0,r9,_TIF_NEED_RESCHED
 	bne-	do_resched
 	andi.	r0,r9,_TIF_SIGPENDING
@@ -911,6 +885,9 @@ do_user_signal:			/* r10 contains MSR_KE
 	REST_NVGPRS(r1)
 	b	recheck
 
+do_clear_restoreall:
+	bl	do_clear_tif_restoreall
+	b	recheck
 /*
  * We come here when we are at the end of handling an exception
  * that occurred at a place where taking an exception will lose
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 2d22bf0..756663e 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -157,12 +157,6 @@ syscall_exit:
 	bge-	syscall_error
 syscall_error_cont:
 
-	/* check for syscall tracing or audit */
-	ld	r9,TI_FLAGS(r12)
-	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
-	bne-	syscall_exit_trace
-syscall_exit_trace_cont:
-
 	/* disable interrupts so current_thread_info()->flags can't change,
 	   and so that we don't get interrupted after loading SRR0/1. */
 	ld	r8,_MSR(r1)
@@ -173,8 +167,9 @@ syscall_exit_trace_cont:
 	rotldi	r10,r10,16
 	mtmsrd	r10,1
 	ld	r9,TI_FLAGS(r12)
-	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED)
+	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL)
 	bne-	syscall_exit_work
+	std	r5,_CCR(r1)
 	ld	r7,_NIP(r1)
 	stdcx.	r0,0,r1			/* to clear the reservation */
 	andi.	r6,r8,MSR_PR
@@ -205,7 +200,6 @@ syscall_error:
 	bne-	syscall_error_cont
 	neg	r3,r3
 	oris	r5,r5,0x1000	/* Set SO bit in CR */
-	std	r5,_CCR(r1)
 	b	syscall_error_cont
         
 /* Traced system call support */
@@ -225,8 +219,22 @@ syscall_dotrace:
 	ld	r10,TI_FLAGS(r10)
 	b	syscall_dotrace_cont
 
-syscall_exit_trace:
+syscall_exit_work:
+	/* Avoid saving r3 and ccr if TIF_RESTOREALL flag is set */
+	andi.	r0,r9,_TIF_RESTOREALL
+	bne-	1f
+
 	std	r3,GPR3(r1)
+	std	r5,_CCR(r1)
+
+	/* If tracing, re-enable interrupts and do it */
+1:	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
+	beq	.ret_from_except_lite
+
+	mfmsr	r10
+	ori	r10,r10,MSR_EE
+	mtmsrd	r10,1
+	
 	bl	.save_nvgprs
 	addi	r3,r1,STACK_FRAME_OVERHEAD
 	bl	.do_syscall_trace_leave
@@ -234,12 +242,14 @@ syscall_exit_trace:
 	ld	r3,GPR3(r1)
 	ld	r5,_CCR(r1)
 	clrrdi	r12,r1,THREAD_SHIFT
-	b	syscall_exit_trace_cont
 
-/* Stuff to do on exit from a system call. */
-syscall_exit_work:
-	std	r3,GPR3(r1)
-	std	r5,_CCR(r1)
+	/* Disable interrupts again and handle other work if any */
+	mfmsr	r10
+	rldicl	r10,r10,48,1
+	rotldi	r10,r10,16
+	mtmsrd	r10,1
+	
+	ld	r9,TI_FLAGS(r12)
 	b	.ret_from_except_lite
 
 /* Save non-volatile GPRs, if not already saved. */
@@ -260,35 +270,6 @@ _GLOBAL(save_nvgprs)
  * the C code.  Similarly, fork, vfork and clone need the full
  * register state on the stack so that it can be copied to the child.
  */
-_GLOBAL(ppc32_sigsuspend)
-	bl	.save_nvgprs
-	bl	.compat_sys_sigsuspend
-	b	70f
-
-_GLOBAL(ppc64_rt_sigsuspend)
-	bl	.save_nvgprs
-	bl	.sys_rt_sigsuspend
-	b	70f
-
-_GLOBAL(ppc32_rt_sigsuspend)
-	bl	.save_nvgprs
-	bl	.compat_sys_rt_sigsuspend
-70:	cmpdi	0,r3,0
-	/* If it returned an error, we need to return via syscall_exit to set
-	   the SO bit in cr0 and potentially stop for ptrace. */
-	bne	syscall_exit
-	/* If sigsuspend() returns zero, we are going into a signal handler. We
-	   may need to call audit_syscall_exit() to mark the exit from sigsuspend() */
-#ifdef CONFIG_AUDITSYSCALL
-	ld	r3,PACACURRENT(r13)
-	ld	r4,AUDITCONTEXT(r3)
-	cmpdi	0,r4,0
-	beq	.ret_from_except	/* No audit_context: Leave immediately. */
-	li	r4, 2			/* AUDITSC_FAILURE */
-	li	r5,-4			/* It's always -EINTR */
-	bl	.audit_syscall_exit
-#endif
-	b	.ret_from_except
 
 _GLOBAL(ppc_fork)
 	bl	.save_nvgprs
@@ -305,37 +286,6 @@ _GLOBAL(ppc_clone)
 	bl	.sys_clone
 	b	syscall_exit
 
-_GLOBAL(ppc32_swapcontext)
-	bl	.save_nvgprs
-	bl	.compat_sys_swapcontext
-	b	80f
-	
-_GLOBAL(ppc64_swapcontext)
-	bl	.save_nvgprs
-	bl	.sys_swapcontext
-	b	80f
-
-_GLOBAL(ppc32_sigreturn)
-	bl	.compat_sys_sigreturn
-	b	80f
-
-_GLOBAL(ppc32_rt_sigreturn)
-	bl	.compat_sys_rt_sigreturn
-	b	80f
-
-_GLOBAL(ppc64_rt_sigreturn)
-	bl	.sys_rt_sigreturn
-
-80:	cmpdi	0,r3,0
-	blt	syscall_exit
-	clrrdi	r4,r1,THREAD_SHIFT
-	ld	r4,TI_FLAGS(r4)
-	andi.	r4,r4,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
-	beq+	81f
-	addi	r3,r1,STACK_FRAME_OVERHEAD
-	bl	.do_syscall_trace_leave
-81:	b	.ret_from_except
-
 _GLOBAL(ret_from_fork)
 	bl	.schedule_tail
 	REST_NVGPRS(r1)
@@ -602,7 +552,12 @@ user_work:
 	bl	.schedule
 	b	.ret_from_except_lite
 
-1:	bl	.save_nvgprs
+1:	andi.	r0,r4,_TIF_RESTOREALL
+	beq	2f
+	bl	.do_clear_tif_restoreall
+	b	.ret_from_except_lite
+
+2:	bl	.save_nvgprs
 	li	r3,0
 	addi	r4,r1,STACK_FRAME_OVERHEAD
 	bl	.do_signal
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 081d931..d18aebd 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -75,7 +75,6 @@
  * registers from *regs.  This is what we need
  * to do when a signal has been delivered.
  */
-#define sigreturn_exit(regs)	return 0
 
 #define GP_REGS_SIZE	min(sizeof(elf_gregset_t32), sizeof(struct pt_regs32))
 #undef __SIGNAL_FRAMESIZE
@@ -178,8 +177,6 @@ static inline int restore_general_regs(s
 
 #else /* CONFIG_PPC64 */
 
-extern void sigreturn_exit(struct pt_regs *);
-
 #define GP_REGS_SIZE	min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
 
 static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
@@ -255,8 +252,10 @@ long sys_sigsuspend(old_sigset_t mask, i
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
-			sigreturn_exit(regs);
+		if (do_signal(&saveset, regs)) {
+			set_thread_flag(TIF_RESTOREALL);
+			return 0;
+		}
 	}
 }
 
@@ -291,8 +290,10 @@ long sys_rt_sigsuspend(
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
-			sigreturn_exit(regs);
+		if (do_signal(&saveset, regs)) {
+			set_thread_flag(TIF_RESTOREALL);
+			return 0;
+		}
 	}
 }
 
@@ -829,12 +830,6 @@ static int handle_rt_signal(unsigned lon
 	regs->gpr[6] = (unsigned long) rt_sf;
 	regs->nip = (unsigned long) ka->sa.sa_handler;
 	regs->trap = 0;
-#ifdef CONFIG_PPC64
-	regs->result = 0;
-
-	if (test_thread_flag(TIF_SINGLESTEP))
-		ptrace_notify(SIGTRAP);
-#endif
 	return 1;
 
 badframe:
@@ -912,8 +907,8 @@ long sys_swapcontext(struct ucontext __u
 	 */
 	if (do_setcontext(new_ctx, regs, 0))
 		do_exit(SIGSEGV);
-	sigreturn_exit(regs);
-	/* doesn't actually return back to here */
+
+	set_thread_flag(TIF_RESTOREALL);
 	return 0;
 }
 
@@ -946,12 +941,11 @@ long sys_rt_sigreturn(int r3, int r4, in
 	 * nobody does any...
 	 */
 	compat_sys_sigaltstack((u32)(u64)&rt_sf->uc.uc_stack, 0, 0, 0, 0, 0, regs);
-	return (int)regs->result;
 #else
 	do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]);
-	sigreturn_exit(regs);		/* doesn't return here */
-	return 0;
 #endif
+	set_thread_flag(TIF_RESTOREALL);
+	return 0;
 
  bad:
 	force_sig(SIGSEGV, current);
@@ -1042,9 +1036,7 @@ int sys_debug_setcontext(struct ucontext
 	 */
 	do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]);
 
-	sigreturn_exit(regs);
-	/* doesn't actually return back to here */
-
+	set_thread_flag(TIF_RESTOREALL);
  out:
 	return 0;
 }
@@ -1109,12 +1101,6 @@ static int handle_signal(unsigned long s
 	regs->gpr[4] = (unsigned long) sc;
 	regs->nip = (unsigned long) ka->sa.sa_handler;
 	regs->trap = 0;
-#ifdef CONFIG_PPC64
-	regs->result = 0;
-
-	if (test_thread_flag(TIF_SINGLESTEP))
-		ptrace_notify(SIGTRAP);
-#endif
 
 	return 1;
 
@@ -1162,12 +1148,8 @@ long sys_sigreturn(int r3, int r4, int r
 	    || restore_user_regs(regs, sr, 1))
 		goto badframe;
 
-#ifdef CONFIG_PPC64
-	return (int)regs->result;
-#else
-	sigreturn_exit(regs);		/* doesn't return */
+	set_thread_flag(TIF_RESTOREALL);
 	return 0;
-#endif
 
 badframe:
 	force_sig(SIGSEGV, current);
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 58194e1..7520ad4 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -96,8 +96,10 @@ long sys_rt_sigsuspend(sigset_t __user *
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs)) {
+			set_thread_flag(TIF_RESTOREALL);
 			return 0;
+		}
 	}
 }
 
@@ -343,6 +345,7 @@ int sys_swapcontext(struct ucontext __us
 		do_exit(SIGSEGV);
 
 	/* This returns like rt_sigreturn */
+	set_thread_flag(TIF_RESTOREALL);
 	return 0;
 }
 
@@ -375,7 +378,8 @@ int sys_rt_sigreturn(unsigned long r3, u
 	 */
 	do_sigaltstack(&uc->uc_stack, NULL, regs->gpr[1]);
 
-	return regs->result;
+	set_thread_flag(TIF_RESTOREALL);
+	return 0;
 
 badframe:
 #if DEBUG_SIG
@@ -454,9 +458,6 @@ static int setup_rt_frame(int signr, str
 	if (err)
 		goto badframe;
 
-	if (test_thread_flag(TIF_SINGLESTEP))
-		ptrace_notify(SIGTRAP);
-
 	return 1;
 
 badframe:
diff --git a/arch/powerpc/kernel/syscalls.c b/arch/powerpc/kernel/syscalls.c
index f72ced1..f8ec816 100644
--- a/arch/powerpc/kernel/syscalls.c
+++ b/arch/powerpc/kernel/syscalls.c
@@ -356,3 +356,8 @@ void do_show_syscall_exit(unsigned long 
 {
 	printk(" -> %lx, current=%p cpu=%d\n", r3, current, smp_processor_id());
 }
+
+void do_clear_tif_restoreall(void)
+{
+	clear_thread_flag(TIF_RESTOREALL);
+}
diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S
index 65eaea9..4bb3650 100644
--- a/arch/powerpc/kernel/systbl.S
+++ b/arch/powerpc/kernel/systbl.S
@@ -113,7 +113,7 @@ SYSCALL(sgetmask)
 COMPAT_SYS(ssetmask)
 SYSCALL(setreuid)
 SYSCALL(setregid)
-SYSX(sys_ni_syscall,ppc32_sigsuspend,ppc_sigsuspend)
+SYS32ONLY(sigsuspend)
 COMPAT_SYS(sigpending)
 COMPAT_SYS(sethostname)
 COMPAT_SYS(setrlimit)
@@ -160,7 +160,7 @@ SYSCALL(swapoff)
 COMPAT_SYS(sysinfo)
 COMPAT_SYS(ipc)
 SYSCALL(fsync)
-SYSX(sys_ni_syscall,ppc32_sigreturn,sys_sigreturn)
+SYS32ONLY(sigreturn)
 PPC_SYS(clone)
 COMPAT_SYS(setdomainname)
 PPC_SYS(newuname)
@@ -213,13 +213,13 @@ COMPAT_SYS(nfsservctl)
 SYSCALL(setresgid)
 SYSCALL(getresgid)
 COMPAT_SYS(prctl)
-SYSX(ppc64_rt_sigreturn,ppc32_rt_sigreturn,sys_rt_sigreturn)
+COMPAT_SYS(rt_sigreturn)
 COMPAT_SYS(rt_sigaction)
 COMPAT_SYS(rt_sigprocmask)
 COMPAT_SYS(rt_sigpending)
 COMPAT_SYS(rt_sigtimedwait)
 COMPAT_SYS(rt_sigqueueinfo)
-SYSX(ppc64_rt_sigsuspend,ppc32_rt_sigsuspend,ppc_rt_sigsuspend)
+COMPAT_SYS(rt_sigsuspend)
 COMPAT_SYS(pread64)
 COMPAT_SYS(pwrite64)
 SYSCALL(chown)
@@ -290,7 +290,7 @@ COMPAT_SYS(clock_settime)
 COMPAT_SYS(clock_gettime)
 COMPAT_SYS(clock_getres)
 COMPAT_SYS(clock_nanosleep)
-SYSX(ppc64_swapcontext,ppc32_swapcontext,ppc_swapcontext)
+COMPAT_SYS(swapcontext)
 COMPAT_SYS(tgkill)
 COMPAT_SYS(utimes)
 COMPAT_SYS(statfs64)
diff --git a/include/asm-powerpc/thread_info.h b/include/asm-powerpc/thread_info.h
index e525f49..d10ec17 100644
--- a/include/asm-powerpc/thread_info.h
+++ b/include/asm-powerpc/thread_info.h
@@ -123,6 +123,7 @@ static inline struct thread_info *curren
 #define TIF_SINGLESTEP		9	/* singlestepping active */
 #define TIF_MEMDIE		10
 #define TIF_SECCOMP		11	/* secure computing */
+#define TIF_RESTOREALL		12	/* signal handler invoked */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -136,10 +137,11 @@ static inline struct thread_info *curren
 #define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
+#define _TIF_RESTOREALL		(1<<TIF_RESTOREALL)
 #define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
 
 #define _TIF_USER_WORK_MASK	(_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \
-				 _TIF_NEED_RESCHED)
+				 _TIF_NEED_RESCHED | _TIF_RESTOREALL)
 
 #endif /* __KERNEL__ */
 

-- 
dwmw2

Re: [RFC] Attempt to clean up sigsuspend et al

From: Paul Mackerras <hidden>
Date: 2005-11-12 00:28:31

David Woodhouse writes:
This is an attempt to get rid of it all, motivated mostly because I want
to add two _new_ functions which would need such treatment (pselect,
ppoll).

We define a new thread flag TIF_RESTOREALL which is set by the syscall
which needs this treatment, and make the syscall return path honour it.
This seems to work as far as I've tested it (i.e. the system boots) for
ppc64, but I haven't even attempted to compile the 32-bit version yet.
Ummm, I don't see where you save the non-volatile GPRs to the
exception frame _before_ do_signal (called from within *_sigsuspend)
copies them to the user stack...

Paul.

Re: [RFC] Attempt to clean up sigsuspend et al

From: David Woodhouse <dwmw2@infradead.org>
Date: 2005-11-12 00:48:54

On Sat, 2005-11-12 at 11:28 +1100, Paul Mackerras wrote:
Ummm, I don't see where you save the non-volatile GPRs to the
exception frame _before_ do_signal (called from within *_sigsuspend)
copies them to the user stack...
Hm, yeah.

I suppose I could make a TIF_xxx flag make entry.S do that. It's not
massively pretty, but then it wasn't pretty beforehand either. At least
I'd have made the ppc32 and ppc64 code match, removed the assembly
wrappers for a bunch of syscalls, and shortened the syscall exit fast
path on ppc64. 

I'd probably still be removing more lines of code than I add, but I'm
not entirely convinced by the whole plan. What do you think?

Failing that, I'll just switch them both to do either what ppc32
currently does with sigreturn_exit() or what ppc64 does with returning
zero to take the ret_from_except path. Probably the former, since
pselect() and ppoll() will sometimes want to return zero without
invoking a signal handler.  And we can probably shorten the ppc64
syscall exit path anyway, either way. 

-- 
dwmw2

Re: [RFC] Attempt to clean up sigsuspend et al

From: Paul Mackerras <hidden>
Date: 2005-11-13 06:58:46

David Woodhouse writes:
I suppose I could make a TIF_xxx flag make entry.S do that. It's not
massively pretty, but then it wasn't pretty beforehand either. At least
I'd have made the ppc32 and ppc64 code match, removed the assembly
wrappers for a bunch of syscalls, and shortened the syscall exit fast
path on ppc64. 

I'd probably still be removing more lines of code than I add, but I'm
not entirely convinced by the whole plan. What do you think?
Or have a mark in the syscall table (e.g. set the bottom bit of the
address) for the syscalls that need the full register set.  That does
mean an extra conditional branch in the syscall entry path for all
syscalls, though.
Failing that, I'll just switch them both to do either what ppc32
currently does with sigreturn_exit() or what ppc64 does with returning
zero to take the ret_from_except path. Probably the former, since
pselect() and ppoll() will sometimes want to return zero without
invoking a signal handler.  And we can probably shorten the ppc64
syscall exit path anyway, either way. 
I'll be upset if you can shorten it by a lot - I thought I had it
pretty tight already. :)

Paul.

Re: [RFC] Attempt to clean up sigsuspend et al

From: David Woodhouse <dwmw2@infradead.org>
Date: 2005-11-13 09:41:33

On Sun, 2005-11-13 at 17:58 +1100, Paul Mackerras wrote:
Or have a mark in the syscall table (e.g. set the bottom bit of the
address) for the syscalls that need the full register set.  That does
mean an extra conditional branch in the syscall entry path for all
syscalls, though.
We don't need to do it on entry; we can do it on exit. If do_signal()
sees that the nvgprs weren't saved in the pt_regs it's playing with,
then it can set a new TIF_SAVENVPGRS_TO_SIG_FRAME bit. I don't think we
even need to store a pointer saying where to put them -- the signal
frame will always be at the top of the userspace stack.

As long as we handle the new bit before TIF_SIGPENDING and before
another call to do_signal(), we don't need to worry about recursive
signals and having to fill in more than one signal frame.

-- 
dwmw2

Re: [RFC] Attempt to clean up sigsuspend et al

From: Paul Mackerras <hidden>
Date: 2005-11-13 10:09:06

David Woodhouse writes:
We don't need to do it on entry; we can do it on exit. If do_signal()
sees that the nvgprs weren't saved in the pt_regs it's playing with,
then it can set a new TIF_SAVENVPGRS_TO_SIG_FRAME bit. I don't think we
even need to store a pointer saying where to put them -- the signal
frame will always be at the top of the userspace stack.
Two problems with that: (1) for *sigsuspend, if do_signal returns 0,
then we need to go back and keep waiting, and (2) it doesn't handle
the other syscalls that need the full register set, such as fork,
clone etc.  Also, for the *sigsuspend case, we need to pass the saved
signal set to do_signal, not NULL (as the call from the exception exit
path does).

Paul.

Re: [RFC] Attempt to clean up sigsuspend et al

From: David Woodhouse <dwmw2@infradead.org>
Date: 2005-11-13 11:58:27

On Sun, 2005-11-13 at 21:09 +1100, Paul Mackerras wrote:
quoted
We don't need to do it on entry; we can do it on exit. If do_signal()
sees that the nvgprs weren't saved in the pt_regs it's playing with,
then it can set a new TIF_SAVENVPGRS_TO_SIG_FRAME bit. I don't think we
even need to store a pointer saying where to put them -- the signal
frame will always be at the top of the userspace stack.
Two problems with that: (1) for *sigsuspend, if do_signal returns 0,
then we need to go back and keep waiting, and (2) it doesn't handle
the other syscalls that need the full register set, such as fork,
clone etc.  Also, for the *sigsuspend case, we need to pass the saved
signal set to do_signal, not NULL (as the call from the exception exit
path does).
It doesn't address fork/vfork/clone; certainly. I haven't really looked
at that code path at all yet. It was the *suspend() functions which
offended me, mostly because I need similar behaviour for pselect() and
ppoll() and I didn't want to add to the mess.

But I think other things you mention are OK -- we'd still call
do_signal() from *sigsuspend() just as we do at the moment, with the
same loop. It's just that do_signal() would notice that the nvgprs
aren't saved, and would set the TIF_SAVENVPGRS_TO_SIG_FRAME bit. ..

	if (!regs->trap & 1)
		set_thread_flag(TIF_SAVENVGPRS_SIG);

We end up returning to userspace via entry.S, and the code there notices
the SAVENVGPRS bit and saves _just_ r14-r31 into the signal frame. It
doesn't have to call do_signal() again, so doesn't have to get the right
saved signal mask when doing so.

(Actually in the case of swapcontext() we don't know that the nvgprs
should be saved in a signal frame at the top of the userspace stack; the
context will be elsewhere. We probably do need to store a pointer in the
thread_info, but that's OK too)

-- 
dwmw2

Re: [RFC] Attempt to clean up sigsuspend et al

From: Paul Mackerras <hidden>
Date: 2005-11-14 04:43:22

David Woodhouse writes:
But I think other things you mention are OK -- we'd still call
do_signal() from *sigsuspend() just as we do at the moment, with the
same loop. It's just that do_signal() would notice that the nvgprs
aren't saved, and would set the TIF_SAVENVPGRS_TO_SIG_FRAME bit. ..
Ah ok, I see now, that sounds all right.
(Actually in the case of swapcontext() we don't know that the nvgprs
should be saved in a signal frame at the top of the userspace stack; the
context will be elsewhere. We probably do need to store a pointer in the
thread_info, but that's OK too)
True.

Paul.

Re: [RFC] Attempt to clean up sigsuspend et al

From: David Woodhouse <dwmw2@infradead.org>
Date: 2005-11-14 20:04:44

On Mon, 2005-11-14 at 15:43 +1100, Paul Mackerras wrote:
Ah ok, I see now, that sounds all right.
I've also changed force_successful_syscall_return() to use a TIF flag,
so we don't have to clear ti->syscall_noerror in the syscall entry path
and that can shrink too.

The syscall exit path now checks ti->flags only once in the fast path,
instead of checking it twice as it did before. All the interesting stuff
is done in the slow path.

We lose the assembly wrappers for the signal-related functions, and
clean up the syscall table to call them directly.

It looks a bit like this... I still need to test it a little harder than
"Yes, it boots", and I need to sync up the 32-bit version. Then we can
have syscall entry/exit paths that actually look similar to each other.

What do you think?
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index b757572..e3e6081 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -92,9 +92,9 @@ int main(void)
 
 	DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
 	DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
-	DEFINE(TI_SC_NOERR, offsetof(struct thread_info, syscall_noerror));
-#ifdef CONFIG_PPC32
+	DEFINE(TI_SIGFRAME, offsetof(struct thread_info, nvgprs_frame));
 	DEFINE(TI_TASK, offsetof(struct thread_info, task));
+#ifdef CONFIG_PPC32
 	DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain));
 	DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
 #endif /* CONFIG_PPC32 */
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 2d22bf0..564452b 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -113,9 +113,7 @@ system_call_common:
 	addi	r9,r1,STACK_FRAME_OVERHEAD
 #endif
 	clrrdi	r11,r1,THREAD_SHIFT
-	li	r12,0
 	ld	r10,TI_FLAGS(r11)
-	stb	r12,TI_SC_NOERR(r11)
 	andi.	r11,r10,_TIF_SYSCALL_T_OR_A
 	bne-	syscall_dotrace
 syscall_dotrace_cont:
@@ -145,23 +143,11 @@ system_call:			/* label this so stack tr
 
 syscall_exit:
 #ifdef SHOW_SYSCALLS
-	std	r3,GPR3(r1)
+	std	r3,RESULT(r1)
 	bl	.do_show_syscall_exit
-	ld	r3,GPR3(r1)
+	ld	r3,RESULT(r1)
 #endif
-	std	r3,RESULT(r1)
-	ld	r5,_CCR(r1)
-	li	r10,-_LAST_ERRNO
-	cmpld	r3,r10
 	clrrdi	r12,r1,THREAD_SHIFT
-	bge-	syscall_error
-syscall_error_cont:
-
-	/* check for syscall tracing or audit */
-	ld	r9,TI_FLAGS(r12)
-	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
-	bne-	syscall_exit_trace
-syscall_exit_trace_cont:
 
 	/* disable interrupts so current_thread_info()->flags can't change,
 	   and so that we don't get interrupted after loading SRR0/1. */
@@ -173,8 +159,14 @@ syscall_exit_trace_cont:
 	rotldi	r10,r10,16
 	mtmsrd	r10,1
 	ld	r9,TI_FLAGS(r12)
-	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED)
+	li	r11,-_LAST_ERRNO
+	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SIGPENDING|_TIF_NEED_RESCHED|_TIF_RESTOREALL|_TIF_SAVE_NVGPRS|_TIF_NOERROR)
 	bne-	syscall_exit_work
+	std	r3,RESULT(r1)
+	cmpld	r3,r11
+	ld	r5,_CCR(r1)
+	bge-	syscall_error
+syscall_error_cont:
 	ld	r7,_NIP(r1)
 	stdcx.	r0,0,r1			/* to clear the reservation */
 	andi.	r6,r8,MSR_PR
@@ -193,21 +185,13 @@ syscall_exit_trace_cont:
 	rfid
 	b	.	/* prevent speculative execution */
 
-syscall_enosys:
-	li	r3,-ENOSYS
-	std	r3,RESULT(r1)
-	clrrdi	r12,r1,THREAD_SHIFT
-	ld	r5,_CCR(r1)
-
-syscall_error:
-	lbz	r11,TI_SC_NOERR(r12)
-	cmpwi	0,r11,0
-	bne-	syscall_error_cont
+syscall_error:	
 	neg	r3,r3
 	oris	r5,r5,0x1000	/* Set SO bit in CR */
+	std	r3,RESULT(r1)
 	std	r5,_CCR(r1)
 	b	syscall_error_cont
-        
+	
 /* Traced system call support */
 syscall_dotrace:
 	bl	.save_nvgprs
@@ -225,21 +209,72 @@ syscall_dotrace:
 	ld	r10,TI_FLAGS(r10)
 	b	syscall_dotrace_cont
 
-syscall_exit_trace:
+syscall_enosys:
+	li	r3,-ENOSYS
+	b	syscall_exit
+	
+syscall_exit_work:
+	/* If TIF_RESTOREALL is set, don't scribble on either r3 or ccr.
+	 If TIF_NOERROR is set, just save r3 as it is. */
+
+	andi.	r0,r9,_TIF_RESTOREALL
+	bne-	2f
+	cmpld	r3,r11		/* r10 is -LAST_ERRNO */
+	blt-	1f
+	andi.	r0,r9,_TIF_NOERROR
+	bne-	1f
+	ld	r5,_CCR(r1)
+	neg	r3,r3
+	oris	r5,r5,0x1000	/* Set SO bit in CR */
+	std	r5,_CCR(r1)
+1:
+	std	r3,RESULT(r1)
 	std	r3,GPR3(r1)
-	bl	.save_nvgprs
+
+2:	andi.	r0,r9,(_TIF_PERSYSCALL_MASK)
+	beq	4f
+
+	/* Clear per-syscall TIF flags if any are set, but _leave_
+	_TIF_SAVE_NVGPRS set in r9 since we haven't dealt with that
+	yet.  */
+
+	li r11,_TIF_PERSYSCALL_MASK
+	addi	r12,r12,TI_FLAGS
+3:	ldarx	r10,0,r12
+	andc	r10,r10,r11
+	stdcx.	r10,0,r12
+	bne-	3b
+	subi	r12,r12,TI_FLAGS
+	
+4:	bl	save_nvgprs
+	/* Anything else left to do? */
+	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP|_TIF_SAVE_NVGPRS)
+	beq	.ret_from_except_lite
+
+	/* Re-enable interrupts */
+	mfmsr	r10
+	ori	r10,r10,MSR_EE
+	mtmsrd	r10,1
+
+	andi.	r0,r9,_TIF_SAVE_NVGPRS
+	bne	save_user_nvgprs
+
+	/* If tracing, re-enable interrupts and do it */
+save_user_nvgprs_cont:	
+	andi.	r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
+	beq	5f
+	
 	addi	r3,r1,STACK_FRAME_OVERHEAD
 	bl	.do_syscall_trace_leave
 	REST_NVGPRS(r1)
-	ld	r3,GPR3(r1)
-	ld	r5,_CCR(r1)
 	clrrdi	r12,r1,THREAD_SHIFT
-	b	syscall_exit_trace_cont
 
-/* Stuff to do on exit from a system call. */
-syscall_exit_work:
-	std	r3,GPR3(r1)
-	std	r5,_CCR(r1)
+	/* Disable interrupts again and handle other work if any */
+5:	mfmsr	r10
+	rldicl	r10,r10,48,1
+	rotldi	r10,r10,16
+	mtmsrd	r10,1
+
 	b	.ret_from_except_lite
 
 /* Save non-volatile GPRs, if not already saved. */
@@ -252,6 +288,109 @@ _GLOBAL(save_nvgprs)
 	std	r0,_TRAP(r1)
 	blr
 
+
+save_user_nvgprs:
+	/* Re-enable interrupts before copying to user */
+	mfmsr	r10
+	ori	r10,r10,MSR_EE
+	mtmsrd	r10,1
+
+	ld	r10,TI_SIGFRAME(r9)
+	andi.	r0,r9,_TIF_32BIT
+	beq-	save_user_nvgprs_64
+
+	/* 32-bit save to userspace */
+101:	stw	r14,56(r10)
+102:	stw	r15,60(r10)
+103:	stw	r16,64(r10)
+104:	stw	r17,68(r10)
+105:	stw	r18,72(r10)
+106:	stw	r19,76(r10)
+107:	stw	r20,80(r10)
+108:	stw	r21,84(r10)
+109:	stw	r22,88(r10)
+110:	stw	r23,92(r10)
+111:	stw	r24,96(r10)
+112:	stw	r25,100(r10)	
+113:	stw	r26,104(r10)
+114:	stw	r27,108(r10)
+115:	stw	r28,112(r10)
+116:	stw	r29,116(r10)
+117:	stw	r30,120(r10)
+118:	stw	r31,124(r10)
+	b	save_user_nvgprs_cont
+
+save_user_nvgprs_64:
+	/* 64-bit save to userspace */
+119:	std	r14,112(r10)
+120:	std	r15,120(r10)
+121:	std	r16,128(r10)
+122:	std	r17,136(r10)
+123:	std	r18,144(r10)
+124:	std	r19,152(r10)
+125:	std	r20,160(r10)
+126:	std	r21,168(r10)
+127:	std	r22,176(r10)
+128:	std	r23,184(r10)
+129:	std	r24,192(r10)
+130:	std	r25,200(r10)
+131:	std	r26,208(r10)
+132:	std	r27,216(r10)
+133:	std	r28,224(r10)
+134:	std	r28,232(r10)
+135:	std	r28,240(r10)
+136:	std	r28,248(r10)
+	b	save_user_nvgprs_cont
+	
+	.section __ex_table,"a"
+	.align 3
+	.llong	101b,save_user_nvgprs_fault
+	.llong	102b,save_user_nvgprs_fault
+	.llong	103b,save_user_nvgprs_fault
+	.llong	104b,save_user_nvgprs_fault
+	.llong	105b,save_user_nvgprs_fault
+	.llong	106b,save_user_nvgprs_fault
+	.llong	107b,save_user_nvgprs_fault
+	.llong	108b,save_user_nvgprs_fault
+	.llong	109b,save_user_nvgprs_fault
+	.llong	110b,save_user_nvgprs_fault
+	.llong	111b,save_user_nvgprs_fault
+	.llong	112b,save_user_nvgprs_fault
+	.llong	113b,save_user_nvgprs_fault
+	.llong	114b,save_user_nvgprs_fault
+	.llong	115b,save_user_nvgprs_fault
+	.llong	116b,save_user_nvgprs_fault
+	.llong	117b,save_user_nvgprs_fault
+	.llong	118b,save_user_nvgprs_fault
+	.llong	119b,save_user_nvgprs_fault
+	.llong	120b,save_user_nvgprs_fault
+	.llong	121b,save_user_nvgprs_fault
+	.llong	122b,save_user_nvgprs_fault
+	.llong	123b,save_user_nvgprs_fault
+	.llong	124b,save_user_nvgprs_fault
+	.llong	125b,save_user_nvgprs_fault
+	.llong	126b,save_user_nvgprs_fault
+	.llong	127b,save_user_nvgprs_fault
+	.llong	128b,save_user_nvgprs_fault
+	.llong	129b,save_user_nvgprs_fault
+	.llong	130b,save_user_nvgprs_fault
+	.llong	131b,save_user_nvgprs_fault
+	.llong	132b,save_user_nvgprs_fault
+	.llong	133b,save_user_nvgprs_fault
+	.llong	134b,save_user_nvgprs_fault
+	.llong	135b,save_user_nvgprs_fault
+	.llong	136b,save_user_nvgprs_fault
+	.previous
+
+save_user_nvgprs_fault:
+	li	r3,9			// SIGSEGV
+	ld	r4,TI_TASK(r9)
+	bl	.force_sigsegv
+
+	clrrdi	r12,r1,THREAD_SHIFT
+	ld	r9,TI_FLAGS(r12)
+	b	save_user_nvgprs_cont
+	
 /*
  * The sigsuspend and rt_sigsuspend system calls can call do_signal
  * and thus put the process into the stopped state where we might
@@ -260,35 +399,6 @@ _GLOBAL(save_nvgprs)
  * the C code.  Similarly, fork, vfork and clone need the full
  * register state on the stack so that it can be copied to the child.
  */
-_GLOBAL(ppc32_sigsuspend)
-	bl	.save_nvgprs
-	bl	.compat_sys_sigsuspend
-	b	70f
-
-_GLOBAL(ppc64_rt_sigsuspend)
-	bl	.save_nvgprs
-	bl	.sys_rt_sigsuspend
-	b	70f
-
-_GLOBAL(ppc32_rt_sigsuspend)
-	bl	.save_nvgprs
-	bl	.compat_sys_rt_sigsuspend
-70:	cmpdi	0,r3,0
-	/* If it returned an error, we need to return via syscall_exit to set
-	   the SO bit in cr0 and potentially stop for ptrace. */
-	bne	syscall_exit
-	/* If sigsuspend() returns zero, we are going into a signal handler. We
-	   may need to call audit_syscall_exit() to mark the exit from sigsuspend() */
-#ifdef CONFIG_AUDITSYSCALL
-	ld	r3,PACACURRENT(r13)
-	ld	r4,AUDITCONTEXT(r3)
-	cmpdi	0,r4,0
-	beq	.ret_from_except	/* No audit_context: Leave immediately. */
-	li	r4, 2			/* AUDITSC_FAILURE */
-	li	r5,-4			/* It's always -EINTR */
-	bl	.audit_syscall_exit
-#endif
-	b	.ret_from_except
 
 _GLOBAL(ppc_fork)
 	bl	.save_nvgprs
@@ -305,37 +415,6 @@ _GLOBAL(ppc_clone)
 	bl	.sys_clone
 	b	syscall_exit
 
-_GLOBAL(ppc32_swapcontext)
-	bl	.save_nvgprs
-	bl	.compat_sys_swapcontext
-	b	80f
-	
-_GLOBAL(ppc64_swapcontext)
-	bl	.save_nvgprs
-	bl	.sys_swapcontext
-	b	80f
-
-_GLOBAL(ppc32_sigreturn)
-	bl	.compat_sys_sigreturn
-	b	80f
-
-_GLOBAL(ppc32_rt_sigreturn)
-	bl	.compat_sys_rt_sigreturn
-	b	80f
-
-_GLOBAL(ppc64_rt_sigreturn)
-	bl	.sys_rt_sigreturn
-
-80:	cmpdi	0,r3,0
-	blt	syscall_exit
-	clrrdi	r4,r1,THREAD_SHIFT
-	ld	r4,TI_FLAGS(r4)
-	andi.	r4,r4,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP)
-	beq+	81f
-	addi	r3,r1,STACK_FRAME_OVERHEAD
-	bl	.do_syscall_trace_leave
-81:	b	.ret_from_except
-
 _GLOBAL(ret_from_fork)
 	bl	.schedule_tail
 	REST_NVGPRS(r1)
diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index 081d931..88b86ac 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -75,7 +75,6 @@
  * registers from *regs.  This is what we need
  * to do when a signal has been delivered.
  */
-#define sigreturn_exit(regs)	return 0
 
 #define GP_REGS_SIZE	min(sizeof(elf_gregset_t32), sizeof(struct pt_regs32))
 #undef __SIGNAL_FRAMESIZE
@@ -155,9 +154,18 @@ static inline int save_general_regs(stru
 	elf_greg_t64 *gregs = (elf_greg_t64 *)regs;
 	int i;
 
-	for (i = 0; i <= PT_RESULT; i ++)
+	if (!FULL_REGS(regs)) {
+		printk("Save NVGPRS at %lx\n", (unsigned long)frame->mc_gregs);
+		set_thread_flag(TIF_SAVE_NVGPRS);
+		current_thread_info()->nvgprs_frame = frame->mc_gregs;
+	}
+
+	for (i = 0; i <= PT_RESULT; i ++) {
+		if (i == 14 && !FULL_REGS(regs))
+			i = 32;
 		if (__put_user((unsigned int)gregs[i], &frame->mc_gregs[i]))
 			return -EFAULT;
+	}
 	return 0;
 }
 
@@ -178,8 +186,6 @@ static inline int restore_general_regs(s
 
 #else /* CONFIG_PPC64 */
 
-extern void sigreturn_exit(struct pt_regs *);
-
 #define GP_REGS_SIZE	min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
 
 static inline int put_sigset_t(sigset_t __user *uset, sigset_t *set)
@@ -255,8 +261,10 @@ long sys_sigsuspend(old_sigset_t mask, i
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
-			sigreturn_exit(regs);
+		if (do_signal(&saveset, regs)) {
+			set_thread_flag(TIF_RESTOREALL);
+			return 0;
+		}
 	}
 }
 
@@ -291,8 +299,10 @@ long sys_rt_sigsuspend(
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
-			sigreturn_exit(regs);
+		if (do_signal(&saveset, regs)) {
+			set_thread_flag(TIF_RESTOREALL);
+			return 0;
+		}
 	}
 }
 
@@ -829,12 +839,6 @@ static int handle_rt_signal(unsigned lon
 	regs->gpr[6] = (unsigned long) rt_sf;
 	regs->nip = (unsigned long) ka->sa.sa_handler;
 	regs->trap = 0;
-#ifdef CONFIG_PPC64
-	regs->result = 0;
-
-	if (test_thread_flag(TIF_SINGLESTEP))
-		ptrace_notify(SIGTRAP);
-#endif
 	return 1;
 
 badframe:
@@ -912,8 +916,8 @@ long sys_swapcontext(struct ucontext __u
 	 */
 	if (do_setcontext(new_ctx, regs, 0))
 		do_exit(SIGSEGV);
-	sigreturn_exit(regs);
-	/* doesn't actually return back to here */
+
+	set_thread_flag(TIF_RESTOREALL);
 	return 0;
 }
 
@@ -946,12 +950,11 @@ long sys_rt_sigreturn(int r3, int r4, in
 	 * nobody does any...
 	 */
 	compat_sys_sigaltstack((u32)(u64)&rt_sf->uc.uc_stack, 0, 0, 0, 0, 0, regs);
-	return (int)regs->result;
 #else
 	do_sigaltstack(&rt_sf->uc.uc_stack, NULL, regs->gpr[1]);
-	sigreturn_exit(regs);		/* doesn't return here */
-	return 0;
 #endif
+	set_thread_flag(TIF_RESTOREALL);
+	return 0;
 
  bad:
 	force_sig(SIGSEGV, current);
@@ -1042,9 +1045,7 @@ int sys_debug_setcontext(struct ucontext
 	 */
 	do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]);
 
-	sigreturn_exit(regs);
-	/* doesn't actually return back to here */
-
+	set_thread_flag(TIF_RESTOREALL);
  out:
 	return 0;
 }
@@ -1109,12 +1110,6 @@ static int handle_signal(unsigned long s
 	regs->gpr[4] = (unsigned long) sc;
 	regs->nip = (unsigned long) ka->sa.sa_handler;
 	regs->trap = 0;
-#ifdef CONFIG_PPC64
-	regs->result = 0;
-
-	if (test_thread_flag(TIF_SINGLESTEP))
-		ptrace_notify(SIGTRAP);
-#endif
 
 	return 1;
 
@@ -1162,12 +1157,8 @@ long sys_sigreturn(int r3, int r4, int r
 	    || restore_user_regs(regs, sr, 1))
 		goto badframe;
 
-#ifdef CONFIG_PPC64
-	return (int)regs->result;
-#else
-	sigreturn_exit(regs);		/* doesn't return */
+	set_thread_flag(TIF_RESTOREALL);
 	return 0;
-#endif
 
 badframe:
 	force_sig(SIGSEGV, current);
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 58194e1..e071e95 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -96,8 +96,10 @@ long sys_rt_sigsuspend(sigset_t __user *
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
-		if (do_signal(&saveset, regs))
+		if (do_signal(&saveset, regs)) {
+			set_thread_flag(TIF_RESTOREALL);
 			return 0;
+		}
 	}
 }
 
@@ -155,6 +157,14 @@ static long setup_sigcontext(struct sigc
 	err |= __put_user(0, &sc->v_regs);
 #endif /* CONFIG_ALTIVEC */
 	err |= __put_user(&sc->gp_regs, &sc->regs);
+	if (!FULL_REGS(regs)) {
+		/* Zero out the unsaved GPRs to avoid information
+		   leak, and set TIF_SAVE_NVGPRS to ensure that the
+		   registers do actually get saved later. */
+		memset(&regs->gpr[14], 0, 18 * sizeof(unsigned long));
+		set_thread_flag(TIF_SAVE_NVGPRS);
+		current_thread_info()->nvgprs_frame = &sc->gp_regs;
+	}
 	err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE);
 	err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE);
 	err |= __put_user(signr, &sc->signal);
@@ -343,6 +353,7 @@ int sys_swapcontext(struct ucontext __us
 		do_exit(SIGSEGV);
 
 	/* This returns like rt_sigreturn */
+	set_thread_flag(TIF_RESTOREALL);
 	return 0;
 }
 
@@ -375,7 +386,8 @@ int sys_rt_sigreturn(unsigned long r3, u
 	 */
 	do_sigaltstack(&uc->uc_stack, NULL, regs->gpr[1]);
 
-	return regs->result;
+	set_thread_flag(TIF_RESTOREALL);
+	return 0;
 
 badframe:
 #if DEBUG_SIG
@@ -454,9 +466,6 @@ static int setup_rt_frame(int signr, str
 	if (err)
 		goto badframe;
 
-	if (test_thread_flag(TIF_SINGLESTEP))
-		ptrace_notify(SIGTRAP);
-
 	return 1;
 
 badframe:
diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S
index 65eaea9..4bb3650 100644
--- a/arch/powerpc/kernel/systbl.S
+++ b/arch/powerpc/kernel/systbl.S
@@ -113,7 +113,7 @@ SYSCALL(sgetmask)
 COMPAT_SYS(ssetmask)
 SYSCALL(setreuid)
 SYSCALL(setregid)
-SYSX(sys_ni_syscall,ppc32_sigsuspend,ppc_sigsuspend)
+SYS32ONLY(sigsuspend)
 COMPAT_SYS(sigpending)
 COMPAT_SYS(sethostname)
 COMPAT_SYS(setrlimit)
@@ -160,7 +160,7 @@ SYSCALL(swapoff)
 COMPAT_SYS(sysinfo)
 COMPAT_SYS(ipc)
 SYSCALL(fsync)
-SYSX(sys_ni_syscall,ppc32_sigreturn,sys_sigreturn)
+SYS32ONLY(sigreturn)
 PPC_SYS(clone)
 COMPAT_SYS(setdomainname)
 PPC_SYS(newuname)
@@ -213,13 +213,13 @@ COMPAT_SYS(nfsservctl)
 SYSCALL(setresgid)
 SYSCALL(getresgid)
 COMPAT_SYS(prctl)
-SYSX(ppc64_rt_sigreturn,ppc32_rt_sigreturn,sys_rt_sigreturn)
+COMPAT_SYS(rt_sigreturn)
 COMPAT_SYS(rt_sigaction)
 COMPAT_SYS(rt_sigprocmask)
 COMPAT_SYS(rt_sigpending)
 COMPAT_SYS(rt_sigtimedwait)
 COMPAT_SYS(rt_sigqueueinfo)
-SYSX(ppc64_rt_sigsuspend,ppc32_rt_sigsuspend,ppc_rt_sigsuspend)
+COMPAT_SYS(rt_sigsuspend)
 COMPAT_SYS(pread64)
 COMPAT_SYS(pwrite64)
 SYSCALL(chown)
@@ -290,7 +290,7 @@ COMPAT_SYS(clock_settime)
 COMPAT_SYS(clock_gettime)
 COMPAT_SYS(clock_getres)
 COMPAT_SYS(clock_nanosleep)
-SYSX(ppc64_swapcontext,ppc32_swapcontext,ppc_swapcontext)
+COMPAT_SYS(swapcontext)
 COMPAT_SYS(tgkill)
 COMPAT_SYS(utimes)
 COMPAT_SYS(statfs64)
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 32f2158..2010d35 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -205,8 +205,8 @@ void _exception(int signr, struct pt_reg
 		if (handler == SIG_DFL) {
 			/* init has generated a synchronous exception
 			   and it doesn't have a handler for the signal */
-			printk(KERN_CRIT "init has generated signal %d "
-			       "but has no handler for it\n", signr);
+			die("init has generated signal %d "
+			    "but has no handler for it\n", regs, signr);
 			do_exit(signr);
 		}
 	}
diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h
index 1f7ecdb..9c550b3 100644
--- a/include/asm-powerpc/ptrace.h
+++ b/include/asm-powerpc/ptrace.h
@@ -87,7 +87,7 @@ extern unsigned long profile_pc(struct p
 
 #define force_successful_syscall_return()   \
 	do { \
-		current_thread_info()->syscall_noerror = 1; \
+		set_thread_flag(TIF_NOERROR); \
 	} while(0)
 
 /*
diff --git a/include/asm-powerpc/thread_info.h b/include/asm-powerpc/thread_info.h
index e525f49..ac1e80e 100644
--- a/include/asm-powerpc/thread_info.h
+++ b/include/asm-powerpc/thread_info.h
@@ -37,8 +37,7 @@ struct thread_info {
 	int		preempt_count;		/* 0 => preemptable,
 						   <0 => BUG */
 	struct restart_block restart_block;
-	/* set by force_successful_syscall_return */
-	unsigned char	syscall_noerror;
+	void *nvgprs_frame;
 	/* low level flags - has atomic operations done on it */
 	unsigned long	flags ____cacheline_aligned_in_smp;
 };
@@ -123,6 +122,9 @@ static inline struct thread_info *curren
 #define TIF_SINGLESTEP		9	/* singlestepping active */
 #define TIF_MEMDIE		10
 #define TIF_SECCOMP		11	/* secure computing */
+#define TIF_RESTOREALL		12	/* Restore all regs (implies NOERROR) */
+#define TIF_SAVE_NVGPRS		13	/* Save r14-r31 in signal frame */
+#define TIF_NOERROR		14	/* Force successful syscall return */
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -136,10 +138,14 @@ static inline struct thread_info *curren
 #define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
 #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)
 #define _TIF_SECCOMP		(1<<TIF_SECCOMP)
+#define _TIF_RESTOREALL		(1<<TIF_RESTOREALL)
+#define _TIF_SAVE_NVGPRS	(1<<TIF_SAVE_NVGPRS)
+#define _TIF_NOERROR		(1<<TIF_NOERROR)
 #define _TIF_SYSCALL_T_OR_A	(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
 
 #define _TIF_USER_WORK_MASK	(_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \
-				 _TIF_NEED_RESCHED)
+				 _TIF_NEED_RESCHED | _TIF_RESTOREALL)
+#define _TIF_PERSYSCALL_MASK	(_TIF_RESTOREALL|_TIF_NOERROR|_TIF_SAVE_NVGPRS)
 
 #endif /* __KERNEL__ */
 

-- 
dwmw2

Re: [RFC] Attempt to clean up sigsuspend et al

From: David Woodhouse <dwmw2@infradead.org>
Date: 2005-11-15 00:19:33

On Sun, 2005-11-13 at 17:58 +1100, Paul Mackerras wrote:
I'll be upset if you can shorten it by a lot - I thought I had it
pretty tight already. :)
The patch appears to speed up a null syscall benchmark by about 3%.

With the old kernel I get an average of 5863600 cycles for 100000
getpid() calls; with the new kernel it's 5695256 cycles.

I still haven't done much testing for correctness, but it needs at least
this (d'oh)...
--- arch/powerpc/kernel/entry_64.S~	2005-11-14 23:41:22.000000000 +0000
+++ arch/powerpc/kernel/entry_64.S	2005-11-14 23:45:35.000000000 +0000
@@ -295,7 +295,7 @@ save_user_nvgprs:
 	ori	r10,r10,MSR_EE
 	mtmsrd	r10,1
 
-	ld	r10,TI_SIGFRAME(r9)
+	ld	r10,TI_SIGFRAME(r12)
 	andi.	r0,r9,_TIF_32BIT
 	beq-	save_user_nvgprs_64
 

-- 
dwmw2
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help