Commit f3cee469 authored by Linus Torvalds's avatar Linus Torvalds

Make sigprocmask() available to kernel threads too, since a lot of

them do want to temporarily block signals.

Kernel users can also block signals that are normally unblockable
to user space, ie SIGKILL and SIGSTOP.

Make nfsd and autofs use the new interface, as an example to others. 
parent 01f5c53c
......@@ -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"));
}
......
......@@ -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);
......
......@@ -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;
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment