diff options
| author | David Howells <dhowells@redhat.com> | 2006-01-18 20:44:00 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-18 22:20:29 -0500 |
| commit | 283828f3c19ceb3a64a8544d42cc189003e8b0fe (patch) | |
| tree | a3ba8f54e809bc1036bce6a404d88c93d578b0c2 /arch/i386/kernel/signal.c | |
| parent | a411aee96ea7fe6fe065df65bf29ea755bcdb554 (diff) | |
[PATCH] Handle TIF_RESTORE_SIGMASK for i386
Handle TIF_RESTORE_SIGMASK as added by David Woodhouse's patch entitled:
[PATCH] 2/3 Add TIF_RESTORE_SIGMASK support for arch/powerpc
[PATCH] 3/3 Generic sys_rt_sigsuspend
It does the following:
(1) Declares TIF_RESTORE_SIGMASK for i386.
(2) Invokes it over to do_signal() when TIF_RESTORE_SIGMASK is set.
(3) Makes do_signal() support TIF_RESTORE_SIGMASK, using the signal mask saved
in current->saved_sigmask.
(4) Discards sys_rt_sigsuspend() from the arch, using the generic one instead.
(5) Makes sys_sigsuspend() save the signal mask and set TIF_RESTORE_SIGMASK
rather than attempting to fudge the return registers.
(6) Makes sys_sigsuspend() return -ERESTARTNOHAND rather than looping
intrinsically.
(7) Makes setup_frame(), setup_rt_frame() and handle_signal() return 0 or
-EFAULT rather than true/false to be consistent with the rest of the
kernel.
Due to the fact do_signal() is then only called from one place:
(8) Makes do_signal() no longer have a return value is it was just being
ignored; force_sig() takes care of this.
(9) Discards the old sigmask argument to do_signal() as it's no longer
necessary.
(10) Makes do_signal() static.
(11) Marks the second argument to do_notify_resume() as unused. The unused
argument should remain in the middle as the arguments are passed in as
registers, and the ordering is specific in entry.S
Given the way do_signal() is now no longer called from sys_{,rt_}sigsuspend(),
they no longer need access to the exception frame, and so can just take
arguments normally.
This patch depends on sys_rt_sigsuspend patch.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/signal.c')
| -rw-r--r-- | arch/i386/kernel/signal.c | 109 |
1 files changed, 48 insertions, 61 deletions
diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index adcd069db91e..963616d364ec 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c | |||
| @@ -37,51 +37,17 @@ | |||
| 37 | asmlinkage int | 37 | asmlinkage int |
| 38 | sys_sigsuspend(int history0, int history1, old_sigset_t mask) | 38 | sys_sigsuspend(int history0, int history1, old_sigset_t mask) |
| 39 | { | 39 | { |
| 40 | struct pt_regs * regs = (struct pt_regs *) &history0; | ||
| 41 | sigset_t saveset; | ||
| 42 | |||
| 43 | mask &= _BLOCKABLE; | 40 | mask &= _BLOCKABLE; |
| 44 | spin_lock_irq(¤t->sighand->siglock); | 41 | spin_lock_irq(¤t->sighand->siglock); |
| 45 | saveset = current->blocked; | 42 | current->saved_sigmask = current->blocked; |
| 46 | siginitset(¤t->blocked, mask); | 43 | siginitset(¤t->blocked, mask); |
| 47 | recalc_sigpending(); | 44 | recalc_sigpending(); |
| 48 | spin_unlock_irq(¤t->sighand->siglock); | 45 | spin_unlock_irq(¤t->sighand->siglock); |
| 49 | 46 | ||
| 50 | regs->eax = -EINTR; | 47 | current->state = TASK_INTERRUPTIBLE; |
| 51 | while (1) { | 48 | schedule(); |
| 52 | current->state = TASK_INTERRUPTIBLE; | 49 | set_thread_flag(TIF_RESTORE_SIGMASK); |
| 53 | schedule(); | 50 | return -ERESTARTNOHAND; |
| 54 | if (do_signal(regs, &saveset)) | ||
| 55 | return -EINTR; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | asmlinkage int | ||
| 60 | sys_rt_sigsuspend(struct pt_regs regs) | ||
| 61 | { | ||
| 62 | sigset_t saveset, newset; | ||
| 63 | |||
| 64 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
| 65 | if (regs.ecx != sizeof(sigset_t)) | ||
| 66 | return -EINVAL; | ||
| 67 | |||
| 68 | if (copy_from_user(&newset, (sigset_t __user *)regs.ebx, sizeof(newset))) | ||
| 69 | return -EFAULT; | ||
| 70 | sigdelsetmask(&newset, ~_BLOCKABLE); | ||
| 71 | |||
| 72 | spin_lock_irq(¤t->sighand->siglock); | ||
| 73 | saveset = current->blocked; | ||
| 74 | current->blocked = newset; | ||
| 75 | recalc_sigpending(); | ||
| 76 | spin_unlock_irq(¤t->sighand->siglock); | ||
| 77 | |||
| 78 | regs.eax = -EINTR; | ||
| 79 | while (1) { | ||
| 80 | current->state = TASK_INTERRUPTIBLE; | ||
| 81 | schedule(); | ||
| 82 | if (do_signal(®s, &saveset)) | ||
| 83 | return -EINTR; | ||
| 84 | } | ||
| 85 | } | 51 | } |
| 86 | 52 | ||
| 87 | asmlinkage int | 53 | asmlinkage int |
| @@ -433,11 +399,11 @@ static int setup_frame(int sig, struct k_sigaction *ka, | |||
| 433 | current->comm, current->pid, frame, regs->eip, frame->pretcode); | 399 | current->comm, current->pid, frame, regs->eip, frame->pretcode); |
| 434 | #endif | 400 | #endif |
| 435 | 401 | ||
| 436 | return 1; | 402 | return 0; |
| 437 | 403 | ||
| 438 | give_sigsegv: | 404 | give_sigsegv: |
| 439 | force_sigsegv(sig, current); | 405 | force_sigsegv(sig, current); |
| 440 | return 0; | 406 | return -EFAULT; |
| 441 | } | 407 | } |
| 442 | 408 | ||
| 443 | static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | 409 | static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, |
| @@ -527,11 +493,11 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
| 527 | current->comm, current->pid, frame, regs->eip, frame->pretcode); | 493 | current->comm, current->pid, frame, regs->eip, frame->pretcode); |
| 528 | #endif | 494 | #endif |
| 529 | 495 | ||
| 530 | return 1; | 496 | return 0; |
| 531 | 497 | ||
| 532 | give_sigsegv: | 498 | give_sigsegv: |
| 533 | force_sigsegv(sig, current); | 499 | force_sigsegv(sig, current); |
| 534 | return 0; | 500 | return -EFAULT; |
| 535 | } | 501 | } |
| 536 | 502 | ||
| 537 | /* | 503 | /* |
| @@ -581,7 +547,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
| 581 | else | 547 | else |
| 582 | ret = setup_frame(sig, ka, oldset, regs); | 548 | ret = setup_frame(sig, ka, oldset, regs); |
| 583 | 549 | ||
| 584 | if (ret) { | 550 | if (ret == 0) { |
| 585 | spin_lock_irq(¤t->sighand->siglock); | 551 | spin_lock_irq(¤t->sighand->siglock); |
| 586 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | 552 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); |
| 587 | if (!(ka->sa.sa_flags & SA_NODEFER)) | 553 | if (!(ka->sa.sa_flags & SA_NODEFER)) |
| @@ -598,11 +564,12 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, | |||
| 598 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 564 | * want to handle. Thus you cannot kill init even with a SIGKILL even by |
| 599 | * mistake. | 565 | * mistake. |
| 600 | */ | 566 | */ |
| 601 | int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset) | 567 | static void fastcall do_signal(struct pt_regs *regs) |
| 602 | { | 568 | { |
| 603 | siginfo_t info; | 569 | siginfo_t info; |
| 604 | int signr; | 570 | int signr; |
| 605 | struct k_sigaction ka; | 571 | struct k_sigaction ka; |
| 572 | sigset_t *oldset; | ||
| 606 | 573 | ||
| 607 | /* | 574 | /* |
| 608 | * We want the common case to go fast, which | 575 | * We want the common case to go fast, which |
| @@ -613,12 +580,14 @@ int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset) | |||
| 613 | * CS suffices. | 580 | * CS suffices. |
| 614 | */ | 581 | */ |
| 615 | if (!user_mode(regs)) | 582 | if (!user_mode(regs)) |
| 616 | return 1; | 583 | return; |
| 617 | 584 | ||
| 618 | if (try_to_freeze()) | 585 | if (try_to_freeze()) |
| 619 | goto no_signal; | 586 | goto no_signal; |
| 620 | 587 | ||
| 621 | if (!oldset) | 588 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
| 589 | oldset = ¤t->saved_sigmask; | ||
| 590 | else | ||
| 622 | oldset = ¤t->blocked; | 591 | oldset = ¤t->blocked; |
| 623 | 592 | ||
| 624 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 593 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
| @@ -628,38 +597,55 @@ int fastcall do_signal(struct pt_regs *regs, sigset_t *oldset) | |||
| 628 | * have been cleared if the watchpoint triggered | 597 | * have been cleared if the watchpoint triggered |
| 629 | * inside the kernel. | 598 | * inside the kernel. |
| 630 | */ | 599 | */ |
| 631 | if (unlikely(current->thread.debugreg[7])) { | 600 | if (unlikely(current->thread.debugreg[7])) |
| 632 | set_debugreg(current->thread.debugreg[7], 7); | 601 | set_debugreg(current->thread.debugreg[7], 7); |
| 633 | } | ||
| 634 | 602 | ||
| 635 | /* Whee! Actually deliver the signal. */ | 603 | /* Whee! Actually deliver the signal. */ |
| 636 | return handle_signal(signr, &info, &ka, oldset, regs); | 604 | if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { |
| 605 | /* a signal was successfully delivered; the saved | ||
| 606 | * sigmask will have been stored in the signal frame, | ||
| 607 | * and will be restored by sigreturn, so we can simply | ||
| 608 | * clear the TIF_RESTORE_SIGMASK flag */ | ||
| 609 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
| 610 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
| 611 | } | ||
| 612 | |||
| 613 | return; | ||
| 637 | } | 614 | } |
| 638 | 615 | ||
| 639 | no_signal: | 616 | no_signal: |
| 640 | /* Did we come from a system call? */ | 617 | /* Did we come from a system call? */ |
| 641 | if (regs->orig_eax >= 0) { | 618 | if (regs->orig_eax >= 0) { |
| 642 | /* Restart the system call - no handlers present */ | 619 | /* Restart the system call - no handlers present */ |
| 643 | if (regs->eax == -ERESTARTNOHAND || | 620 | switch (regs->eax) { |
| 644 | regs->eax == -ERESTARTSYS || | 621 | case -ERESTARTNOHAND: |
| 645 | regs->eax == -ERESTARTNOINTR) { | 622 | case -ERESTARTSYS: |
| 623 | case -ERESTARTNOINTR: | ||
| 646 | regs->eax = regs->orig_eax; | 624 | regs->eax = regs->orig_eax; |
| 647 | regs->eip -= 2; | 625 | regs->eip -= 2; |
| 648 | } | 626 | break; |
| 649 | if (regs->eax == -ERESTART_RESTARTBLOCK){ | 627 | |
| 628 | case -ERESTART_RESTARTBLOCK: | ||
| 650 | regs->eax = __NR_restart_syscall; | 629 | regs->eax = __NR_restart_syscall; |
| 651 | regs->eip -= 2; | 630 | regs->eip -= 2; |
| 631 | break; | ||
| 652 | } | 632 | } |
| 653 | } | 633 | } |
| 654 | return 0; | 634 | |
| 635 | /* if there's no signal to deliver, we just put the saved sigmask | ||
| 636 | * back */ | ||
| 637 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
| 638 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
| 639 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
| 640 | } | ||
| 655 | } | 641 | } |
| 656 | 642 | ||
| 657 | /* | 643 | /* |
| 658 | * notification of userspace execution resumption | 644 | * notification of userspace execution resumption |
| 659 | * - triggered by current->work.notify_resume | 645 | * - triggered by the TIF_WORK_MASK flags |
| 660 | */ | 646 | */ |
| 661 | __attribute__((regparm(3))) | 647 | __attribute__((regparm(3))) |
| 662 | void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, | 648 | void do_notify_resume(struct pt_regs *regs, void *_unused, |
| 663 | __u32 thread_info_flags) | 649 | __u32 thread_info_flags) |
| 664 | { | 650 | { |
| 665 | /* Pending single-step? */ | 651 | /* Pending single-step? */ |
| @@ -667,9 +653,10 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, | |||
| 667 | regs->eflags |= TF_MASK; | 653 | regs->eflags |= TF_MASK; |
| 668 | clear_thread_flag(TIF_SINGLESTEP); | 654 | clear_thread_flag(TIF_SINGLESTEP); |
| 669 | } | 655 | } |
| 656 | |||
| 670 | /* deal with pending signal delivery */ | 657 | /* deal with pending signal delivery */ |
| 671 | if (thread_info_flags & _TIF_SIGPENDING) | 658 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) |
| 672 | do_signal(regs,oldset); | 659 | do_signal(regs); |
| 673 | 660 | ||
| 674 | clear_thread_flag(TIF_IRET); | 661 | clear_thread_flag(TIF_IRET); |
| 675 | } | 662 | } |
