lkml.org 
[lkml]   [1997]   [Dec]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: Linux-2.0.33 out there..
On Thu, 18 Dec 1997, Alan Cox wrote:

> > Thanks for the new kernel release.
> >
> > > - routing memory leak fix
> >
> > It seems that linux-2.0.33 is crushed by mrouted-3.9-beta.
> > Does linux-2.0.33 still have the memory leak problem by multicast routing ?
>
> It may do. If so Ingo Molnar now has a very very good memory leak detector
> which will help no end.
>
> Alan
>
>

Speaking of which..

Hi Alan, hackers,

I've been asked to maintain the package. I'm attaching the 2.1.72.eval
version in hopes that someone(s) will critique it. If this cut is 'ok'
I'll backport to 2.1.33 and try to find a home for the package.

[how big is just too big to drop to the list?]

I'm particularly interested (read concerned) in comments concerning
ptable.h changes. I haven't cycle counted them, but they don't seeem
to be expensive (Bonnie/lmbench/and pgcc -O9 -fno-inline-functions for
no good reason other than to see if it would go KAABOOPS :).

Fretting Bullets,

-mike
diff -urN linux-2.1.72-virgin/arch/i386/config.in linux-2.1.72-memleak/arch/i386/config.in
--- linux-2.1.72-virgin/arch/i386/config.in Sat Dec 6 04:49:04 1997
+++ linux-2.1.72-memleak/arch/i386/config.in Sat Dec 6 05:55:12 1997
@@ -139,4 +139,5 @@
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
fi
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+bool 'Kernel memory leak detection support' CONFIG_MEMLEAK n
endmenu
diff -urN linux-2.1.72-virgin/arch/i386/kernel/traps.c linux-2.1.72-memleak/arch/i386/kernel/traps.c
--- linux-2.1.72-virgin/arch/i386/kernel/traps.c Tue Dec 2 07:16:14 1997
+++ linux-2.1.72-memleak/arch/i386/kernel/traps.c Sun Dec 14 03:56:24 1997
@@ -115,7 +115,9 @@
* segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
* a guess of how much space is likely to be vmalloced.
*/
+#ifndef CONFIG_MEMLEAK
#define VMALLOC_OFFSET (8*1024*1024)
+#endif
#define MODULE_RANGE (8*1024*1024)

static void show_registers(struct pt_regs *regs)
diff -urN linux-2.1.72-virgin/arch/i386/mm/init.c linux-2.1.72-memleak/arch/i386/mm/init.c
--- linux-2.1.72-virgin/arch/i386/mm/init.c Thu Oct 23 11:37:47 1997
+++ linux-2.1.72-memleak/arch/i386/mm/init.c Sat Dec 6 05:52:32 1997
@@ -367,7 +367,11 @@
if (!initrd_start || (tmp < initrd_start || tmp >=
initrd_end))
#endif
+#ifndef CONFIG_MEMLEAK
free_page(tmp);
+#else
+ real_free_pages(tmp,0);
+#endif
}
printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n",
(unsigned long) nr_free_pages << (PAGE_SHIFT-10),
@@ -389,7 +393,11 @@
for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved);
atomic_set(&mem_map[MAP_NR(addr)].count, 1);
+#ifndef CONFIG_MEMLEAK
free_page(addr);
+#else
+ real_free_pages(addr,0);
+#endif
}
printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
}
diff -urN linux-2.1.72-virgin/fs/proc/root.c linux-2.1.72-memleak/fs/proc/root.c
--- linux-2.1.72-virgin/fs/proc/root.c Thu Oct 23 11:38:51 1997
+++ linux-2.1.72-memleak/fs/proc/root.c Sat Dec 6 05:56:13 1997
@@ -409,6 +409,14 @@
NULL /* permission */
};

+#ifdef CONFIG_MEMLEAK
+static struct proc_dir_entry proc_root_memleak = {
+ PROC_MEMLEAK, 7, "memleak",
+ S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
+ 0, &proc_memleak_inode_operations
+};
+#endif
+
static struct proc_dir_entry proc_root_loadavg = {
PROC_LOADAVG, 7, "loadavg",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -645,6 +653,10 @@
proc_register(&proc_root, &proc_root_hardware);
#endif
proc_register(&proc_root, &proc_root_slab);
+
+#ifdef CONFIG_MEMLEAK
+ proc_register(&proc_root, &proc_root_memleak);
+#endif

if (prof_shift) {
proc_register(&proc_root, &proc_root_profile);
diff -urN linux-2.1.72-virgin/include/asm-i386/pgtable.h linux-2.1.72-memleak/include/asm-i386/pgtable.h
--- linux-2.1.72-virgin/include/asm-i386/pgtable.h Sun Nov 30 04:55:39 1997
+++ linux-2.1.72-memleak/include/asm-i386/pgtable.h Thu Dec 18 07:53:28 1997
@@ -188,7 +188,11 @@
* The vmalloc() routines leaves a hole of 4kB between each vmalloced
* area for the same reason. ;)
*/
+#ifndef CONFIG_MEMLEAK
#define VMALLOC_OFFSET (8*1024*1024)
+#else
+#define VMALLOC_OFFSET (128*1024*1024)
+#endif
#define VMALLOC_START (((unsigned long) high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_VMADDR(x) ((unsigned long)(x))

@@ -379,6 +383,7 @@

extern const char bad_pmd_string[];

+#ifndef CONFIG_MEMLEAK
extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
{
address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
@@ -401,6 +406,33 @@
}
return (pte_t *) pmd_page(*pmd) + address;
}
+#else
+#define pte_alloc_kernel(pmd, address) \
+({ \
+ pte_t *ret; \
+ unsigned long ADDRESS = ((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); \
+ if (pmd_none(*(pmd))) { \
+ pte_t * page = (pte_t *) get_free_page(GFP_KERNEL); \
+ if (pmd_none(*(pmd))) { \
+ if (page) { \
+ pmd_val(*(pmd)) = _KERNPG_TABLE + __pa(page); \
+ ret = page + ADDRESS; goto out; \
+ } \
+ pmd_val(*(pmd)) = _KERNPG_TABLE + __pa(BAD_PAGETABLE); \
+ ret = NULL; goto out; \
+ } \
+ free_page((unsigned long) page); \
+ } \
+ if (pmd_bad(*(pmd))) { \
+ printk(bad_pmd_string, pmd_val(*(pmd))); \
+ pmd_val(*(pmd)) = _KERNPG_TABLE + __pa(BAD_PAGETABLE); \
+ ret = NULL; goto out; \
+ } \
+ ret = (pte_t *) pmd_page(*(pmd)) + ADDRESS; \
+out: \
+ ret; \
+})
+#endif

/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
@@ -421,6 +453,7 @@
free_page((unsigned long) pte);
}

