Path: news.gmane.org!not-for-mail
From: Mikael Pettersson <mikpe@it.uu.se>
Newsgroups: gmane.linux.ports.arm.kernel
Subject: [PATCH][v3] ARM support for
	TIF_RESTORE_SIGMASK/pselect6/ppoll/epoll_pwait
Date: Sat, 15 Aug 2009 13:09:28 +0200 (MEST)
Lines: 362
Approved: news@gmane.org
Message-ID: <200908151109.n7FB9Sbs000150@pilspetsen.it.uu.se>
NNTP-Posting-Host: lo.gmane.org
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
X-Trace: ger.gmane.org 1250334940 22899 80.91.229.12 (15 Aug 2009 11:15:40 GMT)
X-Complaints-To: usenet@ger.gmane.org
NNTP-Posting-Date: Sat, 15 Aug 2009 11:15:40 +0000 (UTC)
To: linux-arm-kernel@lists.arm.linux.org.uk
Original-X-From: linux-arm-kernel-bounces+linux-arm-kernel=m.gmane.org@lists.arm.linux.org.uk Sat Aug 15 13:15:33 2009
Return-path: <linux-arm-kernel-bounces+linux-arm-kernel=m.gmane.org@lists.arm.linux.org.uk>
Envelope-to: linux-arm-kernel@m.gmane.org
Original-Received: from zeniv.linux.org.uk ([195.92.253.2])
	by lo.gmane.org with esmtp (Exim 4.50)
	id 1McHER-0000U2-PG
	for linux-arm-kernel@m.gmane.org; Sat, 15 Aug 2009 13:15:31 +0200
