dentry list checker

Bill Hawes (whawes@star.net)
Thu, 18 Dec 1997 16:14:59 -0500


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

I've written a simple dentry-list verifier to help track down the
dentry-trashing bugs people have reported. It runs every 2 seconds and
verifies that the unused dentry list hasn't been corrupted. The tests
aren't very comprehensive, but should suffice to catch the bugs people
have been reporting.

Please give this patch a try if you've had problems with dentry-related
oopses (or just want to help test things.) In the event one of the
messages is triggered, please record the activities immediately
preceding the message, and then try to reproduce the problem after
rebooting. My hope is that the patch will catch the damage close enough
to the cause that we can solve the mystery.

The patch doesn't seem to slow the machine down too much; I wrote it
several days ago and forgot that it was even running.

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

--- fs/dcache.c.old Mon Dec 1 11:14:13 1997
+++ fs/dcache.c Sun Dec 14 19:32:27 1997
@@ -411,6 +411,63 @@
}

/*
+ * 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;
+}
+
+/*
* This is called from do_try_to_free_page() to indicate
* that we should reduce the dcache and inode cache memory.
*/
@@ -424,6 +481,13 @@
*/
void check_dcache_memory()
{
+ static unsigned long next_check = 0;
+
+ if (jiffies > next_check) {
+ next_check = jiffies + 2*HZ;
+ verify_list();
+ }
+
if (dentry_stat.want_pages) {
unsigned int count, goal = 0;
/*
@@ -679,6 +743,8 @@
{
if (!dentry->d_inode)
printk(KERN_WARNING "VFS: moving negative dcache entry\n");
+ if (list_empty(&target->d_hash))
+ printk(KERN_WARNING "VFS: unhashed target\n");

/* Move the dentry to the target hash queue */
list_del(&dentry->d_hash);

--------------E9502BDB038D4DD4DE25E8A7--