+#ifndef CONFIG_MEMLEAK
extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
{
address = (address >> (PAGE_SHIFT-2)) & 4*(PTRS_PER_PTE - 1);
@@ -453,6 +486,43 @@
pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE);
return NULL;
}
+#else
+#define pte_alloc(pmd, address) \
+({ \
+ pte_t *ret; \
+ unsigned long ADDRESS = ((address) >> (PAGE_SHIFT-2)) & 4*(PTRS_PER_PTE - 1); \
+ \
+repeat: \
+ if (pmd_none(*(pmd))) \
+ goto getnew; \
+ if (pmd_bad(*(pmd))) \
+ goto fix; \
+ ret = (pte_t *) (pmd_page(*(pmd)) + ADDRESS); goto out; \
+ \
+getnew: \
+{ \
+ unsigned long page = __get_free_page(GFP_KERNEL); \
+ if (!pmd_none(*(pmd))) \
+ goto freenew; \
+ if (!page) \
+ goto oom; \
+ memset((void *) page, 0, PAGE_SIZE); \
+ pmd_val(*(pmd)) = _PAGE_TABLE + __pa(page); \
+ ret = (pte_t *) (page + ADDRESS); goto out; \
+freenew: \
+ free_page(page); \
+ goto repeat; \
+} \
+ \
+fix: \
+ printk(bad_pmd_string, pmd_val(*(pmd))); \
+oom: \
+ pmd_val(*(pmd)) = _PAGE_TABLE + __pa(BAD_PAGETABLE); \
+ ret = NULL; \
+out: \
+ ret; \
+})
+#endif

/*
* allocating and freeing a pmd is trivial: the 1-entry pmd is
@@ -473,10 +543,17 @@
free_page((unsigned long) pgd);
}

+#ifndef CONFIG_MEMLEAK
extern inline pgd_t * pgd_alloc(void)
{
return (pgd_t *) get_free_page(GFP_KERNEL);
}
+#else
+#define pgd_alloc() \
+((pgd_t *)({ \
+ get_free_page(GFP_KERNEL); \
+}))
+#endif

extern pgd_t swapper_pg_dir[1024];

diff -urN linux-2.1.72-virgin/include/linux/mm.h linux-2.1.72-memleak/include/linux/mm.h
--- linux-2.1.72-virgin/include/linux/mm.h Thu Oct 23 11:39:07 1997
+++ linux-2.1.72-memleak/include/linux/mm.h Mon Dec 15 03:22:00 1997
@@ -234,6 +234,19 @@

extern mem_map_t * mem_map;

+#ifdef CONFIG_MEMLEAK
+/*
+ * 'allocation identifier' for memleak detection
+ */
+struct alloc_struct {
+ int id;
+ char *file;
+ int line;
+};
+
+extern unsigned long memleak_init (unsigned long, unsigned long);
+#endif
+
/*
* This is timing-critical - most of the time in getting a new page
* goes to clearing the page. If you want a page without the clearing
@@ -241,6 +254,7 @@
*/
#define __get_free_page(priority) __get_free_pages((priority),0,0)
#define __get_dma_pages(priority, order) __get_free_pages((priority),(order),1)
+#ifndef CONFIG_MEMLEAK
extern unsigned long FASTCALL(__get_free_pages(int priority, unsigned long gfporder, int dma));

