Messages in this thread Patch in this message | | | Date | Wed, 07 Jan 1998 13:25:50 -0500 | From | Bill Hawes <> | Subject | dentry list checker patch |
| |
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--- 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; /* | |