summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/autofs/waitq.c15
-rw-r--r--fs/nfsd/nfssvc.c14
-rw-r--r--include/linux/signal.h1
-rw-r--r--kernel/signal.c58
4 files changed, 48 insertions, 40 deletions
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
index 6c82dc144b33..d08b648ccae5 100644
--- a/fs/autofs/waitq.c
+++ b/fs/autofs/waitq.c
@@ -158,21 +158,14 @@ int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name)
if ( wq->name ) {
/* Block all but "shutdown" signals while waiting */
- sigset_t oldset;
- unsigned long irqflags;
+ sigset_t sigmask;
- spin_lock_irqsave(&current->sighand->siglock, irqflags);
- oldset = current->blocked;
- siginitsetinv(&current->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
+ siginitsetinv(&sigmask, SHUTDOWN_SIGS);
+ sigprocmask(SIG_BLOCK, &sigmask, &sigmask);
interruptible_sleep_on(&wq->queue);
- spin_lock_irqsave(&current->sighand->siglock, irqflags);
- current->blocked = oldset;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
+ sigprocmask(SIG_SETMASK, &sigmask, NULL);
} else {
DPRINTK(("autofs_wait: skipped sleeping\n"));
}
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 3919e77036e3..369c2ce897f4 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -168,6 +168,7 @@ nfsd(struct svc_rqst *rqstp)
struct svc_serv *serv = rqstp->rq_server;
int err;
struct nfsd_list me;
+ sigset_t shutdown_mask, allowed_mask;
/* Lock module and set up kernel thread */
MOD_INC_USE_COUNT;
@@ -176,6 +177,9 @@ nfsd(struct svc_rqst *rqstp)
sprintf(current->comm, "nfsd");
current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+ siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS);
+ siginitsetinv(&allowed_mask, ALLOWED_SIGS);
+
nfsdstats.th_cnt++;
lockd_up(); /* start lockd */
@@ -189,10 +193,7 @@ nfsd(struct svc_rqst *rqstp)
*/
for (;;) {
/* Block all but the shutdown signals */
- spin_lock_irq(&current->sighand->siglock);
- siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ sigprocmask(SIG_SETMASK, &shutdown_mask, NULL);
/*
* Find a socket with data available and call its
@@ -210,10 +211,7 @@ nfsd(struct svc_rqst *rqstp)
exp_readlock();
/* Process request with signals blocked. */
- spin_lock_irq(&current->sighand->siglock);
- siginitsetinv(&current->blocked, ALLOWED_SIGS);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ sigprocmask(SIG_SETMASK, &allowed_mask, NULL);
svc_process(serv, rqstp);
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 53d9e4324557..dd2e25e2129f 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -204,6 +204,7 @@ static inline void init_sigpending(struct sigpending *sig)
}
extern long do_sigpending(void *, unsigned long);
+extern int sigprocmask(int, sigset_t *, sigset_t *);
#ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER
struct pt_regs;
diff --git a/kernel/signal.c b/kernel/signal.c
index 9dc47e8a804a..b1a35f819baa 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1559,6 +1559,7 @@ EXPORT_SYMBOL(kill_sl_info);
EXPORT_SYMBOL(notify_parent);
EXPORT_SYMBOL(send_sig);
EXPORT_SYMBOL(send_sig_info);
+EXPORT_SYMBOL(sigprocmask);
EXPORT_SYMBOL(block_all_signals);
EXPORT_SYMBOL(unblock_all_signals);
@@ -1585,6 +1586,41 @@ long do_no_restart_syscall(struct restart_block *param)
* used by various programs)
*/
+/*
+ * This is also useful for kernel threads that want to temporarily
+ * (or permanently) block certain signals.
+ *
+ * NOTE! Unlike the user-mode sys_sigprocmask(), the kernel
+ * interface happily blocks "unblockable" signals like SIGKILL
+ * and friends.
+ */
+int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
+{
+ int error;
+ sigset_t old_block;
+
+ spin_lock_irq(&current->sighand->siglock);
+ old_block = current->blocked;
+ error = 0;
+ switch (how) {
+ case SIG_BLOCK:
+ sigorsets(&current->blocked, &current->blocked, set);
+ break;
+ case SIG_UNBLOCK:
+ signandsets(&current->blocked, &current->blocked, set);
+ break;
+ case SIG_SETMASK:
+ current->blocked = *set;
+ default:
+ error = -EINVAL;
+ }
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ if (oldset)
+ *oldset = old_block;
+ return error;
+}
+
asmlinkage long
sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize)
{
@@ -1601,27 +1637,7 @@ sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize)
goto out;
sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
- spin_lock_irq(&current->sighand->siglock);
- old_set = current->blocked;
-
- error = 0;
- switch (how) {
- default:
- error = -EINVAL;
- break;
- case SIG_BLOCK:
- sigorsets(&new_set, &old_set, &new_set);
- break;
- case SIG_UNBLOCK:
- signandsets(&new_set, &old_set, &new_set);
- break;
- case SIG_SETMASK:
- break;
- }
-
- current->blocked = new_set;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ error = sigprocmask(how, &new_set, &old_set);
if (error)
goto out;
if (oset)