From: yury Date: Mon, 25 Sep 2006 12:19:43 +0000 (+0000) Subject: - added online memory debug patch. It shows all abnormal memory allocation, free... X-Git-Tag: v1_8_0_110~486^2~836 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=3d0f892f86522c67571aaba7e1682ffd1d4799fc;p=fs%2Flustre-release.git - added online memory debug patch. It shows all abnormal memory allocation, free, double free, etc. in process and at the end of work, when modules unload. It is deactivated by default, to activate it one needs to add #define CONFIG_DEBUG_MEMORY 1 to config.h --- diff --git a/lustre/include/obd_support.h b/lustre/include/obd_support.h index 955c6cd..a424098 100644 --- a/lustre/include/obd_support.h +++ b/lustre/include/obd_support.h @@ -261,6 +261,109 @@ do { \ extern atomic_t libcfs_kmemory; +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) + +#define OBD_MT_WRONG_SIZE (1 << 0) +#define OBD_MT_LOC_LEN 128 + +struct obd_mem_track { + struct hlist_node mt_hash; + char mt_loc[OBD_MT_LOC_LEN]; + int mt_flags; + void *mt_ptr; + int mt_size; +}; + +void lvfs_memdbg_show(void); +void lvfs_memdbg_insert(struct obd_mem_track *mt); +void lvfs_memdbg_remove(struct obd_mem_track *mt); +struct obd_mem_track *lvfs_memdbg_find(void *ptr); + +int lvfs_memdbg_check_insert(struct obd_mem_track *mt); +struct obd_mem_track *lvfs_memdbg_check_remove(void *ptr); + +static inline struct obd_mem_track * +__new_mem_track(void *ptr, int size, + char *file, int line) +{ + struct obd_mem_track *mt; + + mt = kmalloc(sizeof(*mt), GFP_KERNEL); + if (!mt) + return NULL; + + snprintf(mt->mt_loc, sizeof(mt->mt_loc) - 1, + "%s:%d", file, line); + + mt->mt_size = size; + mt->mt_ptr = ptr; + mt->mt_flags = 0; + return mt; +} + +static inline void +__free_mem_track(struct obd_mem_track *mt) +{ + kfree(mt); +} + +static inline int +__get_mem_track(void *ptr, int size, + char *file, int line) +{ + struct obd_mem_track *mt; + + mt = __new_mem_track(ptr, size, file, line); + if (!mt) { + CWARN("Can't allocate new memory track\n"); + return 0; + } + + if (!lvfs_memdbg_check_insert(mt)) + __free_mem_track(mt); + + return 1; +} + +static inline int +__put_mem_track(void *ptr, int size, + char *file, int line) +{ + struct obd_mem_track *mt; + + if (!(mt = lvfs_memdbg_check_remove(ptr))) { + CWARN("Ptr 0x%p is not allocated. Attempt to free " + "not allocated memory at %s:%d\n", ptr, + file, line); + return 0; + } else { + if (mt->mt_size != size) { + mt->mt_flags |= OBD_MT_WRONG_SIZE; + CWARN("Freeing memory chunk (at 0x%p) of different size " + "than allocated (%d != %d) at %s:%d, allocated at %s\n", + ptr, mt->mt_size, size, file, line, mt->mt_loc); + } else { + __free_mem_track(mt); + } + return 1; + } +} + +#define get_mem_track(ptr, size, file, line) \ + __get_mem_track((ptr), (size), (file), (line)) + +#define put_mem_track(ptr, size, file, line) \ + __put_mem_track((ptr), (size), (file), (line)) + +#else /* !CONFIG_DEBUG_MEMORY */ + +#define get_mem_track(ptr, size, file, line) \ + do {} while (0) + +#define put_mem_track(ptr, size, file, line) \ + do {} while (0) +#endif /* !CONFIG_DEBUG_MEMORY */ + #if defined(LUSTRE_UTILS) /* this version is for utils only */ #define OBD_ALLOC_GFP(ptr, size, gfp_mask) \ do { \ @@ -288,6 +391,7 @@ do { \ atomic_add(size, &obd_memory); \ if (atomic_read(&obd_memory) > obd_memmax) \ obd_memmax = atomic_read(&obd_memory); \ + get_mem_track((ptr), (size), __FILE__, __LINE__); \ CDEBUG(D_MALLOC, "kmalloced '" #ptr "': %d at %p (tot %d)\n", \ (int)(size), ptr, atomic_read(&obd_memory)); \ } \ @@ -319,6 +423,7 @@ do { \ atomic_add(size, &obd_memory); \ if (atomic_read(&obd_memory) > obd_memmax) \ obd_memmax = atomic_read(&obd_memory); \ + get_mem_track((ptr), (size), __FILE__, __LINE__); \ CDEBUG(D_MALLOC, "vmalloced '" #ptr "': %d at %p (tot %d)\n", \ (int)(size), ptr, atomic_read(&obd_memory)); \ } \ @@ -342,6 +447,7 @@ do { \ #define OBD_FREE(ptr, size) \ do { \ LASSERT(ptr); \ + put_mem_track((ptr), (size), __FILE__, __LINE__); \ atomic_sub(size, &obd_memory); \ CDEBUG(D_MALLOC, "kfreed '" #ptr "': %d at %p (tot %d).\n", \ (int)(size), ptr, atomic_read(&obd_memory)); \ @@ -359,6 +465,7 @@ do { \ # define OBD_VFREE(ptr, size) \ do { \ LASSERT(ptr); \ + put_mem_track((ptr), (size), __FILE__, __LINE__); \ atomic_sub(size, &obd_memory); \ CDEBUG(D_MALLOC, "vfreed '" #ptr "': %d at %p (tot %d).\n", \ (int)(size), ptr, atomic_read(&obd_memory)); \ @@ -385,6 +492,7 @@ do { \ atomic_add(size, &obd_memory); \ if (atomic_read(&obd_memory) > obd_memmax) \ obd_memmax = atomic_read(&obd_memory); \ + get_mem_track((ptr), (size), __FILE__, __LINE__); \ CDEBUG(D_MALLOC, "slab-alloced '"#ptr"': %d at %p (tot %d)\n",\ (int)(size), ptr, atomic_read(&obd_memory)); \ } \ @@ -397,6 +505,7 @@ do { \ LASSERT(ptr); \ CDEBUG(D_MALLOC, "slab-freed '" #ptr "': %d at %p (tot %d).\n", \ (int)(size), ptr, atomic_read(&obd_memory)); \ + get_mem_track((ptr), (size), __FILE__, __LINE__); \ atomic_sub(size, &obd_memory); \ POISON(ptr, 0x5a, size); \ cfs_mem_cache_free(slab, ptr); \ diff --git a/lustre/lvfs/lvfs_linux.c b/lustre/lvfs/lvfs_linux.c index 8ae1790..fe1264f 100644 --- a/lustre/lvfs/lvfs_linux.c +++ b/lustre/lvfs/lvfs_linux.c @@ -435,6 +435,187 @@ EXPORT_SYMBOL(l_readdir); EXPORT_SYMBOL(obd_memory); EXPORT_SYMBOL(obd_memmax); +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) +static spinlock_t obd_memlist_lock = SPIN_LOCK_UNLOCKED; +static struct hlist_head *obd_memtable; +static unsigned long obd_memtable_size; + +static int lvfs_memdbg_init(int size) +{ + struct hlist_head *head; + int i; + + LASSERT(size > sizeof(sizeof(struct hlist_head))); + obd_memtable_size = size / sizeof(struct hlist_head); + + CWARN("Allocating %lu memdbg entries.\n", + (unsigned long)obd_memtable_size); + + obd_memtable = kmalloc(size, GFP_KERNEL); + if (!obd_memtable) + return -ENOMEM; + + i = obd_memtable_size; + head = obd_memtable; + do { + INIT_HLIST_HEAD(head); + head++; + i--; + } while(i); + + return 0; +} + +static int lvfs_memdbg_cleanup(void) +{ + struct hlist_node *node = NULL, *tmp = NULL; + struct hlist_head *head; + struct obd_mem_track *mt; + int i; + + spin_lock(&obd_memlist_lock); + for (i = 0, head = obd_memtable; i < obd_memtable_size; i++, head++) { + hlist_for_each_safe(node, tmp, head) { + mt = hlist_entry(node, struct obd_mem_track, mt_hash); + hlist_del_init(&mt->mt_hash); + kfree(mt); + } + } + spin_unlock(&obd_memlist_lock); + kfree(obd_memtable); + return 0; +} + +static inline unsigned long const hashfn(void *ptr) +{ + return (unsigned long)ptr & + (obd_memtable_size - 1); +} + +static void __lvfs_memdbg_insert(struct obd_mem_track *mt) +{ + struct hlist_head *head = obd_memtable + + hashfn(mt->mt_ptr); + hlist_add_head(&mt->mt_hash, head); +} + +void lvfs_memdbg_insert(struct obd_mem_track *mt) +{ + spin_lock(&obd_memlist_lock); + __lvfs_memdbg_insert(mt); + spin_unlock(&obd_memlist_lock); +} +EXPORT_SYMBOL(lvfs_memdbg_insert); + +static void __lvfs_memdbg_remove(struct obd_mem_track *mt) +{ + hlist_del_init(&mt->mt_hash); +} + +void lvfs_memdbg_remove(struct obd_mem_track *mt) +{ + spin_lock(&obd_memlist_lock); + __lvfs_memdbg_remove(mt); + spin_unlock(&obd_memlist_lock); +} +EXPORT_SYMBOL(lvfs_memdbg_remove); + +static struct obd_mem_track *__lvfs_memdbg_find(void *ptr) +{ + struct hlist_node *node = NULL; + struct obd_mem_track *mt = NULL; + struct hlist_head *head; + + head = obd_memtable + hashfn(ptr); + + hlist_for_each(node, head) { + mt = hlist_entry(node, struct obd_mem_track, mt_hash); + if ((unsigned long)mt->mt_ptr == (unsigned long)ptr) + break; + mt = NULL; + } + return mt; +} + +struct obd_mem_track *lvfs_memdbg_find(void *ptr) +{ + struct obd_mem_track *mt; + + spin_lock(&obd_memlist_lock); + mt = __lvfs_memdbg_find(ptr); + spin_unlock(&obd_memlist_lock); + + return mt; +} +EXPORT_SYMBOL(lvfs_memdbg_find); + +int lvfs_memdbg_check_insert(struct obd_mem_track *mt) +{ + spin_lock(&obd_memlist_lock); + if (!__lvfs_memdbg_find(mt->mt_ptr)) { + __lvfs_memdbg_insert(mt); + spin_unlock(&obd_memlist_lock); + return 1; + } + spin_unlock(&obd_memlist_lock); + return 0; +} +EXPORT_SYMBOL(lvfs_memdbg_check_insert); + +struct obd_mem_track * +lvfs_memdbg_check_remove(void *ptr) +{ + struct obd_mem_track *mt; + + spin_lock(&obd_memlist_lock); + mt = __lvfs_memdbg_find(ptr); + if (mt) { + __lvfs_memdbg_remove(mt); + spin_unlock(&obd_memlist_lock); + return mt; + } + spin_unlock(&obd_memlist_lock); + return NULL; +} +EXPORT_SYMBOL(lvfs_memdbg_check_remove); +#endif + +void lvfs_memdbg_show(void) +{ +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) + struct hlist_node *node = NULL; + struct hlist_head *head; + struct obd_mem_track *mt; +#endif + int leaked; + +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) + int i; +#endif + + leaked = atomic_read(&obd_memory); + + if (leaked > 0) { + CWARN("memory leaks detected (max %d, leaked %d)\n", + obd_memmax, leaked); + +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) + spin_lock(&obd_memlist_lock); + for (i = 0, head = obd_memtable; i < obd_memtable_size; i++, head++) { + hlist_for_each(node, head) { + mt = hlist_entry(node, struct obd_mem_track, mt_hash); + CWARN(" [%s] ptr: 0x%p, size: %d, src at \"%s\"\n", + ((mt->mt_flags & OBD_MT_WRONG_SIZE) ? + "wrong ck size" : "leaked memory"), + mt->mt_ptr, mt->mt_size, mt->mt_loc); + } + } + spin_unlock(&obd_memlist_lock); +#endif + } +} +EXPORT_SYMBOL(lvfs_memdbg_show); + #ifdef LUSTRE_KERNEL_VERSION #ifdef HAVE_OLD_DEV_SET_RDONLY void dev_set_rdonly(lvfs_sbdev_type dev, int no_write); @@ -506,20 +687,23 @@ EXPORT_SYMBOL(lvfs_check_io_health); static int __init lvfs_linux_init(void) { + ENTRY; +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) + lvfs_memdbg_init(PAGE_SIZE); +#endif RETURN(0); } static void __exit lvfs_linux_exit(void) { - int leaked; ENTRY; - leaked = atomic_read(&obd_memory); - CDEBUG(leaked ? D_ERROR : D_INFO, - "obd mem max: %d leaked: %d\n", obd_memmax, leaked); - + lvfs_memdbg_show(); + +#if defined (CONFIG_DEBUG_MEMORY) && defined(__KERNEL__) + lvfs_memdbg_cleanup(); +#endif EXIT; - return; } MODULE_AUTHOR("Cluster File Systems, Inc. "); diff --git a/lustre/tests/replay-single.sh b/lustre/tests/replay-single.sh index 0b7b82c..c91075f 100755 --- a/lustre/tests/replay-single.sh +++ b/lustre/tests/replay-single.sh @@ -93,7 +93,7 @@ test_0c() { fail mds # wait for recovery finish - sleep 20 + sleep 10 df $MOUNT # flush fld cache and dentry cache to make it lookup