Original-Received: from [2002:4e20:1eda:1:201:3dff:fe00:156] (helo=lists.arm.linux.org.uk)
	by ZenIV.linux.org.uk with esmtpsa (Exim 4.69 #1 (Red Hat Linux))
	id 1McH9u-0005LV-3W; Sat, 15 Aug 2009 11:10:57 +0000
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
	d=arm.linux.org.uk; s=lists; h=Date:Message-Id:From:To:Subject:
	List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:
	List-Subscribe:MIME-Version:Content-Type:
	Content-Transfer-Encoding; bh=RVq9hPAexlUcptd3h6j5nomOyoEIjcDWAP
	VeJtlhsa4=; b=HAoqRMC9svNzKlR0f938E0cRlLoJZvG2aSun171woWZAClu/Ud
	IYN0iD2vm4eF+kYQkTYD6PGFCMbNnugmZe6gCpS0QhijoKyOzRBdykQHJuOLQtLE
	q9KJzp24cQ/vwsV2+O1OqcmIFboIg2L5JQaUuv0djS7OkdspH0ysmi1lg=
Original-Received: from [::1] (helo=lists.arm.linux.org.uk)
	by lists.arm.linux.org.uk with esmtp (Exim 4.69)
	(envelope-from <linux-arm-kernel-bounces@lists.arm.linux.org.uk>)
	id 1McH9o-0002us-34; Sat, 15 Aug 2009 12:10:44 +0100
Original-Received: from aun.it.uu.se ([130.238.12.36] ident=root)
	by lists.arm.linux.org.uk with esmtps (TLSv1:AES256-SHA:256)
	(Exim 4.69) (envelope-from <mikpe@user.it.uu.se>) id 1McH8h-0002uj-Ck
	for linux-arm-kernel@lists.arm.linux.org.uk;
	Sat, 15 Aug 2009 12:09:35 +0100
Original-Received: from pilspetsen.it.uu.se (daemon@pilspetsen.it.uu.se [130.238.18.39])
	by aun.it.uu.se (8.13.6/8.13.6) with ESMTP id n7FB9S04005839
	for <linux-arm-kernel@lists.arm.linux.org.uk>;
	Sat, 15 Aug 2009 13:09:28 +0200 (MEST)
Original-Received: (from mikpe@localhost)
	by pilspetsen.it.uu.se (8.13.8+Sun/8.13.7) id n7FB9Sbs000150
	for linux-arm-kernel@lists.arm.linux.org.uk;
	Sat, 15 Aug 2009 13:09:28 +0200 (MEST)
X-BeenThere: linux-arm-kernel@lists.arm.linux.org.uk
X-Mailman-Version: 2.1.9
Precedence: list
List-Id: ARM Linux kernel discussions <linux-arm-kernel.lists.arm.linux.org.uk>
List-Unsubscribe: <http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel>,
	<mailto:linux-arm-kernel-request@lists.arm.linux.org.uk?subject=unsubscribe>
List-Archive: <http://lists.arm.linux.org.uk/lurker/list/linux-arm-kernel.html>
List-Post: <mailto:linux-arm-kernel@lists.arm.linux.org.uk>
List-Help: <mailto:linux-arm-kernel-request@lists.arm.linux.org.uk?subject=help>
List-Subscribe: <http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel>,
	<mailto:linux-arm-kernel-request@lists.arm.linux.org.uk?subject=subscribe>
Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=m.gmane.org@lists.arm.linux.org.uk
Xref: news.gmane.org gmane.linux.ports.arm.kernel:64340
Archived-At: <http://permalink.gmane.org/gmane.linux.ports.arm.kernel/64340>

This patch adds support for TIF_RESTORE_SIGMASK to ARM's signal handling,
which allows to hook up the pselect6, ppoll, and epoll_pwait syscalls on ARM.

Tested here with eabi userspace and a test program with a deliberate race
between a child's exit and the parent's sigprocmask/select sequence.
Using sys_pselect6() instead of sigprocmask/select reliably prevents the race.

The other arch's support for TIF_RESTORE_SIGMASK has evolved over time:

In 2.6.16:
- add TIF_RESTORE_SIGMASK which parallels TIF_SIGPENDING
- test both when checking for pending signal [bad, changed later]
- reimplement sys_sigsuspend() to use current->saved_sigmask,
  TIF_RESTORE_SIGMASK [changed later], and -ERESTARTNOHAND;
  ditto for sys_rt_sigsuspend(), but drop private code and
  use common code via __ARCH_WANT_SYS_RT_SIGSUSPEND;
- there are now no "extra" calls to do_signal() so its oldset
  parameter is always &current->blocked so need not be passed,
  also its return value is changed to void
- change handle_signal() to return 0/-errno
- change do_signal() to honor TIF_RESTORE_SIGMASK:
  + get oldset from current->saved_sigmask if TIF_RESTORE_SIGMASK is set
  + if handle_signal() was successful then clear TIF_RESTORE_SIGMASK
  + if no signal was delivered and TIF_RESTORE_SIGMASK is set then
    clear it and restore the sigmask
- hook up sys_pselect6() and sys_ppoll()

In 2.6.19:
- hook up sys_epoll_pwait()

In 2.6.26:
- allow archs to override how TIF_RESTORE_SIGMASK is implemented;
  default set_restore_sigmask() sets both TIF_RESTORE_SIGMASK and
  TIF_SIGPENDING; archs need now just test TIF_SIGPENDING again
  when checking for pending signal work; some archs now implement
  TIF_RESTORE_SIGMASK as a secondary/non-atomic thread flag bit
- call set_restore_sigmask() in sys_sigsuspend() instead of setting
  TIF_RESTORE_SIGMASK

In 2.6.29-rc:
- kill sys_pselect7() which no arch wanted

So for 2.6.31-rc6/ARM this patch does the following:
- Add TIF_RESTORE_SIGMASK. Use the generic set_restore_sigmask()
  which sets both TIF_SIGPENDING and TIF_RESTORE_SIGMASK, so
  TIF_RESTORE_SIGMASK need not claim one of the scarce low thread
  flags, and existing TIF_SIGPENDING and _TIF_WORK_MASK tests need
  not be extended for TIF_RESTORE_SIGMASK.
- sys_sigsuspend() is reimplemented to use current->saved_sigmask and
  set_restore_sigmask(), making it identical to most other archs.
- The private code for sys_rt_sigsuspend() is removed, instead generic
  code supplies it via __ARCH_WANT_SYS_RT_SIGSUSPEND.
- sys_sigsuspend() and sys_rt_sigsuspend() no longer need a pt_regs
  parameter, so their assembly code wrappers are removed.
- handle_signal() is changed to return 0 on success or -errno.
- The oldset parameter to do_signal() is now redundant and removed,
  and the return value is now also redundant so it's changed to void.
- do_signal() is changed to honor TIF_RESTORE_SIGMASK:
  + get oldset from current->saved_sigmask if TIF_RESTORE_SIGMASK is set
  + if handle_signal() was successful then clear TIF_RESTORE_SIGMASK
  + if no signal was delivered and TIF_RESTORE_SIGMASK is set then
    clear it and restore the sigmask
- Hook up sys_pselect6, sys_ppoll, and sys_epoll_pwait.

Signed-off-by: Mikael Pettersson
---
Changes from previous versions:

v2 (only posted as incremental diff against v1):
- moved sigmask restoration in no-signal-delivered case into
  "if (syscall)" block for extra protection against exception
  path also invoking do_signal()
v3 (this version):
- moved oldset initialisation into "if (signr > 0)" block,
  as requested by Russell

 arch/arm/include/asm/thread_info.h |    2 
 arch/arm/include/asm/unistd.h      |    7 +--
 arch/arm/kernel/calls.S            |   10 ++--
 arch/arm/kernel/entry-common.S     |   10 ----
 arch/arm/kernel/signal.c           |   86 +++++++++++++++----------------------
 5 files changed, 48 insertions(+), 67 deletions(-)

diff -rupN linux-2.6.31-rc6/arch/arm/include/asm/thread_info.h linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/include/asm/thread_info.h
--- linux-2.6.31-rc6/arch/arm/include/asm/thread_info.h	2009-08-14 11:11:10.000000000 +0200
+++ linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/include/asm/thread_info.h	2009-08-14 11:26:44.000000000 +0200
@@ -140,6 +140,7 @@ extern void vfp_sync_state(struct thread
 #define TIF_USING_IWMMXT	17
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
+#define TIF_RESTORE_SIGMASK	20
 
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
@@ -147,6 +148,7 @@ extern void vfp_sync_state(struct thread
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_USING_IWMMXT	(1 << TIF_USING_IWMMXT)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
+#define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 
 /*
  * Change these and you break ASM code in entry-common.S
diff -rupN linux-2.6.31-rc6/arch/arm/include/asm/unistd.h linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/include/asm/unistd.h
--- linux-2.6.31-rc6/arch/arm/include/asm/unistd.h	2009-08-14 11:11:10.000000000 +0200
+++ linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/include/asm/unistd.h	2009-08-14 11:26:44.000000000 +0200
@@ -360,8 +360,8 @@
 #define __NR_readlinkat			(__NR_SYSCALL_BASE+332)
 #define __NR_fchmodat			(__NR_SYSCALL_BASE+333)
 #define __NR_faccessat			(__NR_SYSCALL_BASE+334)
-					/* 335 for pselect6 */
-					/* 336 for ppoll */
+#define __NR_pselect6			(__NR_SYSCALL_BASE+335)
+#define __NR_ppoll			(__NR_SYSCALL_BASE+336)
 #define __NR_unshare			(__NR_SYSCALL_BASE+337)
 #define __NR_set_robust_list		(__NR_SYSCALL_BASE+338)
 #define __NR_get_robust_list		(__NR_SYSCALL_BASE+339)
@@ -372,7 +372,7 @@
 #define __NR_vmsplice			(__NR_SYSCALL_BASE+343)
 #define __NR_move_pages			(__NR_SYSCALL_BASE+344)
 #define __NR_getcpu			(__NR_SYSCALL_BASE+345)
-					/* 346 for epoll_pwait */
+#define __NR_epoll_pwait		(__NR_SYSCALL_BASE+346)
 #define __NR_kexec_load			(__NR_SYSCALL_BASE+347)
 #define __NR_utimensat			(__NR_SYSCALL_BASE+348)
 #define __NR_signalfd			(__NR_SYSCALL_BASE+349)
@@ -432,6 +432,7 @@
 #define __ARCH_WANT_SYS_SIGPENDING
 #define __ARCH_WANT_SYS_SIGPROCMASK
 #define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
 
 #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
 #define __ARCH_WANT_SYS_TIME
diff -rupN linux-2.6.31-rc6/arch/arm/kernel/calls.S linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/kernel/calls.S
--- linux-2.6.31-rc6/arch/arm/kernel/calls.S	2009-08-14 11:11:10.000000000 +0200
+++ linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/kernel/calls.S	2009-08-14 11:26:44.000000000 +0200
@@ -81,7 +81,7 @@
 		CALL(sys_ni_syscall)		/* was sys_ssetmask */
 /* 70 */	CALL(sys_setreuid16)
 		CALL(sys_setregid16)
-		CALL(sys_sigsuspend_wrapper)
+		CALL(sys_sigsuspend)
 		CALL(sys_sigpending)
 		CALL(sys_sethostname)
 /* 75 */	CALL(sys_setrlimit)
@@ -188,7 +188,7 @@
 		CALL(sys_rt_sigpending)
 		CALL(sys_rt_sigtimedwait)
 		CALL(sys_rt_sigqueueinfo)
-		CALL(sys_rt_sigsuspend_wrapper)
+		CALL(sys_rt_sigsuspend)
 /* 180 */	CALL(ABI(sys_pread64, sys_oabi_pread64))
 		CALL(ABI(sys_pwrite64, sys_oabi_pwrite64))
 		CALL(sys_chown16)
@@ -344,8 +344,8 @@
 		CALL(sys_readlinkat)
 		CALL(sys_fchmodat)
 		CALL(sys_faccessat)
-/* 335 */	CALL(sys_ni_syscall)		/* eventually pselect6 */
-		CALL(sys_ni_syscall)		/* eventually ppoll */
+/* 335 */	CALL(sys_pselect6)
+		CALL(sys_ppoll)
 		CALL(sys_unshare)
 		CALL(sys_set_robust_list)
 		CALL(sys_get_robust_list)
@@ -355,7 +355,7 @@
 		CALL(sys_vmsplice)
 		CALL(sys_move_pages)
 /* 345 */	CALL(sys_getcpu)
-		CALL(sys_ni_syscall)		/* eventually epoll_pwait */
+		CALL(sys_epoll_pwait)
 		CALL(sys_kexec_load)
 		CALL(sys_utimensat)
 		CALL(sys_signalfd)
diff -rupN linux-2.6.31-rc6/arch/arm/kernel/entry-common.S linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/kernel/entry-common.S
--- linux-2.6.31-rc6/arch/arm/kernel/entry-common.S	2009-08-14 11:11:10.000000000 +0200
+++ linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/kernel/entry-common.S	2009-08-14 11:26:44.000000000 +0200
@@ -373,16 +373,6 @@ sys_clone_wrapper:
 		b	sys_clone
 ENDPROC(sys_clone_wrapper)
 
-sys_sigsuspend_wrapper:
-		add	r3, sp, #S_OFF
-		b	sys_sigsuspend
-ENDPROC(sys_sigsuspend_wrapper)
-
-sys_rt_sigsuspend_wrapper:
-		add	r2, sp, #S_OFF
-		b	sys_rt_sigsuspend
-ENDPROC(sys_rt_sigsuspend_wrapper)
-
 sys_sigreturn_wrapper:
 		add	r0, sp, #S_OFF
 		b	sys_sigreturn
diff -rupN linux-2.6.31-rc6/arch/arm/kernel/signal.c linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/kernel/signal.c
--- linux-2.6.31-rc6/arch/arm/kernel/signal.c	2009-08-14 11:11:10.000000000 +0200
+++ linux-2.6.31-rc6.arm-restore-sigmask-v3/arch/arm/kernel/signal.c	2009-08-14 11:26:44.000000000 +0200
@@ -47,57 +47,22 @@ const unsigned long sigreturn_codes[7] =
 	MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
 };
 
-static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
-
 /*
  * atomically swap in the new signal mask, and wait for a signal.
  */
-asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs)
+asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask)
 {
-	sigset_t saveset;
-
 	mask &= _BLOCKABLE;
 	spin_lock_irq(&current->sighand->siglock);
-	saveset = current->blocked;
+	current->saved_sigmask = current->blocked;
 	siginitset(&current->blocked, mask);
 	recalc_sigpending();
 	spin_unlock_irq(&current->sighand->siglock);
-	regs->ARM_r0 = -EINTR;
-
-	while (1) {
-		current->state = TASK_INTERRUPTIBLE;
-		schedule();
-		if (do_signal(&saveset, regs, 0))
-			return regs->ARM_r0;
-	}
-}
-
-asmlinkage int
-sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs)
-{
-	sigset_t saveset, newset;
-
-	/* XXX: Don't preclude handling different sized sigset_t's. */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (copy_from_user(&newset, unewset, sizeof(newset)))
-		return -EFAULT;
-	sigdelsetmask(&newset, ~_BLOCKABLE);
-
-	spin_lock_irq(&current->sighand->siglock);
-	saveset = current->blocked;
-	current->blocked = newset;
-	recalc_sigpending();
-	spin_unlock_irq(&current->sighand->siglock);
-	regs->ARM_r0 = -EINTR;
 
-	while (1) {
-		current->state = TASK_INTERRUPTIBLE;
-		schedule();
-		if (do_signal(&saveset, regs, 0))
-			return regs->ARM_r0;
-	}
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	set_restore_sigmask();
+	return -ERESTARTNOHAND;
 }
 
 asmlinkage int 
@@ -545,7 +510,7 @@ static inline void setup_syscall_restart
 /*
  * OK, we're invoking a handler
  */	
-static void
+static int
 handle_signal(unsigned long sig, struct k_sigaction *ka,
 	      siginfo_t *info, sigset_t *oldset,
 	      struct pt_regs * regs, int syscall)
@@ -596,7 +561,7 @@ handle_signal(unsigned long sig, struct 
 
 	if (ret != 0) {
 		force_sigsegv(sig, tsk);
-		return;
+		return ret;
 	}
 
 	/*
@@ -610,6 +575,7 @@ handle_signal(unsigned long sig, struct 
 	recalc_sigpending();
 	spin_unlock_irq(&tsk->sighand->siglock);
 
+	return 0;
 }
 
 /*
@@ -621,7 +587,7 @@ handle_signal(unsigned long sig, struct 
  * the kernel can handle, and then we build all the user-level signal handling
  * stack-frames in one go after that.
  */
-static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
+static void do_signal(struct pt_regs *regs, int syscall)
 {
 	struct k_sigaction ka;
 	siginfo_t info;
@@ -634,7 +600,7 @@ static int do_signal(sigset_t *oldset, s
 	 * if so.
 	 */
 	if (!user_mode(regs))
-		return 0;
+		return;
 
 	if (try_to_freeze())
 		goto no_signal;
@@ -643,9 +609,24 @@ static int do_signal(sigset_t *oldset, s
 
 	signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 	if (signr > 0) {
-		handle_signal(signr, &ka, &info, oldset, regs, syscall);
+		sigset_t *oldset;
+
+		if (test_thread_flag(TIF_RESTORE_SIGMASK))
+			oldset = &current->saved_sigmask;
+		else
+			oldset = &current->blocked;
+		if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) {
+			/*
+			 * A signal was successfully delivered; the saved
+			 * sigmask will have been stored in the signal frame,
+			 * and will be restored by sigreturn, so we can simply
+			 * clear the TIF_RESTORE_SIGMASK flag.
+			 */
+			if (test_thread_flag(TIF_RESTORE_SIGMASK))
+				clear_thread_flag(TIF_RESTORE_SIGMASK);
+		}
 		single_step_set(current);
-		return 1;
+		return;
 	}
 
  no_signal:
@@ -697,14 +678,21 @@ static int do_signal(sigset_t *oldset, s
 		    regs->ARM_r0 == -ERESTARTNOINTR) {
 			setup_syscall_restart(regs);
 		}
+
+		/* If there's no signal to deliver, we just put the saved sigmask
+		 * back.
+		 */
+		if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+			clear_thread_flag(TIF_RESTORE_SIGMASK);
+			sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+		}
 	}
 	single_step_set(current);
-	return 0;
 }
 
 asmlinkage void
 do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
 {
 	if (thread_flags & _TIF_SIGPENDING)
-		do_signal(&current->blocked, regs, syscall);
+		do_signal(regs, syscall);
 }

-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ:        http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette:  http://www.arm.linux.org.uk/mailinglists/etiquette.php