diff options
| author | Steven Whitehouse <swhiteho@redhat.com> | 2006-09-28 08:29:59 -0400 |
|---|---|---|
| committer | Steven Whitehouse <swhiteho@redhat.com> | 2006-09-28 08:29:59 -0400 |
| commit | 185a257f2f73bcd89050ad02da5bedbc28fc43fa (patch) | |
| tree | 5e32586114534ed3f2165614cba3d578f5d87307 /arch/sh/kernel/signal.c | |
| parent | 3f1a9aaeffd8d1cbc5ab9776c45cbd66af1c9699 (diff) | |
| parent | a77c64c1a641950626181b4857abb701d8f38ccc (diff) | |
Merge branch 'master' into gfs2
Diffstat (limited to 'arch/sh/kernel/signal.c')
| -rw-r--r-- | arch/sh/kernel/signal.c | 158 |
1 files changed, 83 insertions, 75 deletions
diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index b475c4d2405f..5213f5bc6ce0 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima | 8 | * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima |
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | |||
| 12 | #include <linux/sched.h> | 11 | #include <linux/sched.h> |
| 13 | #include <linux/mm.h> | 12 | #include <linux/mm.h> |
| 14 | #include <linux/smp.h> | 13 | #include <linux/smp.h> |
| @@ -21,6 +20,7 @@ | |||
| 21 | #include <linux/unistd.h> | 20 | #include <linux/unistd.h> |
| 22 | #include <linux/stddef.h> | 21 | #include <linux/stddef.h> |
| 23 | #include <linux/tty.h> | 22 | #include <linux/tty.h> |
| 23 | #include <linux/elf.h> | ||
| 24 | #include <linux/personality.h> | 24 | #include <linux/personality.h> |
| 25 | #include <linux/binfmts.h> | 25 | #include <linux/binfmts.h> |
| 26 | 26 | ||
| @@ -29,12 +29,8 @@ | |||
| 29 | #include <asm/pgtable.h> | 29 | #include <asm/pgtable.h> |
| 30 | #include <asm/cacheflush.h> | 30 | #include <asm/cacheflush.h> |
| 31 | 31 | ||
| 32 | #define DEBUG_SIG 0 | ||
| 33 | |||
| 34 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | 32 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
| 35 | 33 | ||
| 36 | asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); | ||
| 37 | |||
| 38 | /* | 34 | /* |
| 39 | * Atomically swap in the new signal mask, and wait for a signal. | 35 | * Atomically swap in the new signal mask, and wait for a signal. |
| 40 | */ | 36 | */ |
| @@ -43,51 +39,17 @@ sys_sigsuspend(old_sigset_t mask, | |||
| 43 | unsigned long r5, unsigned long r6, unsigned long r7, | 39 | unsigned long r5, unsigned long r6, unsigned long r7, |
| 44 | struct pt_regs regs) | 40 | struct pt_regs regs) |
| 45 | { | 41 | { |
| 46 | sigset_t saveset; | ||
| 47 | |||
| 48 | mask &= _BLOCKABLE; | 42 | mask &= _BLOCKABLE; |
| 49 | spin_lock_irq(¤t->sighand->siglock); | 43 | spin_lock_irq(¤t->sighand->siglock); |
| 50 | saveset = current->blocked; | 44 | current->saved_sigmask = current->blocked; |
| 51 | siginitset(¤t->blocked, mask); | 45 | siginitset(¤t->blocked, mask); |
| 52 | recalc_sigpending(); | 46 | recalc_sigpending(); |
| 53 | spin_unlock_irq(¤t->sighand->siglock); | 47 | spin_unlock_irq(¤t->sighand->siglock); |
| 54 | 48 | ||
| 55 | regs.regs[0] = -EINTR; | 49 | current->state = TASK_INTERRUPTIBLE; |
| 56 | while (1) { | 50 | schedule(); |
| 57 | current->state = TASK_INTERRUPTIBLE; | 51 | set_thread_flag(TIF_RESTORE_SIGMASK); |
| 58 | schedule(); | 52 | return -ERESTARTNOHAND; |
| 59 | if (do_signal(®s, &saveset)) | ||
| 60 | return -EINTR; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | asmlinkage int | ||
| 65 | sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, | ||
| 66 | unsigned long r6, unsigned long r7, | ||
| 67 | struct pt_regs regs) | ||
| 68 | { | ||
| 69 | sigset_t saveset, newset; | ||
| 70 | |||
| 71 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
| 72 | if (sigsetsize != sizeof(sigset_t)) | ||
| 73 | return -EINVAL; | ||
| 74 | |||
| 75 | if (copy_from_user(&newset, unewset, sizeof(newset))) | ||
| 76 | return -EFAULT; | ||
| 77 | sigdelsetmask(&newset, ~_BLOCKABLE); | ||
| 78 | spin_lock_irq(¤t->sighand->siglock); | ||
| 79 | saveset = current->blocked; | ||
| 80 | current->blocked = newset; | ||
| 81 | recalc_sigpending(); | ||
| 82 | spin_unlock_irq(¤t->sighand->siglock); | ||
| 83 | |||
| 84 | regs.regs[0] = -EINTR; | ||
| 85 | while (1) { | ||
| 86 | current->state = TASK_INTERRUPTIBLE; | ||
| 87 | schedule(); | ||
| 88 | if (do_signal(®s, &saveset)) | ||
| 89 | return -EINTR; | ||
| 90 | } | ||
| 91 | } | 53 | } |
| 92 | 54 | ||
| 93 | asmlinkage int | 55 | asmlinkage int |
| @@ -348,7 +310,12 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) | |||
| 348 | return (void __user *)((sp - frame_size) & -8ul); | 310 | return (void __user *)((sp - frame_size) & -8ul); |
| 349 | } | 311 | } |
| 350 | 312 | ||
| 351 | static void setup_frame(int sig, struct k_sigaction *ka, | 313 | /* These symbols are defined with the addresses in the vsyscall page. |
| 314 | See vsyscall-trapa.S. */ | ||
| 315 | extern void __user __kernel_sigreturn; | ||
| 316 | extern void __user __kernel_rt_sigreturn; | ||
| 317 | |||
| 318 | static int setup_frame(int sig, struct k_sigaction *ka, | ||
| 352 | sigset_t *set, struct pt_regs *regs) | 319 | sigset_t *set, struct pt_regs *regs) |
| 353 | { | 320 | { |
| 354 | struct sigframe __user *frame; | 321 | struct sigframe __user *frame; |
| @@ -368,15 +335,18 @@ static void setup_frame(int sig, struct k_sigaction *ka, | |||
| 368 | 335 | ||
| 369 | err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); | 336 | err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); |
| 370 | 337 | ||
| 371 | if (_NSIG_WORDS > 1) { | 338 | if (_NSIG_WORDS > 1) |
| 372 | err |= __copy_to_user(frame->extramask, &set->sig[1], | 339 | err |= __copy_to_user(frame->extramask, &set->sig[1], |
| 373 | sizeof(frame->extramask)); | 340 | sizeof(frame->extramask)); |
| 374 | } | ||
| 375 | 341 | ||
| 376 | /* Set up to return from userspace. If provided, use a stub | 342 | /* Set up to return from userspace. If provided, use a stub |
| 377 | already in userspace. */ | 343 | already in userspace. */ |
| 378 | if (ka->sa.sa_flags & SA_RESTORER) { | 344 | if (ka->sa.sa_flags & SA_RESTORER) { |
| 379 | regs->pr = (unsigned long) ka->sa.sa_restorer; | 345 | regs->pr = (unsigned long) ka->sa.sa_restorer; |
| 346 | #ifdef CONFIG_VSYSCALL | ||
| 347 | } else if (likely(current->mm->context.vdso)) { | ||
| 348 | regs->pr = VDSO_SYM(&__kernel_sigreturn); | ||
| 349 | #endif | ||
| 380 | } else { | 350 | } else { |
| 381 | /* Generate return code (system call to sigreturn) */ | 351 | /* Generate return code (system call to sigreturn) */ |
| 382 | err |= __put_user(MOVW(7), &frame->retcode[0]); | 352 | err |= __put_user(MOVW(7), &frame->retcode[0]); |
| @@ -402,21 +372,22 @@ static void setup_frame(int sig, struct k_sigaction *ka, | |||
| 402 | 372 | ||
| 403 | set_fs(USER_DS); | 373 | set_fs(USER_DS); |
| 404 | 374 | ||
| 405 | #if DEBUG_SIG | 375 | pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", |
| 406 | printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", | 376 | current->comm, current->pid, frame, regs->pc, regs->pr); |
| 407 | current->comm, current->pid, frame, regs->pc, regs->pr); | ||
| 408 | #endif | ||
| 409 | 377 | ||
| 410 | flush_cache_sigtramp(regs->pr); | 378 | flush_cache_sigtramp(regs->pr); |
| 379 | |||
| 411 | if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) | 380 | if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) |
| 412 | flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); | 381 | flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); |
| 413 | return; | 382 | |
| 383 | return 0; | ||
| 414 | 384 | ||
| 415 | give_sigsegv: | 385 | give_sigsegv: |
| 416 | force_sigsegv(sig, current); | 386 | force_sigsegv(sig, current); |
| 387 | return -EFAULT; | ||
| 417 | } | 388 | } |
| 418 | 389 | ||
| 419 | static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | 390 | static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, |
| 420 | sigset_t *set, struct pt_regs *regs) | 391 | sigset_t *set, struct pt_regs *regs) |
| 421 | { | 392 | { |
| 422 | struct rt_sigframe __user *frame; | 393 | struct rt_sigframe __user *frame; |
| @@ -452,6 +423,10 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
| 452 | already in userspace. */ | 423 | already in userspace. */ |
| 453 | if (ka->sa.sa_flags & SA_RESTORER) { | 424 | if (ka->sa.sa_flags & SA_RESTORER) { |
| 454 | regs->pr = (unsigned long) ka->sa.sa_restorer; | 425 | regs->pr = (unsigned long) ka->sa.sa_restorer; |
| 426 | #ifdef CONFIG_VSYSCALL | ||
| 427 | } else if (likely(current->mm->context.vdso)) { | ||
| 428 | regs->pr = VDSO_SYM(&__kernel_rt_sigreturn); | ||
| 429 | #endif | ||
| 455 | } else { | 430 | } else { |
| 456 | /* Generate return code (system call to rt_sigreturn) */ | 431 | /* Generate return code (system call to rt_sigreturn) */ |
| 457 | err |= __put_user(MOVW(7), &frame->retcode[0]); | 432 | err |= __put_user(MOVW(7), &frame->retcode[0]); |
| @@ -477,28 +452,31 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
| 477 | 452 | ||
| 478 | set_fs(USER_DS); | 453 | set_fs(USER_DS); |
| 479 | 454 | ||
| 480 | #if DEBUG_SIG | 455 | pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", |
| 481 | printk("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", | 456 | current->comm, current->pid, frame, regs->pc, regs->pr); |
| 482 | current->comm, current->pid, frame, regs->pc, regs->pr); | ||
| 483 | #endif | ||
| 484 | 457 | ||
| 485 | flush_cache_sigtramp(regs->pr); | 458 | flush_cache_sigtramp(regs->pr); |
| 459 | |||
| 486 | if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) | 460 | if ((-regs->pr & (L1_CACHE_BYTES-1)) < sizeof(frame->retcode)) |
| 487 | flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); | 461 | flush_cache_sigtramp(regs->pr + L1_CACHE_BYTES); |
| 488 | return; | 462 | |
| 463 | return 0; | ||
| 489 | 464 | ||
| 490 | give_sigsegv: | 465 | give_sigsegv: |
| 491 | force_sigsegv(sig, current); | 466 | force_sigsegv(sig, current); |
| 467 | return -EFAULT; | ||
| 492 | } | 468 | } |
| 493 | 469 | ||
| 494 | /* | 470 | /* |
| 495 | * OK, we're invoking a handler | 471 | * OK, we're invoking a handler |
| 496 | */ | 472 | */ |
| 497 | 473 | ||
| 498 | static void | 474 | static int |
| 499 | handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, | 475 | handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, |
| 500 | sigset_t *oldset, struct pt_regs *regs) | 476 | sigset_t *oldset, struct pt_regs *regs) |
| 501 | { | 477 | { |
| 478 | int ret; | ||
| 479 | |||
| 502 | /* Are we from a system call? */ | 480 | /* Are we from a system call? */ |
| 503 | if (regs->tra >= 0) { | 481 | if (regs->tra >= 0) { |
| 504 | /* If so, check system call restarting.. */ | 482 | /* If so, check system call restarting.. */ |
| @@ -539,19 +517,23 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, | |||
| 539 | 517 | ||
| 540 | /* Set up the stack frame */ | 518 | /* Set up the stack frame */ |
| 541 | if (ka->sa.sa_flags & SA_SIGINFO) | 519 | if (ka->sa.sa_flags & SA_SIGINFO) |
| 542 | setup_rt_frame(sig, ka, info, oldset, regs); | 520 | ret = setup_rt_frame(sig, ka, info, oldset, regs); |
| 543 | else | 521 | else |
| 544 | setup_frame(sig, ka, oldset, regs); | 522 | ret = setup_frame(sig, ka, oldset, regs); |
| 545 | 523 | ||
| 546 | if (ka->sa.sa_flags & SA_ONESHOT) | 524 | if (ka->sa.sa_flags & SA_ONESHOT) |
| 547 | ka->sa.sa_handler = SIG_DFL; | 525 | ka->sa.sa_handler = SIG_DFL; |
| 548 | 526 | ||
| 549 | spin_lock_irq(¤t->sighand->siglock); | 527 | if (ret == 0) { |
| 550 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | 528 | spin_lock_irq(¤t->sighand->siglock); |
| 551 | if (!(ka->sa.sa_flags & SA_NODEFER)) | 529 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); |
| 552 | sigaddset(¤t->blocked,sig); | 530 | if (!(ka->sa.sa_flags & SA_NODEFER)) |
| 553 | recalc_sigpending(); | 531 | sigaddset(¤t->blocked,sig); |
| 554 | spin_unlock_irq(¤t->sighand->siglock); | 532 | recalc_sigpending(); |
| 533 | spin_unlock_irq(¤t->sighand->siglock); | ||
| 534 | } | ||
| 535 | |||
| 536 | return ret; | ||
| 555 | } | 537 | } |
| 556 | 538 | ||
| 557 | /* | 539 | /* |
| @@ -563,11 +545,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, | |||
| 563 | * the kernel can handle, and then we build all the user-level signal handling | 545 | * the kernel can handle, and then we build all the user-level signal handling |
| 564 | * stack-frames in one go after that. | 546 | * stack-frames in one go after that. |
| 565 | */ | 547 | */ |
| 566 | int do_signal(struct pt_regs *regs, sigset_t *oldset) | 548 | static void do_signal(struct pt_regs *regs, unsigned int save_r0) |
| 567 | { | 549 | { |
| 568 | siginfo_t info; | 550 | siginfo_t info; |
| 569 | int signr; | 551 | int signr; |
| 570 | struct k_sigaction ka; | 552 | struct k_sigaction ka; |
| 553 | sigset_t *oldset; | ||
| 571 | 554 | ||
| 572 | /* | 555 | /* |
| 573 | * We want the common case to go fast, which | 556 | * We want the common case to go fast, which |
| @@ -576,19 +559,27 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) | |||
| 576 | * if so. | 559 | * if so. |
| 577 | */ | 560 | */ |
| 578 | if (!user_mode(regs)) | 561 | if (!user_mode(regs)) |
| 579 | return 1; | 562 | return; |
| 580 | 563 | ||
| 581 | if (try_to_freeze()) | 564 | if (try_to_freeze()) |
| 582 | goto no_signal; | 565 | goto no_signal; |
| 583 | 566 | ||
| 584 | if (!oldset) | 567 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
| 568 | oldset = ¤t->saved_sigmask; | ||
| 569 | else | ||
| 585 | oldset = ¤t->blocked; | 570 | oldset = ¤t->blocked; |
| 586 | 571 | ||
| 587 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 572 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
| 588 | if (signr > 0) { | 573 | if (signr > 0) { |
| 589 | /* Whee! Actually deliver the signal. */ | 574 | /* Whee! Actually deliver the signal. */ |
| 590 | handle_signal(signr, &ka, &info, oldset, regs); | 575 | if (handle_signal(signr, &ka, &info, oldset, regs) == 0) { |
| 591 | return 1; | 576 | /* a signal was successfully delivered; the saved |
| 577 | * sigmask will have been stored in the signal frame, | ||
| 578 | * and will be restored by sigreturn, so we can simply | ||
| 579 | * clear the TIF_RESTORE_SIGMASK flag */ | ||
| 580 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
| 581 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
| 582 | } | ||
| 592 | } | 583 | } |
| 593 | 584 | ||
| 594 | no_signal: | 585 | no_signal: |
| @@ -597,10 +588,27 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) | |||
| 597 | /* Restart the system call - no handlers present */ | 588 | /* Restart the system call - no handlers present */ |
| 598 | if (regs->regs[0] == -ERESTARTNOHAND || | 589 | if (regs->regs[0] == -ERESTARTNOHAND || |
| 599 | regs->regs[0] == -ERESTARTSYS || | 590 | regs->regs[0] == -ERESTARTSYS || |
| 600 | regs->regs[0] == -ERESTARTNOINTR || | 591 | regs->regs[0] == -ERESTARTNOINTR) { |
| 601 | regs->regs[0] == -ERESTART_RESTARTBLOCK) { | 592 | regs->regs[0] = save_r0; |
| 593 | regs->pc -= 2; | ||
| 594 | } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { | ||
| 602 | regs->pc -= 2; | 595 | regs->pc -= 2; |
| 596 | regs->regs[3] = __NR_restart_syscall; | ||
| 603 | } | 597 | } |
| 604 | } | 598 | } |
| 605 | return 0; | 599 | |
| 600 | /* if there's no signal to deliver, we just put the saved sigmask | ||
| 601 | * back */ | ||
| 602 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
| 603 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
| 604 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0, | ||
| 609 | __u32 thread_info_flags) | ||
| 610 | { | ||
| 611 | /* deal with pending signal delivery */ | ||
| 612 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) | ||
| 613 | do_signal(regs, save_r0); | ||
| 606 | } | 614 | } |