extern inline unsigned long get_free_page(int priority)
@@ -252,12 +266,43 @@
clear_page(page);
return page;
}
+#else
+extern unsigned long FASTCALL(__get_free_pages_wrapper(int priority,
+ unsigned long gfporder, int dma, struct alloc_struct * id));
+extern unsigned long FASTCALL(real__get_free_pages(int priority,
+ unsigned long gfporder, int dma));
+#define __get_free_pages(priority,gfporder,dma) \
+({ \
+ static struct alloc_struct ID = { 0, __FILE__, __LINE__ }; \
+ __get_free_pages_wrapper(priority,gfporder,dma,&ID); \
+})
+
+#define get_free_page(priority) \
+({ \
+ unsigned long page; \
+ \
+ page = __get_free_page(priority); \
+ if (page) \
+ memset((void *) page, 0, PAGE_SIZE); \
+ page; \
+})
+#endif /* CONFIG_MEMLEAK */

/* memory.c & swap.c*/

#define free_page(addr) free_pages((addr),0)
+#ifndef CONFIG_MEMLEAK
extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
extern void FASTCALL(__free_page(struct page *));
+#else
+extern void FASTCALL(free_pages_wrapper(unsigned long addr, unsigned long order));
+extern void FASTCALL(real_free_pages(unsigned long addr, unsigned long order));
+#define free_pages(addr,order) free_pages_wrapper(addr,order)
+
+extern void __free_page_wrapper(struct page *);
+extern void real__free_page(struct page *);
+#define __free_page(page) __free_page_wrapper(page)
+#endif

extern void show_free_areas(void);
extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
diff -urN linux-2.1.72-virgin/include/linux/proc_fs.h linux-2.1.72-memleak/include/linux/proc_fs.h
--- linux-2.1.72-virgin/include/linux/proc_fs.h Mon Dec 1 04:27:11 1997
+++ linux-2.1.72-memleak/include/linux/proc_fs.h Mon Dec 15 03:22:00 1997
@@ -52,7 +52,10 @@
PROC_SLABINFO,
PROC_PARPORT,
PROC_OMIRR, /* whether enabled or not */
- PROC_PPC_HTAB
+ PROC_PPC_HTAB,
+#ifdef CONFIG_MEMLEAK
+ PROC_MEMLEAK,
+#endif
};

enum pid_directory_inos {
@@ -367,7 +370,8 @@
#endif
extern struct inode_operations proc_omirr_inode_operations;
extern struct inode_operations proc_ppc_htab_inode_operations;
-
+#ifdef CONFIG_MEMLEAK
+extern struct inode_operations proc_memleak_inode_operations;
#endif

/*
@@ -388,3 +392,5 @@
* proc_devtree.c
*/
extern void proc_device_tree_init(void);
+
+#endif /* _LINUX_PROC_FS_H */
diff -urN linux-2.1.72-virgin/include/linux/skbuff.h linux-2.1.72-memleak/include/linux/skbuff.h
--- linux-2.1.72-virgin/include/linux/skbuff.h Mon Dec 1 04:27:12 1997
+++ linux-2.1.72-memleak/include/linux/skbuff.h Thu Dec 18 09:37:59 1997
@@ -150,11 +150,34 @@
extern void skb_unlink(struct sk_buff *buf);
extern __u32 skb_queue_len(struct sk_buff_head *list);
extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list);
+extern void kfree_skbmem(struct sk_buff *skb);
+
+#ifndef CONFIG_MEMLEAK
extern struct sk_buff * alloc_skb(unsigned int size, int priority);
extern struct sk_buff * dev_alloc_skb(unsigned int size);
-extern void kfree_skbmem(struct sk_buff *skb);
extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority);
extern struct sk_buff * skb_copy(struct sk_buff *skb, int priority);
+#else /* CONFIG_MEMLEAK */
+
+#include <linux/mm.h>
+
+extern struct sk_buff * alloc_skb_wrap(unsigned int size, int priority, struct alloc_struct * id);
+extern struct sk_buff * dev_alloc_skb_wrap(unsigned int size, struct alloc_struct * id);
+extern struct sk_buff * skb_clone_wrap(struct sk_buff *skb, int priority, struct alloc_struct * id);
+extern struct sk_buff * skb_copy_wrap(struct sk_buff *skb, int priority, struct alloc_struct * id);
+
+#define WRAP(x,y...) \
+({ \
+ static struct alloc_struct ID = { 0, __FILE__, __LINE__ }; \
+ x##_wrap(y,&ID); \
+})
+
+#define alloc_skb(size,priority) WRAP(alloc_skb,size,priority)
+#define dev_alloc_skb(size) WRAP(dev_alloc_skb,size)
+#define skb_clone(skb,priority) WRAP(skb_clone,skb,priority)
+#define skb_copy(skb,priority) WRAP(skb_copy,skb,priority)
+#endif /* CONFIG_MEMLEAK */
+
extern struct sk_buff * skb_realloc_headroom(struct sk_buff *skb, int newheadroom);
#define dev_kfree_skb(a, b) kfree_skb((a), (b))
extern unsigned char * skb_put(struct sk_buff *skb, unsigned int len);
diff -urN linux-2.1.72-virgin/include/linux/slab.h linux-2.1.72-memleak/include/linux/slab.h
--- linux-2.1.72-virgin/include/linux/slab.h Mon Aug 11 23:17:22 1997
+++ linux-2.1.72-memleak/include/linux/slab.h Mon Dec 15 03:22:00 1997
@@ -48,6 +48,7 @@
extern long kmem_cache_init(long, long);
extern void kmem_cache_sizes_init(void);
extern kmem_cache_t *kmem_find_general_cachep(size_t);
+#ifndef CONFIG_MEMLEAK
extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long,
void (*)(void *, kmem_cache_t *, unsigned long),
void (*)(void *, kmem_cache_t *, unsigned long));
@@ -58,6 +59,60 @@
extern void *kmalloc(size_t, int);
extern void kfree(const void *);
extern void kfree_s(const void *, size_t);
+#else
+extern kmem_cache_t *real_kmem_cache_create(const char *, size_t, size_t, unsigned long,
+ void (*)(void *, kmem_cache_t *, unsigned long),
+ void (*)(void *, kmem_cache_t *, unsigned long));
+extern int real_kmem_cache_shrink(kmem_cache_t *);
+extern void *real_kmem_cache_alloc(kmem_cache_t *, int);
+extern void real_kmem_cache_free(kmem_cache_t *, void *);
+
+extern kmem_cache_t *kmem_cache_create_wrapper(const char *, size_t,
+ size_t, unsigned long,
+ void (*)(void *, kmem_cache_t *, unsigned long),
+ void (*)(void *, kmem_cache_t *, unsigned long),
+ struct alloc_struct * id);
+extern int kmem_cache_shrink_wrapper(kmem_cache_t *);
+extern void *kmem_cache_alloc_wrapper(kmem_cache_t *, int,
+ struct alloc_struct * id);
+extern void kmem_cache_free_wrapper(kmem_cache_t *, void *);
+
+#define kmem_cache_create(name,size,offset,flags,ctor,dtor) \
+({ \
+ static struct alloc_struct ID = { 0, __FILE__, __LINE__ }; \
+ kmem_cache_create_wrapper(name,size,offset,flags,ctor,dtor,&ID); \
+})
+
+#define kmem_cache_shrink(cachep) kmem_cache_shrink_wrapper(cachep)
+
+#define kmem_cache_alloc(cachep, flags) \
+({ \
+ static struct alloc_struct ID = { 0, __FILE__, __LINE__ }; \
+ kmem_cache_alloc_wrapper(cachep,flags,&ID); \
+})
+
+#define kmem_cache_free(cachep, objp) kmem_cache_free_wrapper(cachep, objp)
+
+#define kfree_s(obj,objsize) kfree_s_wrapper(obj,objsize)
+
+extern void *real_kmalloc(unsigned int size, int priority);
+extern void real_kfree(const void *);
+extern void real_kfree_s(const void *, size_t);
+
+extern void *kmalloc_wrapper(unsigned int size, int priority,
+ struct alloc_struct * id);
+extern void kfree_wrapper(void * obj);
+extern void kfree_s_wrapper(void * obj, size_t obj_size);
+
+#define kmalloc(size,priority) \
+({ \
+ static struct alloc_struct ID = { 0, __FILE__, __LINE__ }; \
+ kmalloc_wrapper(size,priority,&ID); \
+})
+
+#define kfree(obj) kfree_wrapper(obj)
+#define kfree_s(obj,objsize) kfree_s_wrapper(obj,objsize)
+#endif /* CONFIG_MEMLEAK */

extern int kmem_cache_reap(int, int, int);
extern int get_slabinfo(char *);
diff -urN linux-2.1.72-virgin/init/main.c linux-2.1.72-memleak/init/main.c
--- linux-2.1.72-virgin/init/main.c Thu Dec 11 03:36:51 1997
+++ linux-2.1.72-memleak/init/main.c Thu Dec 11 03:39:37 1997
@@ -963,6 +963,9 @@
#ifdef CONFIG_PCI
memory_start = pci_init(memory_start,memory_end);
#endif
+#ifdef CONFIG_MEMLEAK
+ memory_end = memleak_init(memory_start,memory_end);
+#endif
#ifdef CONFIG_MCA
memory_start = mca_init(memory_start,memory_end);
#endif
diff -urN linux-2.1.72-virgin/kernel/ksyms.c linux-2.1.72-memleak/kernel/ksyms.c
--- linux-2.1.72-virgin/kernel/ksyms.c Fri Dec 5 11:26:54 1997
+++ linux-2.1.72-memleak/kernel/ksyms.c Thu Dec 18 10:01:13 1997
@@ -121,10 +121,11 @@
EXPORT_SYMBOL(exit_files);

/* internal kernel memory management */
+EXPORT_SYMBOL(kmem_find_general_cachep);
+#ifndef CONFIG_MEMLEAK
EXPORT_SYMBOL(__get_free_pages);
EXPORT_SYMBOL(free_pages);
EXPORT_SYMBOL(__free_page);
-EXPORT_SYMBOL(kmem_find_general_cachep);
EXPORT_SYMBOL(kmem_cache_create);
EXPORT_SYMBOL(kmem_cache_shrink);
EXPORT_SYMBOL(kmem_cache_alloc);
@@ -132,6 +133,18 @@
EXPORT_SYMBOL(kmalloc);
EXPORT_SYMBOL(kfree);
EXPORT_SYMBOL(kfree_s);
+#else
+EXPORT_SYMBOL(__get_free_pages_wrapper);
+EXPORT_SYMBOL(free_pages_wrapper);
+EXPORT_SYMBOL(__free_page_wrapper);
+EXPORT_SYMBOL(kmalloc_wrapper);
+EXPORT_SYMBOL(kfree_wrapper);
+EXPORT_SYMBOL(kfree_s_wrapper);
+EXPORT_SYMBOL(kmem_cache_create_wrapper);
+EXPORT_SYMBOL(kmem_cache_shrink_wrapper);
+EXPORT_SYMBOL(kmem_cache_alloc_wrapper);
+EXPORT_SYMBOL(kmem_cache_free_wrapper);
+#endif /* CONFIG_MEMLEAK */
EXPORT_SYMBOL(vmalloc);
EXPORT_SYMBOL(vfree);
EXPORT_SYMBOL(mem_map);
diff -urN linux-2.1.72-virgin/mm/Makefile linux-2.1.72-memleak/mm/Makefile
--- linux-2.1.72-virgin/mm/Makefile Thu Jul 17 04:22:51 1997
+++ linux-2.1.72-memleak/mm/Makefile Sat Dec 6 05:52:32 1997
@@ -12,4 +12,8 @@
vmalloc.o slab.o simp.o\
swap.o vmscan.o page_io.o page_alloc.o swap_state.o swapfile.o

+ifeq ($(CONFIG_MEMLEAK),y)
+O_OBJS += memleak.o
+endif
+
include $(TOPDIR)/Rules.make
diff -urN linux-2.1.72-virgin/mm/memleak.c linux-2.1.72-memleak/mm/memleak.c
--- linux-2.1.72-virgin/mm/memleak.c Thu Jan 1 01:00:00 1970
+++ linux-2.1.72-memleak/mm/memleak.c Thu Dec 18 10:07:19 1997
@@ -0,0 +1,368 @@
+/*
+ * linux/mm/memleak.c, memory leak detector
+ *
+ * Copyright (C) 1997 Ingo Molnar
+ *
+ * Maintainer: Mike Galbraith mikeg@weiden.de
+ *
+ * Changelog:
+ * Dec 18 1997: memleak.c - added wrapper functions for slab.c
+ * Dec 18 1997: include/asm-i386/pgtable.h
+ * converted pte_alloc_kernel, pte_alloc, pgd_alloc to macros
+ */
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+
+/*
+ * Design:
+ *
+ * Every 32 bytes block of memory in the system has an 'allocation map entry'.
+ * A map entry is a two-fields bitmap, an 'ID' pointing to the allocator,
+ * and a ~2 seconds granularity timestamp to see object age.
+ * ID 0 means the block is empty. The allocation map is a very big static
+ * array, and is preallocated at boot, and put at the end of memory.
+ *
+ * This method relies on the fact that no object allocated in Linux
+ * is smaller than 32 bytes. True that we waste ~10% memory, but the
+ * method is very simple, extremely fast and generic. There are lots of
+ * systems that can tolerate 10% less memory, but almost no system
+ * can tolerate the CPU load caused by O(N) or O(log(N)) 'bookeeping
+ * algorithms' when allocating memory in a RL system.
+ *
+ * This method is a O(1) algorithm.
+ *
+ *
+ * Currently wrapped allocators:
+ *
+ * generic page allocator: get_free_pages()/free_pages()
+ * kernel allocator: kmalloc()/kfree()/kfree_s()
+ * kmem_cache_create()/kmem_cache_shrink()
+ * kmem_cache_alloc()/kmem_cache_free()
+ * networking allocator: skb_alloc()/skb_free()
+ *
+ * vmalloc()/vfree() will probably never be supported by
+ * this method, maybe we can represent them through their
+ * first physical page. It's not a common allocation
+ * method.
+ */
+
+#define PROBLEM() printk("MEMLEAK PROBLEM(): <%s,%d>.\n",__FILE__,__LINE__)
+
+#define MIN_MEMBLOCK_SIZE 32
+static unsigned long ENTRIES;
+
+
+#define IDX(addr) (__pa(addr)/MIN_MEMBLOCK_SIZE)
+
+/*
+ * We want to keep the allocation map as small as possible
+ */
+#define ID_BITS 9
+#define MAX_ID (1<<ID_BITS)
+#define TIMESTAMP_BITS (sizeof(unsigned int)*8-ID_BITS)
+
+struct alloc_entry {
+ unsigned int timestamp:TIMESTAMP_BITS, id:ID_BITS;
+};
+
+static struct alloc_entry * alloc_map;
+static struct alloc_struct * id_map [MAX_ID];
+static struct alloc_struct NULL_id = { 0, "<NULL>", 0 };
+
+static int curr_id;
+
+void alloc_addr(unsigned long addr, struct alloc_struct * id)
+{
+ unsigned long idx = IDX(addr);
+
+ if (!alloc_map) {
+ PROBLEM();
+ return;
+ }
+ if (idx > ENTRIES) {
+ PROBLEM();
+ return;
+ }
+ if (alloc_map[idx].id) {
+ PROBLEM();
+ return;
+ }
+
+ /*
+ * Dynamic 'allocation ID' registration. The fast path is
+ * really fast, dont worry about the rest.
+ */
+ if (!id->id) {
+ /*
+ * ID allocation has to be IRQ-safe
+ */
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+
+ if (id->id)
+ goto out;
+ if (!curr_id)
+ goto out;
+#if 0
+ printk("allocating ID.%d for %s:%d.\n",curr_id, id->file, id->line);
+#endif
+ id->id = curr_id;
+ curr_id++;
+ if (curr_id == MAX_ID) {
+ printk("ID wrapped around, stopping ID allocation.\n");
+ printk("Increase ID_BITS in memleak.c.\n");
+ curr_id = 0;
+ } else
+ id_map[curr_id-1] = id;
+out:
+ restore_flags(flags);
+ }
+ alloc_map[idx].id = id->id;
+ alloc_map[idx].timestamp = jiffies>>ID_BITS;
+}
+
+void free_addr(unsigned long addr)
+{
+ int idx = IDX(addr);
+
+ if (!alloc_map) {
+ PROBLEM();
+ return;
+ }
+ if (idx > ENTRIES) {
+ PROBLEM();
+ return;
+ }
+ if (!alloc_map[idx].id) {
+ if (!curr_id)
+ return;
+ PROBLEM();
+ return;
+ }
+ alloc_map[idx].id = 0;
+}
+
+/*
+* kmem_cache_create()/kmem_cache_shrink()/kmem_cache_alloc()/
+* kmem_cache_free wrappers
+*/
+
+kmem_cache_t *
+kmem_cache_create_wrapper(const char *name, size_t size, size_t offset,
+ unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long),
+ void (*dtor)(void*, kmem_cache_t *, unsigned long), struct alloc_struct * id)
+{
+ kmem_cache_t * obj = real_kmem_cache_create(name,size,offset,flags,ctor,dtor);
+ if (obj)
+ alloc_addr((unsigned long)obj,id);
+ return obj;
+}
+
+int kmem_cache_shrink_wrapper(kmem_cache_t *cachep)
+{
+ int ret = real_kmem_cache_shrink(cachep);
+ if (!ret)
+ free_addr((unsigned long)cachep);
+ else
+ PROBLEM();
+ return ret;
+}
+
+void *kmem_cache_alloc_wrapper(kmem_cache_t *cachep, int flags,
+ struct alloc_struct * id)
+{
+ void * obj = real_kmem_cache_alloc(cachep, flags);
+ if (obj)
+ alloc_addr((unsigned long)obj,id);
+ return obj;
+}
+
+void kmem_cache_free_wrapper(kmem_cache_t *cachep, void *objp)
+{
+ if (cachep && objp)
+ free_addr((unsigned long)objp);
+ else
+ PROBLEM();
+ real_kmem_cache_free(cachep, objp);
+}
+
+/*
+* kmalloc()/kfree()/kfree_s() wrappers
+*/
+
+void * kmalloc_wrapper (unsigned int size, int priority,
+ struct alloc_struct * id)
+{
+ void * obj = real_kmalloc(size,priority);
+ if (obj)
+ alloc_addr((unsigned long)obj,id);
+ return obj;
+}
+
+void kfree_wrapper (void * obj)
+{
+ if (obj)
+ free_addr((unsigned long)obj);
+ else
+ PROBLEM();
+ real_kfree(obj);
+}
+
+void kfree_s_wrapper (void * obj, size_t objsize)
+{
+ if (obj)
+ free_addr((unsigned long)obj);
+ else
+ PROBLEM();
+ real_kfree_s(obj,objsize);
+}
+
+unsigned long __get_free_pages_wrapper (int priority, unsigned long gfporder,
+ int dma, struct alloc_struct * id)
+{
+ unsigned long obj = real__get_free_pages(priority,gfporder,dma);
+ if (obj)
+ alloc_addr(obj,id);
+ return obj;
+}
+
+/*
+ * gfp()/free_page[s]() wrappers
+ *
+ * The cli()s are ugly
+ */
+
+void free_pages_wrapper (unsigned long addr, unsigned long order)
+{
+ unsigned long flags;
+ unsigned long map_nr = MAP_NR(addr);
+
+ save_flags(flags);
+ cli();
+
+ if (map_nr < MAP_NR(high_memory)) {
+ mem_map_t * map = mem_map + map_nr;
+ if ((!PageReserved(map)) && (atomic_read(&map->count) == 1))
+ free_addr(addr);
+ }
+ real_free_pages(addr,order);
+ restore_flags(flags);
+}
+
+void __free_page_wrapper (struct page * page)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if ((!PageReserved(page)) && (atomic_read(&page->count) == 1)) {
+ unsigned long obj = PAGE_OFFSET + (page->map_nr << PAGE_SHIFT);
+ if (obj)
+ free_addr(obj);
+ else
+ PROBLEM();
+ }
+ real__free_page(page);
+ restore_flags(flags);
+}
+
+/*
+ * We put the alloc table at the end of physical memory
+ */
+__initfunc(unsigned long memleak_init (unsigned long start_mem, unsigned long end_mem))
+{
+ unsigned long MEMSIZE, size;
+
+ id_map[0] = &NULL_id;
+ curr_id = 1;
+
+ end_mem = PAGE_ALIGN(end_mem);
+
+ MEMSIZE = end_mem-PAGE_OFFSET;
+ ENTRIES = MEMSIZE/(MIN_MEMBLOCK_SIZE+sizeof(struct alloc_entry))+1;
+
+ size = ENTRIES * sizeof(struct alloc_entry);
+
+ end_mem = PAGE_ALIGN(end_mem-size)-PAGE_SIZE;
+
+ alloc_map = (struct alloc_entry *) end_mem;
+
+ printk("MEMLEAK, allocating %ld KB allocation map.\n", size/1024);
+
+ if (!alloc_map) {
+ PROBLEM();
+ for(;;);
+ }
+
+ memset(alloc_map,0,size);
+
+ return end_mem;
+}
+
+
+#define LINE_SIZE 128
+
+static ssize_t read_allocations (struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct alloc_struct * id;
+ unsigned long p;
+ unsigned long idx;
+ unsigned long timestamp;
+
+ char line [LINE_SIZE];
+
+ if(ppos != &file->f_pos)
+ return -ESPIPE;
+
+ p = idx = *ppos;
+
+ if (count < 0)
+ return -EINVAL;
+repeat:
+ if (idx >= ENTRIES)
+ return 0;
+
+ if (alloc_map[idx].id) {
+ id = id_map[alloc_map[idx].id];
+ timestamp = (alloc_map[idx].timestamp<<ID_BITS)/HZ;
+ count = sprintf(line,"<%p> %s:%d (%ld)\n",
+ (void *)(idx*MIN_MEMBLOCK_SIZE),id->file,id->line,timestamp);
+ copy_to_user(buf,line,count);
+ } else {
+ if (!idx) {
+ count = sprintf(line,"<%p> jiffies.c:%d (%ld)\n", NULL, 0, jiffies/HZ);
+ copy_to_user(buf,line,count);
+ } else {
+ idx++;
+ *ppos = idx;
+ /*
+ * there is at least one allocation in the system
+ */
+ goto repeat;
+ }
+ }
+
+ idx++;
+ *ppos = idx;
+
+ return count;
+}
+
+static struct file_operations proc_memleak_operations = {
+ NULL, /* lseek */
+ read_allocations,
+ NULL, /* write */
+};
+
+struct inode_operations proc_memleak_inode_operations = {
+ &proc_memleak_operations,
+};
+
diff -urN linux-2.1.72-virgin/mm/page_alloc.c linux-2.1.72-memleak/mm/page_alloc.c
--- linux-2.1.72-virgin/mm/page_alloc.c Tue Jun 17 01:36:01 1997
+++ linux-2.1.72-memleak/mm/page_alloc.c Thu Dec 18 11:03:35 1997
@@ -130,7 +130,11 @@
spin_unlock_irqrestore(&page_alloc_lock, flags);
}

