[ltp] Memory leak with vmalloc?

Dave Hansen linux-thinkpad@linux-thinkpad.org
Wed, 19 Apr 2006 17:15:29 -0700


--=-2iqpybU9O73zjsqNEfE5
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

On Wed, 2006-04-19 at 16:05 -0700, Dave Hansen wrote:
> On Sun, 2006-03-26 at 21:53 +0100, Hamish Marson wrote:
> > Is anyone else finding the same thing? It doesn't sem to be associated
> > with susped/resume itself... Dumping /proc/meminfo before & after shows
> > no loss during the suspend/resume cycle... At least not when I try by
> > hand.. 
> 
> Module code itself sits in vmalloc'd areas, and the e1000 driver also
> does some vmallocs of its own.  I'd venture a guess that one of those is
> responsible, first, but no guarantees.  Putting some printks around the
> vmallocs in those two places might be helpful.
> 
> I can also write you a debugging patch to print out what all of the
> callers of each vmalloc area were.  Would take an hour or two to put
> together... 


Here's a patch that will dump all of the vmalloc allocations out to
dmesg.  Make sure to recompile all of your modules and do a clean kernel
build.

	cat /proc/buddyinfo

will activiate it.

-- Dave

--=-2iqpybU9O73zjsqNEfE5
Content-Disposition: attachment; filename=vmalloc-debug.patch
Content-Type: text/x-patch; name=vmalloc-debug.patch; charset=ANSI_X3.4-1968
Content-Transfer-Encoding: 7bit



---

 mm/swapfile.c                     |    0 
 work-dave/include/linux/vmalloc.h |   30 ++++++++-----
 work-dave/mm/page_alloc.c         |    5 +-
 work-dave/mm/vmalloc.c            |   87 +++++++++++++++++++++++++-------------
 4 files changed, 83 insertions(+), 39 deletions(-)

diff -puN mm/vmalloc.c~vmalloc-debug mm/vmalloc.c
--- work/mm/vmalloc.c~vmalloc-debug	2006-04-19 17:12:39.000000000 -0700
+++ work-dave/mm/vmalloc.c	2006-04-19 17:12:39.000000000 -0700
@@ -157,8 +157,9 @@ int map_vm_area(struct vm_struct *area, 
 	return err;
 }
 
-struct vm_struct *__get_vm_area_node(unsigned long size, unsigned long flags,
-				unsigned long start, unsigned long end, int node)
+struct vm_struct *__get_vm_area_node_allocfile(unsigned long size, unsigned long flags,
+				unsigned long start, unsigned long end, int node
+				,char *filename, int line)
 {
 	struct vm_struct **p, *tmp, *area;
 	unsigned long align = 1;
@@ -178,6 +179,9 @@ struct vm_struct *__get_vm_area_node(uns
 	size = PAGE_ALIGN(size);
 
 	area = kmalloc_node(sizeof(*area), GFP_KERNEL, node);
+	area->afilename = filename;
+	area->aline = line;
+	memset(area, 0, sizeof(*area));
 	if (unlikely(!area))
 		return NULL;
 
@@ -230,10 +234,11 @@ out:
 	return NULL;
 }
 
-struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
-				unsigned long start, unsigned long end)
+struct vm_struct *__get_vm_area_afile(unsigned long size, unsigned long flags,
+				unsigned long start, unsigned long end,
+				char *f, int l)
 {
-	return __get_vm_area_node(size, flags, start, end, -1);
+	return __get_vm_area_node_allocfile(size, flags, start, end, -1,f,l);
 }
 
 /**
@@ -246,14 +251,38 @@ struct vm_struct *__get_vm_area(unsigned
  *	and reserved it for out purposes.  Returns the area descriptor
  *	on success or %NULL on failure.
  */
-struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
+struct vm_struct *get_vm_area_afile(unsigned long size, unsigned long flags, char *f, int l)
 {
-	return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);
+	return __get_vm_area_afile(size, flags, VMALLOC_START, VMALLOC_END,f,l);
 }
 
-struct vm_struct *get_vm_area_node(unsigned long size, unsigned long flags, int node)
+struct vm_struct *get_vm_area_node_afile(unsigned long size, unsigned long flags, int node,char *f, int l)
 {
-	return __get_vm_area_node(size, flags, VMALLOC_START, VMALLOC_END, node);
+	return __get_vm_area_node_allocfile(size, flags, VMALLOC_START, VMALLOC_END, node,f,l);
+}
+
+void print_vmalloc_stuff(void)
+{
+	struct vm_struct **p, *tmp;
+
+	read_lock(&vmlist_lock);
+	for (p = &vmlist ; (tmp = *p) != NULL ;p = &tmp->next) {
+		printk("vmalloc %p pages: %5d size: %6ld - %s:%d\n",
+				tmp->addr,
+				tmp->nr_pages,
+				tmp->size,
+				tmp->filename,
+				tmp->line);
+		if ( tmp->filename !=  tmp->afilename ||
+		    tmp->line != tmp->aline)
+		printk("vmalloc %p pages: %5d size: %6ld -a- %s:%d\n",
+				tmp->addr,
+				tmp->nr_pages,
+				tmp->size,
+				tmp->afilename,
+				tmp->aline);
+	}
+	read_unlock(&vmlist_lock);
 }
 
 /* Caller must hold vmlist_lock */
