]> git.karo-electronics.de Git - linux-beck.git/commitdiff
rcu: Make call_rcu_tasks() tolerate first call with irqs disabled
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Mon, 2 May 2016 18:58:56 +0000 (11:58 -0700)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Wed, 15 Jun 2016 22:45:00 +0000 (15:45 -0700)
Currently, if the very first call to call_rcu_tasks() has irqs disabled,
it will create the rcu_tasks_kthread with irqs disabled, which will
result in a splat in the memory allocator, which kthread_run() invokes
with the expectation that irqs are enabled.

This commit fixes this problem by deferring kthread creation if called
with irqs disabled.  The first call to call_rcu_tasks() that has irqs
enabled will create the kthread.

This bug was detected by rcutorture changes that were motivated by
Iftekhar Ahmed's mutation-testing efforts.

Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
include/linux/rcupdate.h
kernel/rcu/update.c

index a8af79738a0e7910966b19b37e3becd69f960156..3bc5de08c0b785e5755e05a611356ac8ea4ae21e 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/bug.h>
 #include <linux/compiler.h>
 #include <linux/ktime.h>
+#include <linux/irqflags.h>
 
 #include <asm/barrier.h>
 
index 3e888cd5a5941c43dd05f52209f7f5e884a9bf43..f0d8322bc3ec179f7a3cfa2f9bef7a0d8f8cb950 100644 (file)
@@ -528,6 +528,7 @@ static int rcu_task_stall_timeout __read_mostly = HZ * 60 * 10;
 module_param(rcu_task_stall_timeout, int, 0644);
 
 static void rcu_spawn_tasks_kthread(void);
+static struct task_struct *rcu_tasks_kthread_ptr;
 
 /*
  * Post an RCU-tasks callback.  First call must be from process context
@@ -537,6 +538,7 @@ void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func)
 {
        unsigned long flags;
        bool needwake;
+       bool havetask = READ_ONCE(rcu_tasks_kthread_ptr);
 
        rhp->next = NULL;
        rhp->func = func;
@@ -545,7 +547,9 @@ void call_rcu_tasks(struct rcu_head *rhp, rcu_callback_t func)
        *rcu_tasks_cbs_tail = rhp;
        rcu_tasks_cbs_tail = &rhp->next;
        raw_spin_unlock_irqrestore(&rcu_tasks_cbs_lock, flags);
-       if (needwake) {
+       /* We can't create the thread unless interrupts are enabled. */
+       if ((needwake && havetask) ||
+           (!havetask && !irqs_disabled_flags(flags))) {
                rcu_spawn_tasks_kthread();
                wake_up(&rcu_tasks_cbs_wq);
        }
@@ -790,7 +794,6 @@ static int __noreturn rcu_tasks_kthread(void *arg)
 static void rcu_spawn_tasks_kthread(void)
 {
        static DEFINE_MUTEX(rcu_tasks_kthread_mutex);
-       static struct task_struct *rcu_tasks_kthread_ptr;
        struct task_struct *t;
 
        if (READ_ONCE(rcu_tasks_kthread_ptr)) {