+#ifndef CONFIG_MEMLEAK
void __free_page(struct page *page)
+#else
+void real__free_page(struct page *page)
+#endif
{
if (!PageReserved(page) && atomic_dec_and_test(&page->count)) {
delete_from_swap_cache(page);
@@ -138,7 +142,11 @@
}
}

+#ifndef CONFIG_MEMLEAK
void free_pages(unsigned long addr, unsigned long order)
+#else
+void real_free_pages(unsigned long addr, unsigned long order)
+#endif
{
unsigned long map_nr = MAP_NR(addr);

@@ -194,7 +202,11 @@
map->age = PAGE_INITIAL_AGE; \
} while (0)

+#ifndef CONFIG_MEMLEAK
unsigned long __get_free_pages(int priority, unsigned long order, int dma)
+#else
+unsigned long real__get_free_pages(int priority, unsigned long order, int dma)
+#endif
{
unsigned long flags;
int reserved_pages;
diff -urN linux-2.1.72-virgin/mm/slab.c linux-2.1.72-memleak/mm/slab.c
--- linux-2.1.72-virgin/mm/slab.c Thu Oct 23 11:36:06 1997
+++ linux-2.1.72-memleak/mm/slab.c Thu Dec 18 10:09:20 1997
@@ -102,6 +102,10 @@

#include <linux/mm.h>
#include <linux/slab.h>
+#ifdef CONFIG_MEMLEAK
+#define _IN_SLAB_C_
+#include "unwrap.h"
+#endif
#include <linux/interrupt.h>
#include <linux/config.h>
#include <linux/init.h>
diff -urN linux-2.1.72-virgin/mm/unwrap.h linux-2.1.72-memleak/mm/unwrap.h
--- linux-2.1.72-virgin/mm/unwrap.h Thu Jan 1 01:00:00 1970
+++ linux-2.1.72-memleak/mm/unwrap.h Thu Dec 18 11:36:48 1997
@@ -0,0 +1,30 @@
+#ifndef _UNWRAP_H
+#define _UNWRAP_H
+
+/* unwrap page_alloc.c functions for any other wrapped allocators */
+#undef free_pages
+#undef __get_free_pages
+
+#define free_pages real_free_pages
+#define __get_free_pages real__get_free_pages
+
+/* unwrap slab.c functions */
+#ifdef _IN_SLAB_C_
+#undef kmem_cache_create
+#undef kmem_cache_shrink
+#undef kmem_cache_alloc
+#undef kmem_cache_free
+#undef kmalloc
+#undef kfree
+#undef kfree_s
+
+#define kmem_cache_create real_kmem_cache_create
+#define kmem_cache_shrink real_kmem_cache_shrink
+#define kmem_cache_alloc real_kmem_cache_alloc
+#define kmem_cache_free real_kmem_cache_free
+#define kmalloc real_kmalloc
+#define kfree real_kfree
+#define kfree_s real_kfree_s
+#endif
+
+#endif /* _UNWRAP_H */
diff -urN linux-2.1.72-virgin/net/core/skbuff.c linux-2.1.72-memleak/net/core/skbuff.c
--- linux-2.1.72-virgin/net/core/skbuff.c Mon Dec 1 04:27:16 1997
+++ linux-2.1.72-memleak/net/core/skbuff.c Sat Dec 6 05:52:32 1997
@@ -113,7 +113,12 @@
* to be a good idea.
*/

+#ifndef CONFIG_MEMLEAK
struct sk_buff *alloc_skb(unsigned int size,int priority)
+#else
+struct sk_buff *alloc_skb_wrap(unsigned int size,int priority,
+ struct alloc_struct * id)
+#endif
{
struct sk_buff *skb;
unsigned char *bptr;
@@ -144,7 +149,11 @@
* Allocate some space
*/

+#ifndef CONFIG_MEMLEAK
bptr = kmalloc(size,priority);
+#else
+ bptr = kmalloc_wrapper(size,priority,id);
+#endif
if (bptr == NULL) {
atomic_inc(&net_fails);
return NULL;
@@ -198,7 +207,11 @@
{
/* don't do anything if somebody still uses us */
if (atomic_dec_and_test(&skb->count)) {
+#ifndef CONFIG_MEMLEAK
kfree(skb->head);
+#else
+ kfree_wrapper(skb->head);
+#endif
atomic_dec(&net_skbcount);
}
}
@@ -217,7 +230,11 @@
__kfree_skbmem(skb->data_skb);
}
if (free_head)
+#ifndef CONFIG_MEMLEAK
kfree(addr);
+#else
+ kfree_wrapper(addr);
+#endif
atomic_dec(&net_skbcount);
}
}
@@ -226,7 +243,12 @@
* Duplicate an sk_buff. The new one is not owned by a socket.
*/

+#ifndef CONFIG_MEMLEAK
struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
+#else
+struct sk_buff *skb_clone_wrap(struct sk_buff *skb, int priority,
+ struct alloc_struct * id)
+#endif
{
struct sk_buff *n;
int inbuff = 0;
@@ -237,7 +259,11 @@
skb->inclone = SKB_CLONE_ORIG;
inbuff = SKB_CLONE_INLINE;
} else {
+#ifndef CONFIG_MEMLEAK
n = kmalloc(sizeof(*n), priority);
+#else
+ n = kmalloc_wrapper(sizeof(*n), priority, id);
+#endif
if (!n)
return NULL;
}
@@ -263,7 +289,12 @@
* This is slower, and copies the whole data area
*/

+#ifndef CONFIG_MEMLEAK
struct sk_buff *skb_copy(struct sk_buff *skb, int priority)
+#else
+struct sk_buff *skb_copy_wrap(struct sk_buff *skb, int priority,
+ struct alloc_struct * id)
+#endif
{
struct sk_buff *n;
unsigned long offset;
@@ -272,7 +303,11 @@
* Allocate the copy buffer
*/

+#ifndef CONFIG_MEMLEAK
n=alloc_skb(skb->end - skb->head, priority);
+#else
+ n=alloc_skb_wrap(skb->end - skb->head, priority, id);
+#endif
if(n==NULL)
return NULL;

@@ -365,11 +400,20 @@
return n;
}