@@ -401,8 +430,8 @@ void *vmap(struct page **pages, unsigned
 }
 EXPORT_SYMBOL(vmap);
 
-void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
-				pgprot_t prot, int node)
+void *__vmalloc_area_node_file(struct vm_struct *area, gfp_t gfp_mask,
+				pgprot_t prot, int node, char *file, int line)
 {
 	struct page **pages;
 	unsigned int nr_pages, array_size, i;
@@ -413,9 +442,11 @@ void *__vmalloc_area_node(struct vm_stru
 	area->nr_pages = nr_pages;
 	/* Please note that the recursion is strictly bounded. */
 	if (array_size > PAGE_SIZE)
-		pages = __vmalloc_node(array_size, gfp_mask, PAGE_KERNEL, node);
+		pages = __vmalloc_node_file(array_size, gfp_mask, PAGE_KERNEL, node, file, line);
 	else
 		pages = kmalloc_node(array_size, (gfp_mask & ~__GFP_HIGHMEM), node);
+	area->filename = file;
+	area->line = line;
 	area->pages = pages;
 	if (!area->pages) {
 		remove_vm_area(area->addr);
@@ -445,9 +476,9 @@ fail:
 	return NULL;
 }
 
-void *__vmalloc_area(struct vm_struct *area, gfp_t gfp_mask, pgprot_t prot)
+void *__vmalloc_area_file(struct vm_struct *area, gfp_t gfp_mask, pgprot_t prot, char *file, int line)
 {
-	return __vmalloc_area_node(area, gfp_mask, prot, -1);
+	return __vmalloc_area_node_file(area, gfp_mask, prot, -1, file, line);
 }
 
 /**
@@ -462,8 +493,8 @@ void *__vmalloc_area(struct vm_struct *a
  *	allocator with @gfp_mask flags.  Map them into contiguous
  *	kernel virtual space, using a pagetable protection of @prot.
  */
-void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot,
-			int node)
+void *__vmalloc_node_file(unsigned long size, gfp_t gfp_mask, pgprot_t prot,
+			int node, char *file, int line)
 {
 	struct vm_struct *area;
 
@@ -471,19 +502,19 @@ void *__vmalloc_node(unsigned long size,
 	if (!size || (size >> PAGE_SHIFT) > num_physpages)
 		return NULL;
 
-	area = get_vm_area_node(size, VM_ALLOC, node);
+	area = get_vm_area_node_afile(size, VM_ALLOC, node, file, line);
 	if (!area)
 		return NULL;
 
-	return __vmalloc_area_node(area, gfp_mask, prot, node);
+	return __vmalloc_area_node_file(area, gfp_mask, prot, node, file, line);
 }
-EXPORT_SYMBOL(__vmalloc_node);
+EXPORT_SYMBOL(__vmalloc_node_file);
 
-void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot)
+void *__vmalloc_file(unsigned long size, gfp_t gfp_mask, pgprot_t prot, char *file, int line)
 {
-	return __vmalloc_node(size, gfp_mask, prot, -1);
+	return __vmalloc_node_file(size, gfp_mask, prot, -1, file, line);
 }
-EXPORT_SYMBOL(__vmalloc);
+EXPORT_SYMBOL(__vmalloc_file);
 
 /**
  *	vmalloc  -  allocate virtually contiguous memory
@@ -496,11 +527,11 @@ EXPORT_SYMBOL(__vmalloc);
  *	For tight cotrol over page level allocator and protection flags
  *	use __vmalloc() instead.
  */
-void *vmalloc(unsigned long size)
+void *vmalloc_file(unsigned long size, char *file, int line)
 {
-       return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
+       return __vmalloc_file(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL, file, line);
 }
-EXPORT_SYMBOL(vmalloc);
+EXPORT_SYMBOL(vmalloc_file);
 
 /**
  *	vmalloc_node  -  allocate memory on a specific node
@@ -514,11 +545,11 @@ EXPORT_SYMBOL(vmalloc);
  *	For tight cotrol over page level allocator and protection flags
  *	use __vmalloc() instead.
  */
-void *vmalloc_node(unsigned long size, int node)
+void *vmalloc_node_file(unsigned long size, int node, char *file, int line)
 {
-       return __vmalloc_node(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL, node);
+       return __vmalloc_node_file(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL, node, file, line);
 }
-EXPORT_SYMBOL(vmalloc_node);
+EXPORT_SYMBOL(vmalloc_node_file);
 
 #ifndef PAGE_KERNEL_EXEC
 # define PAGE_KERNEL_EXEC PAGE_KERNEL
diff -puN include/linux/vmalloc.h~vmalloc-debug include/linux/vmalloc.h
--- work/include/linux/vmalloc.h~vmalloc-debug	2006-04-19 17:12:39.000000000 -0700
+++ work-dave/include/linux/vmalloc.h	2006-04-19 17:12:39.000000000 -0700
@@ -26,20 +26,28 @@ struct vm_struct {
 	unsigned int		nr_pages;
 	unsigned long		phys_addr;
 	struct vm_struct	*next;
+	char *afilename;
+	int aline;
+	char *filename;
+	int line;
 };
 
 /*
  *	Highlevel APIs for driver use
  */
-extern void *vmalloc(unsigned long size);
-extern void *vmalloc_node(unsigned long size, int node);
+#define vmalloc(size) vmalloc_file(size, __FILE__, __LINE__)
+extern void *vmalloc_file(unsigned long size, char *file, int line);
+#define vmalloc_node(size, node) vmalloc_node_file(size, node, __FILE__, __LINE__)
+extern void *vmalloc_node_file(unsigned long size, int node, char *file, int line);
 extern void *vmalloc_exec(unsigned long size);
 extern void *vmalloc_32(unsigned long size);
-extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot);
-extern void *__vmalloc_area(struct vm_struct *area, gfp_t gfp_mask,
-				pgprot_t prot);
-extern void *__vmalloc_node(unsigned long size, gfp_t gfp_mask,
-				pgprot_t prot, int node);
+#define __vmalloc(size, gfp_mask, prot) __vmalloc_file(size, gfp_mask, prot, __FILE__, __LINE__)
+extern void *__vmalloc_file(unsigned long size, gfp_t gfp_mask, pgprot_t prot, char *file, int line);
+#define __vmalloc_area(a, b, c) __vmalloc_area_file(a, b, c, __FILE, __LINE__)
+extern void *__vmalloc_area_file(struct vm_struct *area, gfp_t gfp_mask,
+				pgprot_t prot, char *file, int line);
+extern void *__vmalloc_node_file(unsigned long size, gfp_t gfp_mask,
+				pgprot_t prot, int node, char *file, int line);
 extern void vfree(void *addr);
 
 extern void *vmap(struct page **pages, unsigned int count,
@@ -49,11 +57,13 @@ extern void vunmap(void *addr);
 /*
  *	Lowlevel-APIs (not for driver use!)
  */
-extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
+#define get_vm_area(a, b) get_vm_area_afile(a, b, __FILE__, __LINE__)
+extern struct vm_struct *get_vm_area_afile(unsigned long size, unsigned long flags,char *f, int l);
 extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
 					unsigned long start, unsigned long end);
-extern struct vm_struct *get_vm_area_node(unsigned long size,
-					unsigned long flags, int node);
+#define get_vm_area_node(a,b,c) get_vm_area_node_afile(a,b,c, __FILE__, __LINE__)
+extern struct vm_struct *get_vm_area_node_afile(unsigned long size,
+					unsigned long flags, int node,char *f, int l);
 extern struct vm_struct *remove_vm_area(void *addr);
 extern struct vm_struct *__remove_vm_area(void *addr);
 extern int map_vm_area(struct vm_struct *area, pgprot_t prot,
diff -puN mm/page_alloc.c~vmalloc-debug mm/page_alloc.c
--- work/mm/page_alloc.c~vmalloc-debug	2006-04-19 17:12:39.000000000 -0700
+++ work-dave/mm/page_alloc.c	2006-04-19 17:12:39.000000000 -0700
@@ -2274,6 +2274,8 @@ static int frag_show(struct seq_file *m,
 	struct zone *node_zones = pgdat->node_zones;
 	unsigned long flags;
 	int order;
+	extern void print_vmalloc_stuff(void);
+	print_vmalloc_stuff();
 
 	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
 		if (!populated_zone(zone))
@@ -2305,7 +2307,8 @@ static int zoneinfo_show(struct seq_file
 	struct zone *zone;
 	struct zone *node_zones = pgdat->node_zones;
 	unsigned long flags;
-
+extern void print_vmalloc_stuff(void);
+	print_vmalloc_stuff();
 	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) {
 		int i;
 
diff -puN fs/proc/proc_misc.c~vmalloc-debug fs/proc/proc_misc.c
diff -puN include/linux/list.h~vmalloc-debug include/linux/list.h
diff -puN fs/proc/mmu.c~vmalloc-debug fs/proc/mmu.c
diff -puN fs/proc/kcore.c~vmalloc-debug fs/proc/kcore.c
diff -puN mm/swapfile.c~vmalloc-debug mm/swapfile.c
_

--=-2iqpybU9O73zjsqNEfE5--