summaryrefslogtreecommitdiffstats
path: root/kernel/signal.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-08-28 22:00:09 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-08-28 22:00:09 +0000
commit1a1d77dd589de5a567fa95e36aa6999c704ceca4 (patch)
tree141e31f89f18b9fe0831f31852e0435ceaccafc5 /kernel/signal.c
parentfb9c690a18b3d66925a65b17441c37fa14d4370b (diff)
Merge with 2.4.0-test7.
Diffstat (limited to 'kernel/signal.c')
-rw-r--r--kernel/signal.c105
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(&current->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(&current->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);
/*