Reproducing call_rcu_tasks self-test bug on a dual sockets Intel Xeon machine

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



1. Abstract
Previously, Matthew has reported a call_rcu_tasks bug [1]. But it is
hard to reproduce on a dual socket Intel machine. This report
developed a method to reproduce that bug on such a machine.

2. Introduction
Matthew has reported a call_rcu_tasks bug [1]. It is easy to reproduce
it on Thinkpad P1 gen4 (Intel i7-11800H) with following steps:
a) cp [2] to .config
b) build the kernel
c) execute  kvm -smp 4 -net none -serial file:/tmp/console.log -m 512
-kernel vmlinux -append "console=ttyS0"

The bug can even be reproduced on Thinkpad P1 gen4 (i7-11800H) by
stress testing the TASKS01 with the script [3].

However, the bug can't be reproduced by above methods on a dual socket
Intel Xeon machine (PowerEdge R720, Intel(R) Xeon(R) CPU E5-2660 0 @
2.20GHz, 2 CPUs, 16 cores, 32 threads).

3. Discover the reason behind the bug
On thinkpad P1 gen4, I tried to discover the reason behind the bug.
After some experimentation, I found that removing CONFIG_NFS_V4 from
the kernel config file will greatly increase the probabilities of
triggering the bug.

I guess maybe the init calls lend the grace period to RCU tasks self
tests, so I add some debug code to a test kernel to see what happens:
root@e0816662e3db:/mnt/linux# git diff
diff --git a/init/main.c b/init/main.c
index 98182c3c2c4b..9731566014cc 100644
--- a/init/main.c
+++ b/init/main.c
@@ -1356,6 +1356,8 @@ static int __init ignore_unknown_bootoption(char
*param, char *val,
        return 0;
 }

+extern bool *notrunp;
+unsigned int numofcalls = 0;
 static void __init do_initcall_level(int level, char *command_line)
 {
        initcall_entry_t *fn;
@@ -1367,8 +1369,19 @@ static void __init do_initcall_level(int level,
char *command_line)
                   NULL, ignore_unknown_bootoption);

        trace_initcall_level(initcall_level_names[level]);
-       for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
+       for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) {
+               bool before = *notrunp, after;
+               unsigned long jiffies_save = jiffies;
+               numofcalls++;
                do_one_initcall(initcall_from_entry(fn));
+               after = *notrunp;
+               if (before != after) {
+                 char sym[KSYM_SYMBOL_LEN];
+                 sprint_symbol(sym, (unsigned long )initcall_from_entry(fn));
+                 printk(KERN_INFO "init call level %d: %s changed the
rcu task test %lu\n",
+                        level, sym, jiffies - jiffies_save);
+               }
+       }
 }

 static void __init do_initcalls(void)
@@ -1376,15 +1389,17 @@ static void __init do_initcalls(void)
        int level;
        size_t len = strlen(saved_command_line) + 1;
        char *command_line;
-
+       unsigned long jiffies_save = jiffies;
        command_line = kzalloc(len, GFP_KERNEL);
        if (!command_line)
                panic("%s: Failed to allocate %zu bytes\n", __func__, len);
-
+
        for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {
                /* Parser modifies command_line, restore it each time */
                strcpy(command_line, saved_command_line);
                do_initcall_level(level, command_line);
+               if (level == 5)
+                 printk(KERN_INFO"init call level 5 costs: %lu
numofcalls %u\n", jiffies - jiffies_save, numofcalls);
        }

        kfree(command_line);
diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h
index 99cf3a13954c..f4e175eeaec3 100644
--- a/kernel/rcu/tasks.h
+++ b/kernel/rcu/tasks.h
@@ -1660,7 +1660,7 @@ static void test_rcu_tasks_callback(struct rcu_head *rhp)

        rttd->notrun = true;
 }
-
+bool *notrunp = &tests[0].notrun;
 static void rcu_tasks_initiate_self_tests(void)
 {
        pr_info("Running RCU-tasks wait API self tests\n");

The tests on Thinkpad P1 gen4 and Dell PowerEdge R720 show that during
the test, some init call will lend the grace period to the RCU tasks
self tests.

And of course, I am not using the above modification in Section 5 and
Section 6' tests. Section 5 and Section 6's tests are performed on
unmodified mainline!!

4. Reduce the init calls in kernel configuration
Based on above observation, I tried to reduce the kernel configuration
starting from [2] meanwhile keep the qemu bootable. Finally I got
[4].
And [4] will trigger the bug on Dell PowerEdge R720!

5. Trigger the bug in a more Linux way.
Why TASKS01 will not trigger the bug? There are two reasons:
a) there are too many init calls that will lend the grace period
b) the debug kernel param will lend the grace period

Solution to reason a:
I write a C++ program [5] to print the difference between the .config
generated by TASKS01 and [4]. Then I use the difference to create a
TASKS04.

Solution to reason b:
I add loglevel=7 to qemu boot parameter.

The result consists of newly created TASKS04 and TASKS04.boot [6].

Now the bug can trigger every time on Dell PowerEdge R720 by invoking:
./tools/testing/selftests/rcutorture/bin/kvm.sh --configs "TASKS04"
--duration 10 --trust-make

6. Conclusion
I am able to trigger the bug everytime in mainline without modifying
the kernel's functional code on a dual socket Intel Xeon machine.

7. Acknowledgement
Thanks Paul for your constant encouragement! Thanks Zhouyi

References
[1] https://lore.kernel.org/rcu/20220322142101.GQ4285@paulmck-ThinkPad-P17-Gen-1/T/#mb696465963393bbc3d38e85ffef0e03cf1e4fa54
[2] http://154.223.142.244/20220321/config-20220321
[3] http://154.223.142.244/20220418/test.sh.txt
[4] http://154.223.142.244/20220418/config
[5] http://154.223.142.244/20220418/processconfig.cpp
[6] http://154.223.142.244/20220418/0001-RCU.patch



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux