dentry list checker patch

Bill Hawes (whawes@star.net)
Wed, 07 Jan 1998 13:25:50 -0500


This is a multi-part message in MIME format.
--------------0B4AC6A32F8F9699CC6100E4
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

The attached patch performs consistency checks on the dentry list at
periodic intervals in an attempt to catch an elusive bug causing dcache
corruption. So far only a couple of people have reported triggering the
messages, but no clear pattern of failures has emerged yet. My hope is
that if more people have the patch in place, we may be able to correlate
the failures and find the problem. (There were many oops reports related
to this dcache corruption problem in 2.1.6x, but for some reason they
have become much less frequent.)

I've added a compile flag so the patch can be easily disabled once we've
found the problem, or if someone doesn't want to run it. It doesn't slow
the system down very much anyway though.

I'd also like to ask for suggestions of a better way to track down stray
pointers (or whatever) that cause problems of this sort. Anyone have an
idea of how to protect a dynamic list like the dentry list? Or if we
detect corruption at address xxxx, any idea on how to find the pointer
that trashed it?

Regards,
Bill
--------------0B4AC6A32F8F9699CC6100E4
Content-Type: text/plain; charset=us-ascii; name="dcheck_78-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="dcheck_78-patch"

--- fs/dcache.c.old Tue Jan 6 11:39:05 1998
+++ fs/dcache.c Wed Jan 7 14:06:56 1998
@@ -19,6 +19,7 @@
#include <linux/malloc.h>
#include <linux/init.h>

+#define DCACHE_VERIFY 1
#define DCACHE_PARANOIA 1
/* #define DCACHE_DEBUG 1 */

@@ -412,6 +421,65 @@
prune_dcache(found);
}

+#ifdef DCACHE_VERIFY
+/*
+ * Check that the unused dentry list is valid.
+ */
+void verify_list(void)
+{
+ struct list_head *tmp, *next, *prev;
+ struct dentry *dentry;
+ struct inode *inode;
+ unsigned long memstart = PAGE_OFFSET;
+ unsigned long memmax = PAGE_OFFSET + (num_physpages << PAGE_SHIFT);
+ unsigned long align_mask = ~0x3;
+ unsigned long addr;
+
+ prev = &dentry_unused;
+ for (tmp = dentry_unused.next; tmp != &dentry_unused; tmp = next) {
+
+ addr = (unsigned long) tmp;
+ if (addr < memstart || addr > memmax)
+ goto bad;
+ if ((addr & align_mask) != addr)
+ goto bad;
+ if (prev != tmp->prev)
+ goto bad_prev;
+ next = tmp->next;
+ prev = tmp;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ inode = dentry->d_inode;
+ if (!inode)
+ continue;
+ addr = (unsigned long) inode;
+ if (addr < memstart || addr > memmax)
+ goto bad_inode;
+ if ((addr & align_mask) != addr)
+ goto bad_inode;
+ if (inode->i_sb != dentry->d_sb)
+ goto bad_sb;
+ if (!inode->i_count || inode->i_count > 50)
+ goto bad_count;
+ }
+ return;
+bad:
+ printk("VFS: dentry list corrupt, addr=%08lx\n", addr);
+ return;
+bad_prev:
+ printk("VFS: bad prev pointer in dentry list, prev=%p\n", prev);
+ return;
+bad_inode:
+ printk("VFS: dentry has bad inode pointer, addr=%08lx\n", addr);
+ return;
+bad_sb:
+ printk("VFS: dentry superblock mismatch\n");
+ return;
+bad_count:
+ printk("VFS: bad count for in-use inode, count=%d\n", inode->i_count);
+ return;
+}
+#endif
+
/*
* This is called from do_try_to_free_page() to indicate
* that we should reduce the dcache and inode cache memory.
@@ -426,6 +494,15 @@
*/
void check_dcache_memory()
{
+#ifdef DCACHE_VERIFY
+ static unsigned long next_check = 0;
+
+ if (jiffies > next_check) {
+ next_check = jiffies + 2*HZ;
+ verify_list();
+ }
+#endif
+
if (dentry_stat.want_pages) {
unsigned int count, goal = 0;
/*

--------------0B4AC6A32F8F9699CC6100E4--