add info for debugging, this may be helpful for debugging concurrency.
Signed-off-by: Wang ShaoBo bobo.shaobowang@huawei.com --- include/linux/dwait.h | 6 +++++ kernel/sched/wait.c | 39 +++++++++++++++++++++++++++++ lib/list_debug.c | 57 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 include/linux/dwait.h
diff --git a/include/linux/dwait.h b/include/linux/dwait.h new file mode 100644 index 000000000000..edb95ce09b4d --- /dev/null +++ b/include/linux/dwait.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_DWAIT_H +#define _LINUX_DWAIT_H +int waitqueue_smp_nmi_call_function(void); +int waitqueue_smp_nmi_call_function_add(void); +#endif /* _LINUX_DWAIT_H */ diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c index a55642aa3f68..2977a8aa52ca 100644 --- a/kernel/sched/wait.c +++ b/kernel/sched/wait.c @@ -6,6 +6,45 @@ */ #include "sched.h"
+#include <asm/nmi.h> +#include <linux/dwait.h> + +struct list_head *dprev, *dnext, *dentry; + +static void run_nmi_waitqueue(void *info) +{ + pr_info("CPU:%d prev %llx next %llx entry %llx entryprev %llx entrynext %llx\n", smp_processor_id(), dprev, dnext, dentry, dentry->prev, dentry->next); + if (dnext != LIST_POISON1 && dnext != NULL) + pr_info("nextprev %llx nextnext %llx\n", dnext->prev, dnext->next); + if (dprev != LIST_POISON2 && dprev != NULL) + pr_info("prevnext %llx prevprev %llx\n", dprev->next, dprev->prev); +} + +static void run_nmi_waitqueue_add(void *info) +{ + pr_info("CPU:%d new %llx prev %llx next %llx \n", smp_processor_id(), dentry, dprev, dnext); + if (dnext != LIST_POISON1 && dnext != LIST_POISON2 && dnext != NULL) + pr_info("nextprev %llx nextnext %llx\n", dnext->prev, dnext->next); + if (dprev != LIST_POISON2 && dprev != LIST_POISON2 && dprev != NULL) + pr_info("prevnext %llx prevprev %llx\n", dprev->next, dprev->prev); + if (dentry != LIST_POISON1 && dentry != LIST_POISON2 && dentry != NULL) + pr_info("newnext %llx newprev %llx\n", dentry->next, dentry->prev); +} + +int waitqueue_smp_nmi_call_function_add(void) +{ + smp_call_function_many(cpu_online_mask, run_nmi_waitqueue_add, NULL, 1); + + return 0; +} + +int waitqueue_smp_nmi_call_function(void) +{ + smp_call_function_many(cpu_online_mask, run_nmi_waitqueue, NULL, 1); + + return 0; +} + void __init_waitqueue_head(struct wait_queue_head *wq_head, const char *name, struct lock_class_key *key) { spin_lock_init(&wq_head->lock); diff --git a/lib/list_debug.c b/lib/list_debug.c index 5d5424b51b74..cb2b680791ff 100644 --- a/lib/list_debug.c +++ b/lib/list_debug.c @@ -11,15 +11,44 @@ #include <linux/kernel.h> #include <linux/rculist.h>
+#include <linux/delay.h> + /* * Check that the data structures for the list manipulations are reasonably * valid. Failures here indicate memory corruption (and possibly an exploit * attempt). */
+#include <linux/dwait.h> + +#define VALADDR 0xffff000000000000 +#define IS_VALADDR(x) ((((u64)x) & VALADDR) == VALADDR) + +extern struct list_head *dprev, *dnext, *dentry; bool __list_add_valid(struct list_head *new, struct list_head *prev, struct list_head *next) { + int ret = true; + + if (prev == NULL || next == NULL ||next->prev != prev ||prev->next != next ||(new == prev || new == next) || !IS_VALADDR(prev) || !IS_VALADDR(next)) + ret = false; + + if (ret == false) { + pr_info("CPU:%d new %llx prev %llx next %llx \n", smp_processor_id(), new, prev, next); + if (next != LIST_POISON1 && next != LIST_POISON2 && next != NULL && IS_VALADDR(next)) + pr_info("nextprev %llx nextnext %llx\n", next->prev, next->next); + if (prev != LIST_POISON2 && prev != LIST_POISON2 && prev != NULL && IS_VALADDR(prev)) + pr_info("prevnext %llx prevprev %llx\n", prev->next, prev->prev); + if (new != LIST_POISON1 && new != LIST_POISON2 && new != NULL && IS_VALADDR(new)) + pr_info("newnext %llx newprev %llx\n", new->next, new->prev); + dprev = prev; + dnext = next; + dentry = new; + wmb(); + waitqueue_smp_nmi_call_function_add(); + mdelay(1000); + } + if (CHECK_DATA_CORRUPTION(next->prev != prev, "list_add corruption. next->prev should be prev (%px), but was %px. (next=%px).\n", prev, next->prev, next) || @@ -29,19 +58,36 @@ bool __list_add_valid(struct list_head *new, struct list_head *prev, CHECK_DATA_CORRUPTION(new == prev || new == next, "list_add double add: new=%px, prev=%px, next=%px.\n", new, prev, next)) - return false; + ret = false;
- return true; + return ret; } EXPORT_SYMBOL(__list_add_valid); - bool __list_del_entry_valid(struct list_head *entry) { + int ret = true; struct list_head *prev, *next;
prev = entry->prev; next = entry->next;
+ if (next == LIST_POISON1 || prev == LIST_POISON2|| prev == NULL || next == NULL || prev->next != entry||next->prev != entry || !IS_VALADDR(prev) || !IS_VALADDR(next)) + ret = false; + + if (ret == false) { + pr_info("CPU:%d prev %llx next %llx entry %llx entryprev %llx entrynext %llx\n", smp_processor_id(), prev, next, entry, entry->prev, entry->next); + if (next != LIST_POISON1 && next != NULL && IS_VALADDR(next)) + pr_info("nextprev %llx nextnext %llx\n", next->prev, next->next); + if (prev != LIST_POISON2 && prev != NULL && IS_VALADDR(prev)) + pr_info("prevnext %llx prevprev %llx\n", prev->next, prev->prev); + dprev = prev; + dnext = next; + dentry = entry; + wmb(); + waitqueue_smp_nmi_call_function(); + mdelay(1000); + } + if (CHECK_DATA_CORRUPTION(next == LIST_POISON1, "list_del corruption, %px->next is LIST_POISON1 (%px)\n", entry, LIST_POISON1) || @@ -54,9 +100,10 @@ bool __list_del_entry_valid(struct list_head *entry) CHECK_DATA_CORRUPTION(next->prev != entry, "list_del corruption. next->prev should be %px, but was %px\n", entry, next->prev)) - return false; + ret = false; +
- return true; + return ret;
} EXPORT_SYMBOL(__list_del_entry_valid);