summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-03-02 17:36:46 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-04 04:21:10 +0100
commita27341cd5fcb7cf2d2d4726e9f324009f7162c00 (patch)
tree5b55a232509de5791ad00a15da3eaa93c3ae55c6 /kernel
parentMerge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp (diff)
downloadlinux-a27341cd5fcb7cf2d2d4726e9f324009f7162c00.tar.xz
linux-a27341cd5fcb7cf2d2d4726e9f324009f7162c00.zip
Prioritize synchronous signals over 'normal' signals
This makes sure that we pick the synchronous signals caused by a processor fault over any pending regular asynchronous signals sent to use by [t]kill(). This is not strictly required semantics, but it makes it _much_ easier for programs like Wine that expect to find the fault information in the signal stack. Without this, if a non-synchronous signal gets picked first, the delayed asynchronous signal will have its signal context pointing to the new signal invocation, rather than the instruction that caused the SIGSEGV or SIGBUS in the first place. This is not all that pretty, and we're discussing making the synchronous signals more explicit rather than have these kinds of implicit preferences of SIGSEGV and friends. See for example http://bugzilla.kernel.org/show_bug.cgi?id=15395 for some of the discussion. But in the meantime this is a simple and fairly straightforward work-around, and the whole if (x & Y) x &= Y; thing can be compiled into (and gcc does do it) just three instructions: movq %rdx, %rax andl $Y, %eax cmovne %rax, %rdx so it is at least a simple solution to a subtle issue. Reported-and-tested-by: Pavel Vilim <wylda@volny.cz> Acked-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/signal.c43
1 files changed, 30 insertions, 13 deletions
diff --git a/kernel/signal.c b/kernel/signal.c
index 934ae5e687b9..5bb9baffa4f1 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -159,6 +159,10 @@ void recalc_sigpending(void)
/* Given the mask, find the first available signal that should be serviced. */
+#define SYNCHRONOUS_MASK \
+ (sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \
+ sigmask(SIGTRAP) | sigmask(SIGFPE))
+
int next_signal(struct sigpending *pending, sigset_t *mask)
{
unsigned long i, *s, *m, x;
@@ -166,26 +170,39 @@ int next_signal(struct sigpending *pending, sigset_t *mask)
s = pending->signal.sig;
m = mask->sig;
+
+ /*
+ * Handle the first word specially: it contains the
+ * synchronous signals that need to be dequeued first.
+ */
+ x = *s &~ *m;
+ if (x) {
+ if (x & SYNCHRONOUS_MASK)
+ x &= SYNCHRONOUS_MASK;
+ sig = ffz(~x) + 1;
+ return sig;
+ }
+
switch (_NSIG_WORDS) {
default:
- for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)
- if ((x = *s &~ *m) != 0) {
- sig = ffz(~x) + i*_NSIG_BPW + 1;
- break;
- }
+ for (i = 1; i < _NSIG_WORDS; ++i) {
+ x = *++s &~ *++m;
+ if (!x)
+ continue;
+ sig = ffz(~x) + i*_NSIG_BPW + 1;
+ break;
+ }
break;
- case 2: if ((x = s[0] &~ m[0]) != 0)
- sig = 1;
- else if ((x = s[1] &~ m[1]) != 0)
- sig = _NSIG_BPW + 1;
- else
+ case 2:
+ x = s[1] &~ m[1];
+ if (!x)
break;
- sig += ffz(~x);
+ sig = ffz(~x) + _NSIG_BPW + 1;
break;
- case 1: if ((x = *s &~ *m) != 0)
- sig = ffz(~x) + 1;
+ case 1:
+ /* Nothing to do */
break;
}