diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2003-02-09 02:17:26 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-02-09 02:17:26 -0800 |
| commit | f3cee4691de36dbdeb3bcaaedaaf409ad2b4ccdb (patch) | |
| tree | 8bd3bfb8e9166a93fb43f710d56b13b2d9eb82d1 | |
| parent | 01f5c53ced535d7cb1aa3e423b969c6728895b07 (diff) | |
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.
| -rw-r--r-- | fs/autofs/waitq.c | 15 | ||||
| -rw-r--r-- | fs/nfsd/nfssvc.c | 14 | ||||
| -rw-r--r-- | include/linux/signal.h | 1 | ||||
| -rw-r--r-- | kernel/signal.c | 58 |
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(¤t->sighand->siglock, irqflags); - oldset = current->blocked; - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); + siginitsetinv(&sigmask, SHUTDOWN_SIGS); + sigprocmask(SIG_BLOCK, &sigmask, &sigmask); interruptible_sleep_on(&wq->queue); - spin_lock_irqsave(¤t->sighand->siglock, irqflags); - current->blocked = oldset; - recalc_sigpending(); - spin_unlock_irqrestore(¤t->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(¤t->sighand->siglock); - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - recalc_sigpending(); - spin_unlock_irq(¤t->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(¤t->sighand->siglock); - siginitsetinv(¤t->blocked, ALLOWED_SIGS); - recalc_sigpending(); - spin_unlock_irq(¤t->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(¤t->sighand->siglock); + old_block = current->blocked; + error = 0; + switch (how) { + case SIG_BLOCK: + sigorsets(¤t->blocked, ¤t->blocked, set); + break; + case SIG_UNBLOCK: + signandsets(¤t->blocked, ¤t->blocked, set); + break; + case SIG_SETMASK: + current->blocked = *set; + default: + error = -EINVAL; + } + recalc_sigpending(); + spin_unlock_irq(¤t->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(¤t->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(¤t->sighand->siglock); + error = sigprocmask(how, &new_set, &old_set); if (error) goto out; if (oset) |
