Merge branches 'doc.2021.01.06a', 'fixes.2021.01.04b', 'kfree_rcu.2021.01.04a', ...
[sfrench/cifs-2.6.git] / kernel / rcu / tasks.h
index 35bdcfd84d42827dc95cdc57e75994ed1fe483d9..af7c19439f4ec95623a9721fe8dcd27a35242158 100644 (file)
@@ -241,7 +241,7 @@ static int __noreturn rcu_tasks_kthread(void *arg)
        }
 }
 
-/* Spawn RCU-tasks grace-period kthread, e.g., at core_initcall() time. */
+/* Spawn RCU-tasks grace-period kthread. */
 static void __init rcu_spawn_tasks_kthread_generic(struct rcu_tasks *rtp)
 {
        struct task_struct *t;
@@ -564,7 +564,6 @@ static int __init rcu_spawn_tasks_kthread(void)
        rcu_spawn_tasks_kthread_generic(&rcu_tasks);
        return 0;
 }
-core_initcall(rcu_spawn_tasks_kthread);
 
 #if !defined(CONFIG_TINY_RCU)
 void show_rcu_tasks_classic_gp_kthread(void)
@@ -692,7 +691,6 @@ static int __init rcu_spawn_tasks_rude_kthread(void)
        rcu_spawn_tasks_kthread_generic(&rcu_tasks_rude);
        return 0;
 }
-core_initcall(rcu_spawn_tasks_rude_kthread);
 
 #if !defined(CONFIG_TINY_RCU)
 void show_rcu_tasks_rude_gp_kthread(void)
@@ -968,6 +966,11 @@ static void rcu_tasks_trace_pregp_step(void)
 static void rcu_tasks_trace_pertask(struct task_struct *t,
                                    struct list_head *hop)
 {
+       // During early boot when there is only the one boot CPU, there
+       // is no idle task for the other CPUs. Just return.
+       if (unlikely(t == NULL))
+               return;
+
        WRITE_ONCE(t->trc_reader_special.b.need_qs, false);
        WRITE_ONCE(t->trc_reader_checked, false);
        t->trc_ipi_to_cpu = -1;
@@ -1193,7 +1196,6 @@ static int __init rcu_spawn_tasks_trace_kthread(void)
        rcu_spawn_tasks_kthread_generic(&rcu_tasks_trace);
        return 0;
 }
-core_initcall(rcu_spawn_tasks_trace_kthread);
 
 #if !defined(CONFIG_TINY_RCU)
 void show_rcu_tasks_trace_gp_kthread(void)
@@ -1222,6 +1224,100 @@ void show_rcu_tasks_gp_kthreads(void)
 }
 #endif /* #ifndef CONFIG_TINY_RCU */
 
+#ifdef CONFIG_PROVE_RCU
+struct rcu_tasks_test_desc {
+       struct rcu_head rh;
+       const char *name;
+       bool notrun;
+};
+
+static struct rcu_tasks_test_desc tests[] = {
+       {
+               .name = "call_rcu_tasks()",
+               /* If not defined, the test is skipped. */
+               .notrun = !IS_ENABLED(CONFIG_TASKS_RCU),
+       },
+       {
+               .name = "call_rcu_tasks_rude()",
+               /* If not defined, the test is skipped. */
+               .notrun = !IS_ENABLED(CONFIG_TASKS_RUDE_RCU),
+       },
+       {
+               .name = "call_rcu_tasks_trace()",
+               /* If not defined, the test is skipped. */
+               .notrun = !IS_ENABLED(CONFIG_TASKS_TRACE_RCU)
+       }
+};
+
+static void test_rcu_tasks_callback(struct rcu_head *rhp)
+{
+       struct rcu_tasks_test_desc *rttd =
+               container_of(rhp, struct rcu_tasks_test_desc, rh);
+
+       pr_info("Callback from %s invoked.\n", rttd->name);
+
+       rttd->notrun = true;
+}
+
+static void rcu_tasks_initiate_self_tests(void)
+{
+       pr_info("Running RCU-tasks wait API self tests\n");
+#ifdef CONFIG_TASKS_RCU
+       synchronize_rcu_tasks();
+       call_rcu_tasks(&tests[0].rh, test_rcu_tasks_callback);
+#endif
+
+#ifdef CONFIG_TASKS_RUDE_RCU
+       synchronize_rcu_tasks_rude();
+       call_rcu_tasks_rude(&tests[1].rh, test_rcu_tasks_callback);
+#endif
+
+#ifdef CONFIG_TASKS_TRACE_RCU
+       synchronize_rcu_tasks_trace();
+       call_rcu_tasks_trace(&tests[2].rh, test_rcu_tasks_callback);
+#endif
+}
+
+static int rcu_tasks_verify_self_tests(void)
+{
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               if (!tests[i].notrun) {         // still hanging.
+                       pr_err("%s has been failed.\n", tests[i].name);
+                       ret = -1;
+               }
+       }
+
+       if (ret)
+               WARN_ON(1);
+
+       return ret;
+}
+late_initcall(rcu_tasks_verify_self_tests);
+#else /* #ifdef CONFIG_PROVE_RCU */
+static void rcu_tasks_initiate_self_tests(void) { }
+#endif /* #else #ifdef CONFIG_PROVE_RCU */
+
+void __init rcu_init_tasks_generic(void)
+{
+#ifdef CONFIG_TASKS_RCU
+       rcu_spawn_tasks_kthread();
+#endif
+
+#ifdef CONFIG_TASKS_RUDE_RCU
+       rcu_spawn_tasks_rude_kthread();
+#endif
+
+#ifdef CONFIG_TASKS_TRACE_RCU
+       rcu_spawn_tasks_trace_kthread();
+#endif
+
+       // Run the self-tests.
+       rcu_tasks_initiate_self_tests();
+}
+
 #else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
 static inline void rcu_tasks_bootup_oddness(void) {}
 void show_rcu_tasks_gp_kthreads(void) {}