diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-08-28 22:00:09 +0000 |
commit | 1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch) | |
tree | 141e31f89f18b9fe0831f31852e0435ceaccafc5 /kernel/signal.c | |
parent | fb9c690a18b3d66925a65b17441c37fa14d4370b (diff) |
Merge with 2.4.0-test7.
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 105 |
1 files changed, 80 insertions, 25 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 4e73949da..b64225778 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -46,6 +46,42 @@ void __init signals_init(void) } +/* Given the mask, find the first available signal that should be serviced. */ + +static int +next_signal(sigset_t *signal, sigset_t *mask) +{ + unsigned long i, *s, *m, x; + int sig = 0; + + s = signal->sig; + m = mask->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; + } + 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 + break; + sig += ffz(~x); + break; + + case 1: if ((x = *s &~ *m) != 0) + sig = ffz(~x) + 1; + break; + } + + return sig; +} + /* * Flush all pending signals for a task. */ @@ -87,6 +123,32 @@ flush_signal_handlers(struct task_struct *t) } } +/* Notify the system that a driver wants to block all signals for this + * process, and wants to be notified if any signals at all were to be + * sent/acted upon. If the notifier routine returns non-zero, then the + * signal will be acted upon after all. If the notifier routine returns 0, + * then then signal will be blocked. Only one block per process is + * allowed. priv is a pointer to private data that the notifier routine + * can use to determine if the signal should be blocked or not. */ + +void +block_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask) +{ + current->notifier_mask = mask; + current->notifier_data = priv; + current->notifier = notifier; +} + +/* Notify the system that blocking has ended. */ + +void +unblock_all_signals(void) +{ + current->notifier = NULL; + current->notifier_data = NULL; + recalc_sigpending(current); +} + /* * Dequeue a signal and return the element to the caller, which is * expected to free it. @@ -97,7 +159,6 @@ flush_signal_handlers(struct task_struct *t) int dequeue_signal(sigset_t *mask, siginfo_t *info) { - unsigned long i, *s, *m, x; int sig = 0; #if DEBUG_SIG @@ -105,30 +166,22 @@ printk("SIG dequeue (%s:%d): %d ", current->comm, current->pid, signal_pending(current)); #endif - /* Find the first desired signal that is pending. */ - s = current->signal.sig; - m = mask->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; - } - 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 - break; - sig += ffz(~x); - break; - - case 1: if ((x = *s &~ *m) != 0) - sig = ffz(~x) + 1; - break; + sig = next_signal(¤t->signal, mask); + if (current->notifier) { + sigset_t merged; + int i; + int altsig; + + for (i = 0; i < _NSIG_WORDS; i++) + merged.sig[i] = mask->sig[i] + | current->notifier_mask->sig[i]; + altsig = next_signal(¤t->signal, &merged); + if (sig != altsig) { + if (!(current->notifier)(current->notifier_data)) { + current->sigpending = 0; + return 0; + } + } } if (sig) { @@ -658,6 +711,8 @@ EXPORT_SYMBOL(notify_parent); EXPORT_SYMBOL(recalc_sigpending); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); +EXPORT_SYMBOL(block_all_signals); +EXPORT_SYMBOL(unblock_all_signals); /* |