From 7bc9020b72e17bfb4d1764ccf3c7201136976e18 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 12 Oct 2003 19:35:05 -0700 Subject: [PATCH] [PATCH] Kernel thread signal handling. - add disallow_signal() to complement allow_signal(), rather than having different subsystems try to do it by hand. - add a version of dequeue_signal() which does the necessary locking on its own, again to avoid having modules have to care. - let allow_signal() to actually allow signals other than SIGKILL. Currently they get either converted to SIGKILL or silently dropped, according to whether your kernel thread happens to have sa_handler set for the signal in question. (Barf alert: we do this by just installing a dummy handler) - make jffs2 use the cleaned up infrastructure --- fs/jffs2/background.c | 25 ++++++++++++++----------- include/linux/sched.h | 14 ++++++++++++++ kernel/exit.c | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 8d60e62800b0a..ebef6567753c8 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "nodelist.h" @@ -75,6 +76,9 @@ static int jffs2_garbage_collect_thread(void *_c) struct jffs2_sb_info *c = _c; daemonize("jffs2_gcd_mtd%d", c->mtd->index); + allow_signal(SIGKILL); + allow_signal(SIGSTOP); + allow_signal(SIGCONT); c->gc_task = current; up(&c->gc_thread_start); @@ -82,10 +86,7 @@ static int jffs2_garbage_collect_thread(void *_c) set_user_nice(current, 10); for (;;) { - spin_lock_irq(¤t_sig_lock); - siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(); - spin_unlock_irq(¤t_sig_lock); + allow_signal(SIGHUP); if (!thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); @@ -97,6 +98,13 @@ static int jffs2_garbage_collect_thread(void *_c) schedule(); } + if (current->flags & PF_FREEZE) { + refrigerator(0); + /* refrigerator() should recalc sigpending for us + but doesn't. No matter - allow_signal() will. */ + continue; + } + cond_resched(); /* Put_super will send a SIGKILL and then wait on the sem. @@ -105,9 +113,7 @@ static int jffs2_garbage_collect_thread(void *_c) siginfo_t info; unsigned long signr; - spin_lock_irq(¤t_sig_lock); - signr = dequeue_signal(current, ¤t->blocked, &info); - spin_unlock_irq(¤t_sig_lock); + signr = dequeue_signal_lock(current, ¤t->blocked, &info); switch(signr) { case SIGSTOP: @@ -132,10 +138,7 @@ static int jffs2_garbage_collect_thread(void *_c) } } /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ - spin_lock_irq(¤t_sig_lock); - siginitsetinv (¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); - recalc_sigpending(); - spin_unlock_irq(¤t_sig_lock); + disallow_signal(SIGHUP); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { diff --git a/include/linux/sched.h b/include/linux/sched.h index a7a92f6a70da2..a15d95aa34b5c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -576,6 +576,19 @@ extern void proc_caches_init(void); extern void flush_signals(struct task_struct *); extern void flush_signal_handlers(struct task_struct *, int force_default); extern int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info); + +static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&tsk->sighand->siglock, flags); + ret = dequeue_signal(tsk, mask, info); + spin_unlock_irqrestore(&tsk->sighand->siglock, flags); + + return ret; +} + extern void block_all_signals(int (*notifier)(void *priv), void *priv, sigset_t *mask); extern void unblock_all_signals(void); @@ -673,6 +686,7 @@ extern NORET_TYPE void do_group_exit(int); extern void reparent_to_init(void); extern void daemonize(const char *, ...); extern int allow_signal(int); +extern int disallow_signal(int); extern task_t *child_reaper; extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *); diff --git a/kernel/exit.c b/kernel/exit.c index 08e2068e2ae5e..25b62f5971be6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -273,6 +273,13 @@ int allow_signal(int sig) spin_lock_irq(¤t->sighand->siglock); sigdelset(¤t->blocked, sig); + if (!current->mm) { + /* Kernel threads handle their own signals. + Let the signal code know it'll be handled, so + that they don't get converted to SIGKILL or + just silently dropped */ + current->sighand->action[(sig)-1].sa.sa_handler = (void *)2; + } recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); return 0; @@ -280,6 +287,20 @@ int allow_signal(int sig) EXPORT_SYMBOL(allow_signal); +int disallow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + spin_lock_irq(¤t->sighand->siglock); + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + return 0; +} + +EXPORT_SYMBOL(disallow_signal); + /* * Put all the gunge required to become a kernel thread without * attached user resources in one place where it belongs. -- 2.53.0