From patchwork Tue Feb 28 09:04:23 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Antipov X-Patchwork-Id: 6965 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id A6AF82447B for ; Tue, 28 Feb 2012 09:03:11 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 4622FA187B5 for ; Tue, 28 Feb 2012 09:03:11 +0000 (UTC) Received: by iage36 with SMTP id e36so908942iag.11 for ; Tue, 28 Feb 2012 01:03:10 -0800 (PST) Received: from mr.google.com ([10.50.89.201]) by 10.50.89.201 with SMTP id bq9mr1753747igb.55.1330419790440 (num_hops = 1); Tue, 28 Feb 2012 01:03:10 -0800 (PST) Received: by 10.50.89.201 with SMTP id bq9mr1488201igb.55.1330419790385; Tue, 28 Feb 2012 01:03:10 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.11.10 with SMTP id r10csp3757ibr; Tue, 28 Feb 2012 01:03:09 -0800 (PST) Received: by 10.204.156.129 with SMTP id x1mr7141694bkw.72.1330419788546; Tue, 28 Feb 2012 01:03:08 -0800 (PST) Received: from mail-bk0-f50.google.com (mail-bk0-f50.google.com [209.85.214.50]) by mx.google.com with ESMTPS id w9si9647953bkd.74.2012.02.28.01.03.08 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 28 Feb 2012 01:03:08 -0800 (PST) Received-SPF: neutral (google.com: 209.85.214.50 is neither permitted nor denied by best guess record for domain of dmitry.antipov@linaro.org) client-ip=209.85.214.50; Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.214.50 is neither permitted nor denied by best guess record for domain of dmitry.antipov@linaro.org) smtp.mail=dmitry.antipov@linaro.org Received: by bkuw11 with SMTP id w11so2029448bku.37 for ; Tue, 28 Feb 2012 01:03:07 -0800 (PST) Received-SPF: pass (google.com: domain of dmitry.antipov@linaro.org designates 10.204.131.90 as permitted sender) client-ip=10.204.131.90; Received: from mr.google.com ([10.204.131.90]) by 10.204.131.90 with SMTP id w26mr8645522bks.55.1330419787991 (num_hops = 1); Tue, 28 Feb 2012 01:03:07 -0800 (PST) MIME-Version: 1.0 Received: by 10.204.131.90 with SMTP id w26mr6962117bks.55.1330419787801; Tue, 28 Feb 2012 01:03:07 -0800 (PST) Received: from localhost.localdomain ([78.153.153.8]) by mx.google.com with ESMTPS id d5sm29676796bkb.3.2012.02.28.01.03.06 (version=SSLv3 cipher=OTHER); Tue, 28 Feb 2012 01:03:07 -0800 (PST) From: Dmitry Antipov To: Rusty Russell Cc: Andrew Morton , linux-kernel@vger.kernel.org, linaro-dev@lists.linaro.org, patches@linaro.org, Dmitry Antipov Subject: [RFC PATCH] module: debugging check for runaway kthreads Date: Tue, 28 Feb 2012 13:04:23 +0400 Message-Id: <1330419863-3508-1-git-send-email-dmitry.antipov@linaro.org> X-Mailer: git-send-email 1.7.7.6 X-Gm-Message-State: ALoCoQlAxPJNu4927pcYnNitr8m+otcYyeZYILYfnJEStg5RRCwDef7J3HM2i1TBjJNzDFf+jHMr Debugging option CONFIG_MODULE_KTHREAD_CHECK provides a way to check whether all kernel threads created by the module and have used module code as a thread worker function are really exited when the module is unloaded. The following pseudo-code contains example of an error which is likely to be catched with this debugging check: static struct task_struct *tsk; static DECLARE_COMPLETION(done); static void *func(void *unused) { while (!kthread_should_stop()) real_work(); complete(&done); } static int __init modinit(void) { tsk = kthread_run(func, NULL, "func"); return IS_ERR(tsk) ? PTR_ERR(tsk) : 0; } static void __exit modexit(void) { wait_for_completion(&done); } Signed-off-by: Dmitry Antipov --- include/linux/kthread.h | 5 +++++ init/Kconfig | 9 +++++++++ kernel/kthread.c | 24 ++++++++++++++++++++++++ kernel/module.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 0 deletions(-) diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 0714b24..33897c3 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -13,6 +13,11 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), #define kthread_create(threadfn, data, namefmt, arg...) \ kthread_create_on_node(threadfn, data, -1, namefmt, ##arg) +#ifdef CONFIG_MODULE_KTHREAD_CHECK +unsigned long get_kthread_func(struct task_struct *tsk); +#else +#define get_kthread_func(tsk, addr, mod) (0) +#endif /** * kthread_run - create and wake a thread. diff --git a/init/Kconfig b/init/Kconfig index 3f42cd6..fa7c6e0 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1397,6 +1397,15 @@ config MODULE_FORCE_UNLOAD rmmod). This is mainly for kernel developers and desperate users. If unsure, say N. +config MODULE_KTHREAD_CHECK + bool "Check for runaway kernel threads at module unload" + depends on MODULE_UNLOAD && EXPERIMENTAL && DEBUG_KERNEL + help + This option allows you to check whether all kernel threads created + by the module and have used module code as a thread worker function + are really exited when the module is unloaded. This is mainly for + module developers. If insure, say N. + config MODVERSIONS bool "Module versioning support" help diff --git a/kernel/kthread.c b/kernel/kthread.c index 3d3de63..5c53817 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -38,6 +38,9 @@ struct kthread_create_info struct kthread { int should_stop; +#ifdef CONFIG_MODULE_KTHREAD_CHECK + void *fn; +#endif void *data; struct completion exited; }; @@ -45,6 +48,24 @@ struct kthread { #define to_kthread(tsk) \ container_of((tsk)->vfork_done, struct kthread, exited) +#ifdef CONFIG_MODULE_KTHREAD_CHECK + +unsigned long get_kthread_func(struct task_struct *tsk) +{ + struct kthread *kt; + unsigned long addr; + + get_task_struct(tsk); + BUG_ON(!(tsk->flags & PF_KTHREAD)); + kt = to_kthread(tsk); + barrier(); + addr = tsk->vfork_done ? (unsigned long)kt->fn : 0UL; + put_task_struct(tsk); + return addr; +} + +#endif /* CONFIG_MODULE_KTHREAD_CHECK */ + /** * kthread_should_stop - should this kthread return now? * @@ -106,6 +127,9 @@ static int kthread(void *_create) int ret; self.should_stop = 0; +#ifdef CONFIG_MODULE_KTHREAD_CHECK + self.fn = threadfn; +#endif self.data = data; init_completion(&self.exited); current->vfork_done = &self.exited; diff --git a/kernel/module.c b/kernel/module.c index 2c93276..fe6637b 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -764,6 +765,49 @@ static void wait_for_zero_refcount(struct module *mod) mutex_lock(&module_mutex); } +#ifdef CONFIG_KALLSYMS +static const char *get_ksymbol(struct module *mod, unsigned long addr, + unsigned long *size, unsigned long *offset); +#else +#define get_ksymbol(mod, addr, size, offset) NULL +#endif + +#ifdef CONFIG_MODULE_KTHREAD_CHECK + +static void check_kthreads(struct module *mod) +{ + unsigned long flags; + struct task_struct *g, *p; + + read_lock_irqsave(&tasklist_lock, flags); + do_each_thread(g, p) { + const char *name; + unsigned long addr, offset, size; + + /* Note kthreadd is special. Other kthreads should + have their 'struct kthread' on the stack until + do_exit() calls schedule() for the last time. */ + if (p->mm || p == kthreadd_task) + continue; + + addr = get_kthread_func(p); + if (__module_text_address(addr) == mod) { + name = get_ksymbol(mod, addr, &size, &offset); + printk(KERN_WARNING "kthread %p[%s:%d] running " + "0x%lx(%s) is still alive, fix module %s, " + "crash possible\n", p, p->comm, p->pid, addr, + (name ? name : ""), mod->name); + } + } while_each_thread(g, p); + read_unlock_irqrestore(&tasklist_lock, flags); +} + +#else + +#define check_kthreads(mod) do { } while (0) + +#endif /* CONFIG_MODULE_KTHREAD_CHECK */ + SYSCALL_DEFINE2(delete_module, const char __user *, name_user, unsigned int, flags) { @@ -831,6 +875,7 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod); async_synchronize_full(); + check_kthreads(mod); /* Store the name of the last unloaded module for diagnostic purposes */ strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));