Re: fault in __d_lookup [Was: 2.6.25-mm1]

From: Matthew Wilcox
Date: Mon Apr 21 2008 - 13:23:33 EST


On Mon, Apr 21, 2008 at 11:59:51AM +0200, Jiri Slaby wrote:
> On 04/21/2008 11:45 AM, Al Viro wrote:
> >Well, if list has such turd in it, you'll certainly hit it every time
> >you walk that list, so 100% reproducible is not surprising.
> >
> >How well is it reproducible from fresh boot?
>
> Few days with suspend/resume cycles. This one was booted 12 hours ago, one
> suspend/resume. Will keep an eye on it and keep you informed.

Shall we see if we can catch it earlier? I have no idea if this will
help ... I haven't even booted it on a testmachine yet ;-) If I got
something wrong, it'll BUG() pretty early.

diff --git a/include/linux/list.h b/include/linux/list.h
index 75ce2cb..238ca1e 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -724,10 +724,17 @@ static inline int hlist_empty(const struct hlist_head *h)
return !h->first;
}

+#ifdef CONFIG_DEBUG_LIST
+extern void hlist_check(struct hlist_node *n);
+#else
+#define hlist_check(n) do { } while (0)
+#endif
+
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
+ hlist_check(n);
*pprev = next;
if (next)
next->pprev = pprev;
@@ -785,6 +792,7 @@ static inline void hlist_replace_rcu(struct hlist_node *old,
{
struct hlist_node *next = old->next;

+ hlist_check(old);
new->next = next;
new->pprev = old->pprev;
smp_wmb();
@@ -840,6 +848,7 @@ static inline void hlist_add_head_rcu(struct hlist_node *n,
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
+ hlist_check(next);
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
@@ -849,6 +858,7 @@ static inline void hlist_add_before(struct hlist_node *n,
static inline void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)
{
+ hlist_check(next);
next->next = n->next;
n->next = next;
next->pprev = &n->next;
@@ -878,6 +888,7 @@ static inline void hlist_add_after(struct hlist_node *n,
static inline void hlist_add_before_rcu(struct hlist_node *n,
struct hlist_node *next)
{
+ hlist_check(next);
n->pprev = next->pprev;
n->next = next;
smp_wmb();
@@ -906,6 +917,7 @@ static inline void hlist_add_before_rcu(struct hlist_node *n,
static inline void hlist_add_after_rcu(struct hlist_node *prev,
struct hlist_node *n)
{
+ hlist_check(prev);
n->next = prev->next;
n->pprev = &prev->next;
smp_wmb();
diff --git a/lib/list_debug.c b/lib/list_debug.c
index 4350ba9..00b56bf 100644
--- a/lib/list_debug.c
+++ b/lib/list_debug.c
@@ -1,5 +1,8 @@
/*
* Copyright 2006, Red Hat, Inc., Dave Jones
+ * Copyright 2008 Intel Corporation
+ * Author: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
+ *
* Released under the General Public License (GPL).
*
* This file contains the linked list implementations for
@@ -76,3 +79,18 @@ void list_del(struct list_head *entry)
entry->prev = LIST_POISON2;
}
EXPORT_SYMBOL(list_del);
+
+void hlist_check(struct hlist_node *n)
+{
+ if (unlikely(*n->pprev != n)) {
+ printk(KERN_ERR "hlist corruption. *pprev should be %p, "
+ "but was %p\n", n, *n->pprev);
+ BUG();
+ }
+ if (unlikely(n->next != NULL && n->next->pprev != &n->next)) {
+ printk(KERN_ERR "hlist corruption. n->next->pprev should be"
+ "%p, but was %p\n", &n->next, n->next->pprev);
+ BUG();
+ }
+}
+EXPORT_SYMBOL(hlist_check);

--
Intel are signing my paycheques ... these opinions are still mine
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours. We can't possibly take such
a retrograde step."
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/