Commit 943df1485a8ff0e600729e082e568ece04d4de9e
1 parent
fe0faa005d
Exists in
master
and in
39 other branches
signal: introduce do_sigtimedwait() to factor out compat/native code
Factor out the common code in sys_rt_sigtimedwait/compat_sys_rt_sigtimedwait to the new helper, do_sigtimedwait(). Add the comment to document the extra tick we add to timespec_to_jiffies(ts), thanks to Linus who explained this to me. Perhaps it would be better to move compat_sys_rt_sigtimedwait() into signal.c under CONFIG_COMPAT, then we can make do_sigtimedwait() static. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Tejun Heo <tj@kernel.org> Reviewed-by: Matt Fleming <matt.fleming@linux.intel.com>
Showing 3 changed files with 74 additions and 79 deletions Side-by-side Diff
include/linux/signal.h
... | ... | @@ -242,6 +242,8 @@ |
242 | 242 | extern long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, |
243 | 243 | siginfo_t *info); |
244 | 244 | extern long do_sigpending(void __user *, unsigned long); |
245 | +extern int do_sigtimedwait(const sigset_t *, siginfo_t *, | |
246 | + const struct timespec *); | |
245 | 247 | extern int sigprocmask(int, sigset_t *, sigset_t *); |
246 | 248 | extern void set_current_blocked(const sigset_t *); |
247 | 249 | extern int show_unhandled_signals; |
kernel/compat.c
... | ... | @@ -890,10 +890,9 @@ |
890 | 890 | { |
891 | 891 | compat_sigset_t s32; |
892 | 892 | sigset_t s; |
893 | - int sig; | |
894 | 893 | struct timespec t; |
895 | 894 | siginfo_t info; |
896 | - long ret, timeout; | |
895 | + long ret; | |
897 | 896 | |
898 | 897 | if (sigsetsize != sizeof(sigset_t)) |
899 | 898 | return -EINVAL; |
900 | 899 | |
901 | 900 | |
902 | 901 | |
903 | 902 | |
904 | 903 | |
905 | 904 | |
906 | 905 | |
... | ... | @@ -901,45 +900,19 @@ |
901 | 900 | if (copy_from_user(&s32, uthese, sizeof(compat_sigset_t))) |
902 | 901 | return -EFAULT; |
903 | 902 | sigset_from_compat(&s, &s32); |
904 | - sigdelsetmask(&s,sigmask(SIGKILL)|sigmask(SIGSTOP)); | |
905 | - signotset(&s); | |
906 | 903 | |
907 | - timeout = MAX_SCHEDULE_TIMEOUT; | |
908 | 904 | if (uts) { |
909 | - if (get_compat_timespec (&t, uts)) | |
905 | + if (get_compat_timespec(&t, uts)) | |
910 | 906 | return -EFAULT; |
911 | - if (!timespec_valid(&t)) | |
912 | - return -EINVAL; | |
913 | - timeout = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec); | |
914 | 907 | } |
915 | 908 | |
916 | - spin_lock_irq(¤t->sighand->siglock); | |
917 | - sig = dequeue_signal(current, &s, &info); | |
918 | - if (!sig && timeout) { | |
919 | - current->real_blocked = current->blocked; | |
920 | - sigandsets(¤t->blocked, ¤t->blocked, &s); | |
921 | - recalc_sigpending(); | |
922 | - spin_unlock_irq(¤t->sighand->siglock); | |
909 | + ret = do_sigtimedwait(&s, &info, uts ? &t : NULL); | |
923 | 910 | |
924 | - timeout = schedule_timeout_interruptible(timeout); | |
925 | - | |
926 | - spin_lock_irq(¤t->sighand->siglock); | |
927 | - sig = dequeue_signal(current, &s, &info); | |
928 | - current->blocked = current->real_blocked; | |
929 | - siginitset(¤t->real_blocked, 0); | |
930 | - recalc_sigpending(); | |
911 | + if (ret > 0 && uinfo) { | |
912 | + if (copy_siginfo_to_user32(uinfo, &info)) | |
913 | + ret = -EFAULT; | |
931 | 914 | } |
932 | - spin_unlock_irq(¤t->sighand->siglock); | |
933 | 915 | |
934 | - if (sig) { | |
935 | - ret = sig; | |
936 | - if (uinfo) { | |
937 | - if (copy_siginfo_to_user32(uinfo, &info)) | |
938 | - ret = -EFAULT; | |
939 | - } | |
940 | - } else { | |
941 | - ret = timeout?-EINTR:-EAGAIN; | |
942 | - } | |
943 | 916 | return ret; |
944 | 917 | |
945 | 918 | } |
kernel/signal.c
... | ... | @@ -2504,6 +2504,66 @@ |
2504 | 2504 | #endif |
2505 | 2505 | |
2506 | 2506 | /** |
2507 | + * do_sigtimedwait - wait for queued signals specified in @which | |
2508 | + * @which: queued signals to wait for | |
2509 | + * @info: if non-null, the signal's siginfo is returned here | |
2510 | + * @ts: upper bound on process time suspension | |
2511 | + */ | |
2512 | +int do_sigtimedwait(const sigset_t *which, siginfo_t *info, | |
2513 | + const struct timespec *ts) | |
2514 | +{ | |
2515 | + struct task_struct *tsk = current; | |
2516 | + long timeout = MAX_SCHEDULE_TIMEOUT; | |
2517 | + sigset_t mask = *which; | |
2518 | + int sig; | |
2519 | + | |
2520 | + if (ts) { | |
2521 | + if (!timespec_valid(ts)) | |
2522 | + return -EINVAL; | |
2523 | + timeout = timespec_to_jiffies(ts); | |
2524 | + /* | |
2525 | + * We can be close to the next tick, add another one | |
2526 | + * to ensure we will wait at least the time asked for. | |
2527 | + */ | |
2528 | + if (ts->tv_sec || ts->tv_nsec) | |
2529 | + timeout++; | |
2530 | + } | |
2531 | + | |
2532 | + /* | |
2533 | + * Invert the set of allowed signals to get those we want to block. | |
2534 | + */ | |
2535 | + sigdelsetmask(&mask, sigmask(SIGKILL) | sigmask(SIGSTOP)); | |
2536 | + signotset(&mask); | |
2537 | + | |
2538 | + spin_lock_irq(&tsk->sighand->siglock); | |
2539 | + sig = dequeue_signal(tsk, &mask, info); | |
2540 | + if (!sig && timeout) { | |
2541 | + /* | |
2542 | + * None ready, temporarily unblock those we're interested | |
2543 | + * while we are sleeping in so that we'll be awakened when | |
2544 | + * they arrive. | |
2545 | + */ | |
2546 | + tsk->real_blocked = tsk->blocked; | |
2547 | + sigandsets(&tsk->blocked, &tsk->blocked, &mask); | |
2548 | + recalc_sigpending(); | |
2549 | + spin_unlock_irq(&tsk->sighand->siglock); | |
2550 | + | |
2551 | + timeout = schedule_timeout_interruptible(timeout); | |
2552 | + | |
2553 | + spin_lock_irq(&tsk->sighand->siglock); | |
2554 | + sig = dequeue_signal(tsk, &mask, info); | |
2555 | + tsk->blocked = tsk->real_blocked; | |
2556 | + siginitset(&tsk->real_blocked, 0); | |
2557 | + recalc_sigpending(); | |
2558 | + } | |
2559 | + spin_unlock_irq(&tsk->sighand->siglock); | |
2560 | + | |
2561 | + if (sig) | |
2562 | + return sig; | |
2563 | + return timeout ? -EINTR : -EAGAIN; | |
2564 | +} | |
2565 | + | |
2566 | +/** | |
2507 | 2567 | * sys_rt_sigtimedwait - synchronously wait for queued signals specified |
2508 | 2568 | * in @uthese |
2509 | 2569 | * @uthese: queued signals to wait for |
2510 | 2570 | |
... | ... | @@ -2515,11 +2575,10 @@ |
2515 | 2575 | siginfo_t __user *, uinfo, const struct timespec __user *, uts, |
2516 | 2576 | size_t, sigsetsize) |
2517 | 2577 | { |
2518 | - int ret, sig; | |
2519 | 2578 | sigset_t these; |
2520 | 2579 | struct timespec ts; |
2521 | 2580 | siginfo_t info; |
2522 | - long timeout; | |
2581 | + int ret; | |
2523 | 2582 | |
2524 | 2583 | /* XXX: Don't preclude handling different sized sigset_t's. */ |
2525 | 2584 | if (sigsetsize != sizeof(sigset_t)) |
2526 | 2585 | |
2527 | 2586 | |
2528 | 2587 | |
... | ... | @@ -2528,55 +2587,16 @@ |
2528 | 2587 | if (copy_from_user(&these, uthese, sizeof(these))) |
2529 | 2588 | return -EFAULT; |
2530 | 2589 | |
2531 | - /* | |
2532 | - * Invert the set of allowed signals to get those we | |
2533 | - * want to block. | |
2534 | - */ | |
2535 | - sigdelsetmask(&these, sigmask(SIGKILL)|sigmask(SIGSTOP)); | |
2536 | - signotset(&these); | |
2537 | - | |
2538 | - timeout = MAX_SCHEDULE_TIMEOUT; | |
2539 | 2590 | if (uts) { |
2540 | 2591 | if (copy_from_user(&ts, uts, sizeof(ts))) |
2541 | 2592 | return -EFAULT; |
2542 | - if (!timespec_valid(&ts)) | |
2543 | - return -EINVAL; | |
2544 | - timeout = timespec_to_jiffies(&ts) + (ts.tv_sec || ts.tv_nsec); | |
2545 | 2593 | } |
2546 | 2594 | |
2547 | - spin_lock_irq(¤t->sighand->siglock); | |
2548 | - sig = dequeue_signal(current, &these, &info); | |
2549 | - if (!sig && timeout) { | |
2550 | - /* | |
2551 | - * None ready -- temporarily unblock those we're | |
2552 | - * interested while we are sleeping in so that we'll | |
2553 | - * be awakened when they arrive. | |
2554 | - */ | |
2555 | - current->real_blocked = current->blocked; | |
2556 | - sigandsets(¤t->blocked, ¤t->blocked, &these); | |
2557 | - recalc_sigpending(); | |
2558 | - spin_unlock_irq(¤t->sighand->siglock); | |
2595 | + ret = do_sigtimedwait(&these, &info, uts ? &ts : NULL); | |
2559 | 2596 | |
2560 | - timeout = schedule_timeout_interruptible(timeout); | |
2561 | - | |
2562 | - spin_lock_irq(¤t->sighand->siglock); | |
2563 | - sig = dequeue_signal(current, &these, &info); | |
2564 | - current->blocked = current->real_blocked; | |
2565 | - siginitset(¤t->real_blocked, 0); | |
2566 | - recalc_sigpending(); | |
2567 | - } | |
2568 | - spin_unlock_irq(¤t->sighand->siglock); | |
2569 | - | |
2570 | - if (sig) { | |
2571 | - ret = sig; | |
2572 | - if (uinfo) { | |
2573 | - if (copy_siginfo_to_user(uinfo, &info)) | |
2574 | - ret = -EFAULT; | |
2575 | - } | |
2576 | - } else { | |
2577 | - ret = -EAGAIN; | |
2578 | - if (timeout) | |
2579 | - ret = -EINTR; | |
2597 | + if (ret > 0 && uinfo) { | |
2598 | + if (copy_siginfo_to_user(uinfo, &info)) | |
2599 | + ret = -EFAULT; | |
2580 | 2600 | } |
2581 | 2601 | |
2582 | 2602 | return ret; |