+#ifndef CONFIG_MEMLEAK
struct sk_buff *dev_alloc_skb(unsigned int length)
+#else
+struct sk_buff *dev_alloc_skb_wrap(unsigned int length,
+ struct alloc_struct * id)
+#endif
{
struct sk_buff *skb;

+#ifndef CONFIG_MEMLEAK
skb = alloc_skb(length+16, GFP_ATOMIC);
+#else
+ skb = alloc_skb_wrap(length+16, GFP_ATOMIC, id);
+#endif
if (skb)
skb_reserve(skb,16);
return skb;
diff -urN linux-2.1.72-virgin/net/netsyms.c linux-2.1.72-memleak/net/netsyms.c
--- linux-2.1.72-virgin/net/netsyms.c Sat Dec 6 04:49:13 1997
+++ linux-2.1.72-memleak/net/netsyms.c Sat Dec 6 05:52:32 1997
@@ -329,11 +329,21 @@
EXPORT_SYMBOL(fddi_type_trans);
#endif /* CONFIG_FDDI */
EXPORT_SYMBOL(eth_copy_and_sum);
+#ifndef CONFIG_MEMLEAK
EXPORT_SYMBOL(alloc_skb);
+#else
+EXPORT_SYMBOL(alloc_skb_wrap);
+#endif
EXPORT_SYMBOL(__kfree_skb);
+#ifndef CONFIG_MEMLEAK
EXPORT_SYMBOL(skb_clone);
EXPORT_SYMBOL(skb_copy);
EXPORT_SYMBOL(dev_alloc_skb);
+#else
+EXPORT_SYMBOL(skb_clone_wrap);
+EXPORT_SYMBOL(skb_copy_wrap);
+EXPORT_SYMBOL(dev_alloc_skb_wrap);
+#endif
EXPORT_SYMBOL(netif_rx);
EXPORT_SYMBOL(dev_add_pack);
EXPORT_SYMBOL(dev_remove_pack);
\
 
 \ /
  Last update: 2005-03-22 13:40    [W:0.077 / U:0.552 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site