linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] kmemleak: Add support for the bootmem allocator
@ 2009-07-06 10:51 Catalin Marinas
  2009-07-06 10:51 ` [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks Catalin Marinas
                   ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Catalin Marinas @ 2009-07-06 10:51 UTC (permalink / raw)
  To: linux-mm, linux-kernel; +Cc: Ingo Molnar, Pekka Enberg

Hi,

In the last few days, I went through of false positives reported by
kmemleak and it turns out some of them were caused by not tracking
alloc_bootmem* calls. Rather than adding more and more kmemleak
annotations throughout the kernel, I decided to add support for tracking
all the alloc_bootmem* and free_bootmem calls.

The latter may not have a corresponding alloc_bootmem* pair or it may
only free part of a block. I changed kmemleak to support this usage.

Thanks for your feedback.


Catalin Marinas (3):
      kmemleak: Remove alloc_bootmem annotations introduced in the past
      kmemleak: Add callbacks to the bootmem allocator
      kmemleak: Allow partial freeing of memory blocks


 include/linux/kmemleak.h |    4 +++
 kernel/pid.c             |    7 ------
 mm/bootmem.c             |   36 ++++++++++++++++++++++++------
 mm/kmemleak.c            |   55 ++++++++++++++++++++++++++++++++++++++++++----
 mm/page_alloc.c          |   14 +++---------
 5 files changed, 86 insertions(+), 30 deletions(-)

-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-06 10:51 [RFC PATCH 0/3] kmemleak: Add support for the bootmem allocator Catalin Marinas
@ 2009-07-06 10:51 ` Catalin Marinas
  2009-07-07  7:12   ` Pekka Enberg
  2009-07-06 10:51 ` [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator Catalin Marinas
  2009-07-06 10:52 ` [RFC PATCH 3/3] kmemleak: Remove alloc_bootmem annotations introduced in the past Catalin Marinas
  2 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-06 10:51 UTC (permalink / raw)
  To: linux-mm, linux-kernel; +Cc: Ingo Molnar, Pekka Enberg

Functions like free_bootmem() are allowed to free only part of a memory
block. This patch adds support for this via the kmemleak_free_part()
callback which removes the original object and creates one or two
additional objects as a result of the memory block split.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
---
 include/linux/kmemleak.h |    4 +++
 mm/kmemleak.c            |   55 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index 7796aed..6a63807 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -27,6 +27,7 @@ extern void kmemleak_init(void);
 extern void kmemleak_alloc(const void *ptr, size_t size, int min_count,
 			   gfp_t gfp);
 extern void kmemleak_free(const void *ptr);
+extern void kmemleak_free_part(const void *ptr, size_t size);
 extern void kmemleak_padding(const void *ptr, unsigned long offset,
 			     size_t size);
 extern void kmemleak_not_leak(const void *ptr);
@@ -71,6 +72,9 @@ static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
 static inline void kmemleak_free(const void *ptr)
 {
 }
+static inline void kmemleak_free_part(const void *ptr, size_t size)
+{
+}
 static inline void kmemleak_free_recursive(const void *ptr, unsigned long flags)
 {
 }
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 5f7d8ae..57f8081 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -210,6 +210,7 @@ static DEFINE_MUTEX(scan_mutex);
 enum {
 	KMEMLEAK_ALLOC,
 	KMEMLEAK_FREE,
+	KMEMLEAK_FREE_PART,
 	KMEMLEAK_NOT_LEAK,
 	KMEMLEAK_IGNORE,
 	KMEMLEAK_SCAN_AREA,
@@ -522,15 +523,20 @@ out:
 
 /*
  * Remove the metadata (struct kmemleak_object) for a memory block from the
- * object_list and object_tree_root and decrement its use_count.
+ * object_list and object_tree_root and decrement its use_count. If the memory
+ * block is partially freed (size > 0), the function may create additional
+ * metadata for the remaining parts of the block.
  */
-static void delete_object(unsigned long ptr)
+static void delete_object(unsigned long ptr, size_t size)
 {
 	unsigned long flags;
 	struct kmemleak_object *object;
+	unsigned long start, end;
+	unsigned int min_count;
+	gfp_t gfp_flags;
 
 	write_lock_irqsave(&kmemleak_lock, flags);
-	object = lookup_object(ptr, 0);
+	object = lookup_object(ptr, 1);
 	if (!object) {
 #ifdef DEBUG
 		kmemleak_warn("Freeing unknown object at 0x%08lx\n",
@@ -552,8 +558,29 @@ static void delete_object(unsigned long ptr)
 	 */
 	spin_lock_irqsave(&object->lock, flags);
 	object->flags &= ~OBJECT_ALLOCATED;
+	start = object->pointer;
+	end = object->pointer + object->size;
+	min_count = object->min_count;
 	spin_unlock_irqrestore(&object->lock, flags);
 	put_object(object);
+
+	if (!size)
+		return;
+
+	/*
+	 * Partial freeing. Just create one or two objects that may result
+	 * from the memory block split.
+	 */
+	if (in_atomic())
+		gfp_flags = GFP_ATOMIC;
+	else
+		gfp_flags = GFP_KERNEL;
+
+	if (ptr > start)
+		create_object(start, ptr - start, min_count, gfp_flags);
+	if (ptr + size < end)
+		create_object(ptr + size, end - ptr - size, min_count,
+			      gfp_flags);
 }
 
 /*
@@ -720,13 +747,28 @@ void kmemleak_free(const void *ptr)
 	pr_debug("%s(0x%p)\n", __func__, ptr);
 
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
-		delete_object((unsigned long)ptr);
+		delete_object((unsigned long)ptr, 0);
 	else if (atomic_read(&kmemleak_early_log))
 		log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free);
 
 /*
+ * Partial memory freeing function callback. This function is usually called
+ * from bootmem allocator when (part of) a memory block is freed.
+ */
+void kmemleak_free_part(const void *ptr, size_t size)
+{
+	pr_debug("%s(0x%p)\n", __func__, ptr);
+
+	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+		delete_object((unsigned long)ptr, size);
+	else if (atomic_read(&kmemleak_early_log))
+		log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
+}
+EXPORT_SYMBOL_GPL(kmemleak_free_part);
+
+/*
  * Mark an already allocated memory block as a false positive. This will cause
  * the block to no longer be reported as leak and always be scanned.
  */
@@ -1345,7 +1387,7 @@ static int kmemleak_cleanup_thread(void *arg)
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(object, &object_list, object_list)
-		delete_object(object->pointer);
+		delete_object(object->pointer, 0);
 	rcu_read_unlock();
 	mutex_unlock(&scan_mutex);
 
@@ -1440,6 +1482,9 @@ void __init kmemleak_init(void)
 		case KMEMLEAK_FREE:
 			kmemleak_free(log->ptr);
 			break;
+		case KMEMLEAK_FREE_PART:
+			kmemleak_free_part(log->ptr, log->size);
+			break;
 		case KMEMLEAK_NOT_LEAK:
 			kmemleak_not_leak(log->ptr);
 			break;

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-06 10:51 [RFC PATCH 0/3] kmemleak: Add support for the bootmem allocator Catalin Marinas
  2009-07-06 10:51 ` [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks Catalin Marinas
@ 2009-07-06 10:51 ` Catalin Marinas
  2009-07-06 10:58   ` Catalin Marinas
  2009-07-07  7:08   ` Pekka Enberg
  2009-07-06 10:52 ` [RFC PATCH 3/3] kmemleak: Remove alloc_bootmem annotations introduced in the past Catalin Marinas
  2 siblings, 2 replies; 21+ messages in thread
From: Catalin Marinas @ 2009-07-06 10:51 UTC (permalink / raw)
  To: linux-mm, linux-kernel; +Cc: Ingo Molnar, Pekka Enberg

This patch adds kmemleak_alloc/free callbacks to the bootmem allocator.
This would allow scanning of such blocks and help avoiding a whole class
of false positives and more kmemleak annotations.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
---
 mm/bootmem.c |   36 +++++++++++++++++++++++++++++-------
 1 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/mm/bootmem.c b/mm/bootmem.c
index d2a9ce9..18858ad 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -335,6 +335,8 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
 {
 	unsigned long start, end;
 
+	kmemleak_free(__va(physaddr));
+
 	start = PFN_UP(physaddr);
 	end = PFN_DOWN(physaddr + size);
 
@@ -354,6 +356,8 @@ void __init free_bootmem(unsigned long addr, unsigned long size)
 {
 	unsigned long start, end;
 
+	kmemleak_free_part(__va(addr), size);
+
 	start = PFN_UP(addr);
 	end = PFN_DOWN(addr + size);
 
@@ -597,7 +601,9 @@ restart:
 void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
 					unsigned long goal)
 {
-	return ___alloc_bootmem_nopanic(size, align, goal, 0);
+	void *ptr =  ___alloc_bootmem_nopanic(size, align, goal, 0);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
+	return ptr;
 }
 
 static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
@@ -631,7 +637,9 @@ static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
 void * __init __alloc_bootmem(unsigned long size, unsigned long align,
 			      unsigned long goal)
 {
-	return ___alloc_bootmem(size, align, goal, 0);
+	void *ptr = ___alloc_bootmem(size, align, goal, 0);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
+	return ptr;
 }
 
 static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata,
@@ -669,10 +677,14 @@ static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata,
 void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
 				   unsigned long align, unsigned long goal)
 {
+	void *ptr;
+
 	if (WARN_ON_ONCE(slab_is_available()))
 		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
-	return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);
+	ptr = ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
+	return ptr;
 }
 
 #ifdef CONFIG_SPARSEMEM
@@ -707,14 +719,18 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
 		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
 	ptr = alloc_arch_preferred_bootmem(pgdat->bdata, size, align, goal, 0);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
 	if (ptr)
 		return ptr;
 
 	ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
 	if (ptr)
 		return ptr;
 
-	return __alloc_bootmem_nopanic(size, align, goal);
+	ptr = __alloc_bootmem_nopanic(size, align, goal);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
+	return ptr;
 }
 
 #ifndef ARCH_LOW_ADDRESS_LIMIT
@@ -737,7 +753,9 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
 void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
 				  unsigned long goal)
 {
-	return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
+	void *ptr =  ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
+	return ptr;
 }
 
 /**
@@ -758,9 +776,13 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
 void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size,
 				       unsigned long align, unsigned long goal)
 {
+	void *ptr;
+
 	if (WARN_ON_ONCE(slab_is_available()))
 		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
 
-	return ___alloc_bootmem_node(pgdat->bdata, size, align,
-				goal, ARCH_LOW_ADDRESS_LIMIT);
+	ptr = ___alloc_bootmem_node(pgdat->bdata, size, align,
+				    goal, ARCH_LOW_ADDRESS_LIMIT);
+	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
+	return ptr;
 }

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFC PATCH 3/3] kmemleak: Remove alloc_bootmem annotations introduced in the past
  2009-07-06 10:51 [RFC PATCH 0/3] kmemleak: Add support for the bootmem allocator Catalin Marinas
  2009-07-06 10:51 ` [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks Catalin Marinas
  2009-07-06 10:51 ` [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator Catalin Marinas
@ 2009-07-06 10:52 ` Catalin Marinas
  2009-07-07  7:12   ` Pekka Enberg
  2 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-06 10:52 UTC (permalink / raw)
  To: linux-mm, linux-kernel; +Cc: Ingo Molnar, Pekka Enberg

kmemleak_alloc() calls were added in some places where alloc_bootmem was
called. Since now kmemleak tracks bootmem allocations, these explicit
calls should be run.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
---
 kernel/pid.c    |    7 -------
 mm/page_alloc.c |   14 +++-----------
 2 files changed, 3 insertions(+), 18 deletions(-)

diff --git a/kernel/pid.c b/kernel/pid.c
index 5fa1db4..31310b5 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -36,7 +36,6 @@
 #include <linux/pid_namespace.h>
 #include <linux/init_task.h>
 #include <linux/syscalls.h>
-#include <linux/kmemleak.h>
 
 #define pid_hashfn(nr, ns)	\
 	hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)
@@ -513,12 +512,6 @@ void __init pidhash_init(void)
 	pid_hash = alloc_bootmem(pidhash_size *	sizeof(*(pid_hash)));
 	if (!pid_hash)
 		panic("Could not alloc pidhash!\n");
-	/*
-	 * pid_hash contains references to allocated struct pid objects and it
-	 * must be scanned by kmemleak to avoid false positives.
-	 */
-	kmemleak_alloc(pid_hash, pidhash_size *	sizeof(*(pid_hash)), 0,
-		       GFP_KERNEL);
 	for (i = 0; i < pidhash_size; i++)
 		INIT_HLIST_HEAD(&pid_hash[i]);
 }
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index e0f2cdf..202ef6b 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4745,8 +4745,10 @@ void *__init alloc_large_system_hash(const char *tablename,
 			 * some pages at the end of hash table which
 			 * alloc_pages_exact() automatically does
 			 */
-			if (get_order(size) < MAX_ORDER)
+			if (get_order(size) < MAX_ORDER) {
 				table = alloc_pages_exact(size, GFP_ATOMIC);
+				kmemleak_alloc(table, size, 1, GFP_ATOMIC);
+			}
 		}
 	} while (!table && size > PAGE_SIZE && --log2qty);
 
@@ -4764,16 +4766,6 @@ void *__init alloc_large_system_hash(const char *tablename,
 	if (_hash_mask)
 		*_hash_mask = (1 << log2qty) - 1;
 
-	/*
-	 * If hashdist is set, the table allocation is done with __vmalloc()
-	 * which invokes the kmemleak_alloc() callback. This function may also
-	 * be called before the slab and kmemleak are initialised when
-	 * kmemleak simply buffers the request to be executed later
-	 * (GFP_ATOMIC flag ignored in this case).
-	 */
-	if (!hashdist)
-		kmemleak_alloc(table, size, 1, GFP_ATOMIC);
-
 	return table;
 }
 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-06 10:51 ` [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator Catalin Marinas
@ 2009-07-06 10:58   ` Catalin Marinas
  2009-07-07  7:08   ` Pekka Enberg
  1 sibling, 0 replies; 21+ messages in thread
From: Catalin Marinas @ 2009-07-06 10:58 UTC (permalink / raw)
  To: linux-mm, linux-kernel; +Cc: Ingo Molnar, Pekka Enberg

On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> This patch adds kmemleak_alloc/free callbacks to the bootmem allocator.
> This would allow scanning of such blocks and help avoiding a whole class
> of false positives and more kmemleak annotations.
> 
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Pekka Enberg <penberg@cs.helsinki.fi>
> ---
>  mm/bootmem.c |   36 +++++++++++++++++++++++++++++-------
>  1 files changed, 29 insertions(+), 7 deletions(-)
> 
> diff --git a/mm/bootmem.c b/mm/bootmem.c
> index d2a9ce9..18858ad 100644
> --- a/mm/bootmem.c
> +++ b/mm/bootmem.c
> @@ -335,6 +335,8 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
>  {
>  	unsigned long start, end;
>  
> +	kmemleak_free(__va(physaddr));

This should actually be

+	kmemleak_free_part(__va(physaddr), size);

-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-06 10:51 ` [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator Catalin Marinas
  2009-07-06 10:58   ` Catalin Marinas
@ 2009-07-07  7:08   ` Pekka Enberg
  2009-07-07 16:53     ` Johannes Weiner
  1 sibling, 1 reply; 21+ messages in thread
From: Pekka Enberg @ 2009-07-07  7:08 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: linux-mm, linux-kernel, Ingo Molnar, hannes

On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> This patch adds kmemleak_alloc/free callbacks to the bootmem allocator.
> This would allow scanning of such blocks and help avoiding a whole class
> of false positives and more kmemleak annotations.
> 
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Pekka Enberg <penberg@cs.helsinki.fi>

Looks good to me!

Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>

But lets cc Johannes on this too.

> ---
>  mm/bootmem.c |   36 +++++++++++++++++++++++++++++-------
>  1 files changed, 29 insertions(+), 7 deletions(-)
> 
> diff --git a/mm/bootmem.c b/mm/bootmem.c
> index d2a9ce9..18858ad 100644
> --- a/mm/bootmem.c
> +++ b/mm/bootmem.c
> @@ -335,6 +335,8 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
>  {
>  	unsigned long start, end;
>  
> +	kmemleak_free(__va(physaddr));
> +
>  	start = PFN_UP(physaddr);
>  	end = PFN_DOWN(physaddr + size);
>  
> @@ -354,6 +356,8 @@ void __init free_bootmem(unsigned long addr, unsigned long size)
>  {
>  	unsigned long start, end;
>  
> +	kmemleak_free_part(__va(addr), size);
> +
>  	start = PFN_UP(addr);
>  	end = PFN_DOWN(addr + size);
>  
> @@ -597,7 +601,9 @@ restart:
>  void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
>  					unsigned long goal)
>  {
> -	return ___alloc_bootmem_nopanic(size, align, goal, 0);
> +	void *ptr =  ___alloc_bootmem_nopanic(size, align, goal, 0);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> +	return ptr;
>  }
>  
>  static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
> @@ -631,7 +637,9 @@ static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
>  void * __init __alloc_bootmem(unsigned long size, unsigned long align,
>  			      unsigned long goal)
>  {
> -	return ___alloc_bootmem(size, align, goal, 0);
> +	void *ptr = ___alloc_bootmem(size, align, goal, 0);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> +	return ptr;
>  }
>  
>  static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata,
> @@ -669,10 +677,14 @@ static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata,
>  void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size,
>  				   unsigned long align, unsigned long goal)
>  {
> +	void *ptr;
> +
>  	if (WARN_ON_ONCE(slab_is_available()))
>  		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
>  
> -	return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);
> +	ptr = ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> +	return ptr;
>  }
>  
>  #ifdef CONFIG_SPARSEMEM
> @@ -707,14 +719,18 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
>  		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
>  
>  	ptr = alloc_arch_preferred_bootmem(pgdat->bdata, size, align, goal, 0);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
>  	if (ptr)
>  		return ptr;
>  
>  	ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
>  	if (ptr)
>  		return ptr;
>  
> -	return __alloc_bootmem_nopanic(size, align, goal);
> +	ptr = __alloc_bootmem_nopanic(size, align, goal);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> +	return ptr;
>  }
>  
>  #ifndef ARCH_LOW_ADDRESS_LIMIT
> @@ -737,7 +753,9 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
>  void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
>  				  unsigned long goal)
>  {
> -	return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
> +	void *ptr =  ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> +	return ptr;
>  }
>  
>  /**
> @@ -758,9 +776,13 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
>  void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size,
>  				       unsigned long align, unsigned long goal)
>  {
> +	void *ptr;
> +
>  	if (WARN_ON_ONCE(slab_is_available()))
>  		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
>  
> -	return ___alloc_bootmem_node(pgdat->bdata, size, align,
> -				goal, ARCH_LOW_ADDRESS_LIMIT);
> +	ptr = ___alloc_bootmem_node(pgdat->bdata, size, align,
> +				    goal, ARCH_LOW_ADDRESS_LIMIT);
> +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> +	return ptr;
>  }
> 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-06 10:51 ` [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks Catalin Marinas
@ 2009-07-07  7:12   ` Pekka Enberg
  2009-07-07  8:42     ` Catalin Marinas
  0 siblings, 1 reply; 21+ messages in thread
From: Pekka Enberg @ 2009-07-07  7:12 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: linux-mm, linux-kernel, Ingo Molnar, akpm

On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> @@ -552,8 +558,29 @@ static void delete_object(unsigned long ptr)
>  	 */
>  	spin_lock_irqsave(&object->lock, flags);
>  	object->flags &= ~OBJECT_ALLOCATED;
> +	start = object->pointer;
> +	end = object->pointer + object->size;
> +	min_count = object->min_count;
>  	spin_unlock_irqrestore(&object->lock, flags);
>  	put_object(object);
> +
> +	if (!size)
> +		return;
> +
> +	/*
> +	 * Partial freeing. Just create one or two objects that may result
> +	 * from the memory block split.
> +	 */
> +	if (in_atomic())
> +		gfp_flags = GFP_ATOMIC;
> +	else
> +		gfp_flags = GFP_KERNEL;

Are you sure we can do this? There's a big fat comment on top of
in_atomic() that suggest this is not safe. Why do we need to create the
object here anyway and not in the _alloc_ paths where gfp flags are
explicitly passed?

> +
> +	if (ptr > start)
> +		create_object(start, ptr - start, min_count, gfp_flags);
> +	if (ptr + size < end)
> +		create_object(ptr + size, end - ptr - size, min_count,
> +			      gfp_flags);
>  }
>  
>  /*


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 3/3] kmemleak: Remove alloc_bootmem annotations introduced in the past
  2009-07-06 10:52 ` [RFC PATCH 3/3] kmemleak: Remove alloc_bootmem annotations introduced in the past Catalin Marinas
@ 2009-07-07  7:12   ` Pekka Enberg
  0 siblings, 0 replies; 21+ messages in thread
From: Pekka Enberg @ 2009-07-07  7:12 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: linux-mm, linux-kernel, Ingo Molnar

On Mon, 2009-07-06 at 11:52 +0100, Catalin Marinas wrote:
> kmemleak_alloc() calls were added in some places where alloc_bootmem was
> called. Since now kmemleak tracks bootmem allocations, these explicit
> calls should be run.
> 
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Pekka Enberg <penberg@cs.helsinki.fi>

Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>

> ---
>  kernel/pid.c    |    7 -------
>  mm/page_alloc.c |   14 +++-----------
>  2 files changed, 3 insertions(+), 18 deletions(-)
> 
> diff --git a/kernel/pid.c b/kernel/pid.c
> index 5fa1db4..31310b5 100644
> --- a/kernel/pid.c
> +++ b/kernel/pid.c
> @@ -36,7 +36,6 @@
>  #include <linux/pid_namespace.h>
>  #include <linux/init_task.h>
>  #include <linux/syscalls.h>
> -#include <linux/kmemleak.h>
>  
>  #define pid_hashfn(nr, ns)	\
>  	hash_long((unsigned long)nr + (unsigned long)ns, pidhash_shift)
> @@ -513,12 +512,6 @@ void __init pidhash_init(void)
>  	pid_hash = alloc_bootmem(pidhash_size *	sizeof(*(pid_hash)));
>  	if (!pid_hash)
>  		panic("Could not alloc pidhash!\n");
> -	/*
> -	 * pid_hash contains references to allocated struct pid objects and it
> -	 * must be scanned by kmemleak to avoid false positives.
> -	 */
> -	kmemleak_alloc(pid_hash, pidhash_size *	sizeof(*(pid_hash)), 0,
> -		       GFP_KERNEL);
>  	for (i = 0; i < pidhash_size; i++)
>  		INIT_HLIST_HEAD(&pid_hash[i]);
>  }
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index e0f2cdf..202ef6b 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -4745,8 +4745,10 @@ void *__init alloc_large_system_hash(const char *tablename,
>  			 * some pages at the end of hash table which
>  			 * alloc_pages_exact() automatically does
>  			 */
> -			if (get_order(size) < MAX_ORDER)
> +			if (get_order(size) < MAX_ORDER) {
>  				table = alloc_pages_exact(size, GFP_ATOMIC);
> +				kmemleak_alloc(table, size, 1, GFP_ATOMIC);
> +			}
>  		}
>  	} while (!table && size > PAGE_SIZE && --log2qty);
>  
> @@ -4764,16 +4766,6 @@ void *__init alloc_large_system_hash(const char *tablename,
>  	if (_hash_mask)
>  		*_hash_mask = (1 << log2qty) - 1;
>  
> -	/*
> -	 * If hashdist is set, the table allocation is done with __vmalloc()
> -	 * which invokes the kmemleak_alloc() callback. This function may also
> -	 * be called before the slab and kmemleak are initialised when
> -	 * kmemleak simply buffers the request to be executed later
> -	 * (GFP_ATOMIC flag ignored in this case).
> -	 */
> -	if (!hashdist)
> -		kmemleak_alloc(table, size, 1, GFP_ATOMIC);
> -
>  	return table;
>  }
>  
> 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-07  7:12   ` Pekka Enberg
@ 2009-07-07  8:42     ` Catalin Marinas
  2009-07-07 13:39       ` Catalin Marinas
  0 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-07  8:42 UTC (permalink / raw)
  To: Pekka Enberg; +Cc: linux-mm, linux-kernel, Ingo Molnar, akpm

Pekka Enberg <penberg@cs.helsinki.fi> wrote:
> On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
>> @@ -552,8 +558,29 @@ static void delete_object(unsigned long ptr)
>>  	 */
>>  	spin_lock_irqsave(&object->lock, flags);
>>  	object->flags &= ~OBJECT_ALLOCATED;
>> +	start = object->pointer;
>> +	end = object->pointer + object->size;
>> +	min_count = object->min_count;
>>  	spin_unlock_irqrestore(&object->lock, flags);
>>  	put_object(object);
>> +
>> +	if (!size)
>> +		return;
>> +
>> +	/*
>> +	 * Partial freeing. Just create one or two objects that may result
>> +	 * from the memory block split.
>> +	 */
>> +	if (in_atomic())
>> +		gfp_flags = GFP_ATOMIC;
>> +	else
>> +		gfp_flags = GFP_KERNEL;
>
> Are you sure we can do this? There's a big fat comment on top of
> in_atomic() that suggest this is not safe.

It's not safe but I thought it's slightly better than not checking it.

> Why do we need to create the
> object here anyway and not in the _alloc_ paths where gfp flags are
> explicitly passed?

That's the free_bootmem case where Linux can only partially free a
block previously allocated with alloc_bootmem (that's why I haven't
tracked this from the beginning). So if it only frees some part in the
middle of a block, I would have to create two separate
kmemleak_objects (well, I can reuse one but I preferred fewer
modifications as this is not on a fast path anyway).

In the tests I did, free_bootmem is called before the slab allocator
is initialised and therefore before kmemleak is initialised, which
means that the requests are just logged and the kmemleak_* functions
are called later from the kmemleak_init() function. All allocations
via this function are fine to only use GFP_KERNEL.

If my reasoning above is correct, I'll only pass GFP_KERNEL and add a
comment in the code clarifying when the partial freeing happen.

Thanks.

-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-07  8:42     ` Catalin Marinas
@ 2009-07-07 13:39       ` Catalin Marinas
  2009-07-08  6:40         ` Pekka Enberg
  0 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-07 13:39 UTC (permalink / raw)
  To: Pekka Enberg; +Cc: linux-mm, linux-kernel, Ingo Molnar, akpm

Catalin Marinas <catalin.marinas@arm.com> wrote:
> Pekka Enberg <penberg@cs.helsinki.fi> wrote:
>> On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
>>> @@ -552,8 +558,29 @@ static void delete_object(unsigned long ptr)
>>>  	 */
>>>  	spin_lock_irqsave(&object->lock, flags);
>>>  	object->flags &= ~OBJECT_ALLOCATED;
>>> +	start = object->pointer;
>>> +	end = object->pointer + object->size;
>>> +	min_count = object->min_count;
>>>  	spin_unlock_irqrestore(&object->lock, flags);
>>>  	put_object(object);
>>> +
>>> +	if (!size)
>>> +		return;
>>> +
>>> +	/*
>>> +	 * Partial freeing. Just create one or two objects that may result
>>> +	 * from the memory block split.
>>> +	 */
>>> +	if (in_atomic())
>>> +		gfp_flags = GFP_ATOMIC;
>>> +	else
>>> +		gfp_flags = GFP_KERNEL;
>>
>> Are you sure we can do this? There's a big fat comment on top of
>> in_atomic() that suggest this is not safe.
[...]
> That's the free_bootmem case where Linux can only partially free a
> block previously allocated with alloc_bootmem (that's why I haven't
> tracked this from the beginning). So if it only frees some part in the
> middle of a block, I would have to create two separate
> kmemleak_objects (well, I can reuse one but I preferred fewer
> modifications as this is not on a fast path anyway).
>
> In the tests I did, free_bootmem is called before the slab allocator
> is initialised and therefore before kmemleak is initialised, which
> means that the requests are just logged and the kmemleak_* functions
> are called later from the kmemleak_init() function. All allocations
> via this function are fine to only use GFP_KERNEL.

Here's an updated patch:


kmemleak: Allow partial freeing of memory blocks

From: Catalin Marinas <catalin.marinas@arm.com>

Functions like free_bootmem() are allowed to free only part of a memory
block. This patch adds support for this via the kmemleak_free_part()
callback which removes the original object and creates one or two
additional objects as a result of the memory block split.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
---
 include/linux/kmemleak.h |    4 ++++
 mm/kmemleak.c            |   52 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index 7796aed..6a63807 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -27,6 +27,7 @@ extern void kmemleak_init(void);
 extern void kmemleak_alloc(const void *ptr, size_t size, int min_count,
 			   gfp_t gfp);
 extern void kmemleak_free(const void *ptr);
+extern void kmemleak_free_part(const void *ptr, size_t size);
 extern void kmemleak_padding(const void *ptr, unsigned long offset,
 			     size_t size);
 extern void kmemleak_not_leak(const void *ptr);
@@ -71,6 +72,9 @@ static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
 static inline void kmemleak_free(const void *ptr)
 {
 }
+static inline void kmemleak_free_part(const void *ptr, size_t size)
+{
+}
 static inline void kmemleak_free_recursive(const void *ptr, unsigned long flags)
 {
 }
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 5f7d8ae..8614255 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -210,6 +210,7 @@ static DEFINE_MUTEX(scan_mutex);
 enum {
 	KMEMLEAK_ALLOC,
 	KMEMLEAK_FREE,
+	KMEMLEAK_FREE_PART,
 	KMEMLEAK_NOT_LEAK,
 	KMEMLEAK_IGNORE,
 	KMEMLEAK_SCAN_AREA,
@@ -522,15 +523,19 @@ out:
 
 /*
  * Remove the metadata (struct kmemleak_object) for a memory block from the
- * object_list and object_tree_root and decrement its use_count.
+ * object_list and object_tree_root and decrement its use_count. If the memory
+ * block is partially freed (size > 0), the function may create additional
+ * metadata for the remaining parts of the block.
  */
-static void delete_object(unsigned long ptr)
+static void delete_object(unsigned long ptr, size_t size)
 {
 	unsigned long flags;
 	struct kmemleak_object *object;
+	unsigned long start, end;
+	unsigned int min_count;
 
 	write_lock_irqsave(&kmemleak_lock, flags);
-	object = lookup_object(ptr, 0);
+	object = lookup_object(ptr, 1);
 	if (!object) {
 #ifdef DEBUG
 		kmemleak_warn("Freeing unknown object at 0x%08lx\n",
@@ -552,8 +557,27 @@ static void delete_object(unsigned long ptr)
 	 */
 	spin_lock_irqsave(&object->lock, flags);
 	object->flags &= ~OBJECT_ALLOCATED;
+	start = object->pointer;
+	end = object->pointer + object->size;
+	min_count = object->min_count;
 	spin_unlock_irqrestore(&object->lock, flags);
 	put_object(object);
+
+	if (!size)
+		return;
+
+	/*
+	 * Partial freeing. Just create one or two objects that may result
+	 * from the memory block split. Note that partial freeing is only done
+	 * by free_bootmem() and this happens before kmemleak_init() is
+	 * called. The path below is only executed during early log recording
+	 * in kmemleak_init(), so GFP_KERNEL is enough.
+	 */
+	if (ptr > start)
+		create_object(start, ptr - start, min_count, GFP_KERNEL);
+	if (ptr + size < end)
+		create_object(ptr + size, end - ptr - size, min_count,
+			      GFP_KERNEL);
 }
 
 /*
@@ -720,13 +744,28 @@ void kmemleak_free(const void *ptr)
 	pr_debug("%s(0x%p)\n", __func__, ptr);
 
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
-		delete_object((unsigned long)ptr);
+		delete_object((unsigned long)ptr, 0);
 	else if (atomic_read(&kmemleak_early_log))
 		log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free);
 
 /*
+ * Partial memory freeing function callback. This function is usually called
+ * from bootmem allocator when (part of) a memory block is freed.
+ */
+void kmemleak_free_part(const void *ptr, size_t size)
+{
+	pr_debug("%s(0x%p)\n", __func__, ptr);
+
+	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+		delete_object((unsigned long)ptr, size);
+	else if (atomic_read(&kmemleak_early_log))
+		log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
+}
+EXPORT_SYMBOL_GPL(kmemleak_free_part);
+
+/*
  * Mark an already allocated memory block as a false positive. This will cause
  * the block to no longer be reported as leak and always be scanned.
  */
@@ -1345,7 +1384,7 @@ static int kmemleak_cleanup_thread(void *arg)
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(object, &object_list, object_list)
-		delete_object(object->pointer);
+		delete_object(object->pointer, 0);
 	rcu_read_unlock();
 	mutex_unlock(&scan_mutex);
 
@@ -1440,6 +1479,9 @@ void __init kmemleak_init(void)
 		case KMEMLEAK_FREE:
 			kmemleak_free(log->ptr);
 			break;
+		case KMEMLEAK_FREE_PART:
+			kmemleak_free_part(log->ptr, log->size);
+			break;
 		case KMEMLEAK_NOT_LEAK:
 			kmemleak_not_leak(log->ptr);
 			break;


-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-07  7:08   ` Pekka Enberg
@ 2009-07-07 16:53     ` Johannes Weiner
  2009-07-07 22:09       ` Catalin Marinas
  0 siblings, 1 reply; 21+ messages in thread
From: Johannes Weiner @ 2009-07-07 16:53 UTC (permalink / raw)
  To: Pekka Enberg; +Cc: Catalin Marinas, linux-mm, linux-kernel, Ingo Molnar

On Tue, Jul 07, 2009 at 10:08:50AM +0300, Pekka Enberg wrote:
> On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> > This patch adds kmemleak_alloc/free callbacks to the bootmem allocator.
> > This would allow scanning of such blocks and help avoiding a whole class
> > of false positives and more kmemleak annotations.
> > 
> > Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> > Cc: Ingo Molnar <mingo@elte.hu>
> > Cc: Pekka Enberg <penberg@cs.helsinki.fi>
> 
> Looks good to me!
> 
> Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
> 
> But lets cc Johannes on this too.

> > @@ -597,7 +601,9 @@ restart:
> >  void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
> >  					unsigned long goal)
> >  {
> > -	return ___alloc_bootmem_nopanic(size, align, goal, 0);
> > +	void *ptr =  ___alloc_bootmem_nopanic(size, align, goal, 0);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > +	return ptr;

You may get an object from kzalloc() here, I don't think you want to
track that (again), right?

Pekka already worked out all the central places to catch 'slab already
available' allocations, they can probably help you place the hooks.

> >  static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
> > @@ -631,7 +637,9 @@ static void * __init ___alloc_bootmem(unsigned long size, unsigned long align,
> >  void * __init __alloc_bootmem(unsigned long size, unsigned long align,
> >  			      unsigned long goal)
> >  {
> > -	return ___alloc_bootmem(size, align, goal, 0);
> > +	void *ptr = ___alloc_bootmem(size, align, goal, 0);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > +	return ptr;

Same here.

> >  #ifdef CONFIG_SPARSEMEM
> > @@ -707,14 +719,18 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
> >  		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
> >  
> >  	ptr = alloc_arch_preferred_bootmem(pgdat->bdata, size, align, goal, 0);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> >  	if (ptr)
> >  		return ptr;
> >  
> >  	ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> >  	if (ptr)
> >  		return ptr;
> >  
> > -	return __alloc_bootmem_nopanic(size, align, goal);
> > +	ptr = __alloc_bootmem_nopanic(size, align, goal);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > +	return ptr;
> >  }

Can you use a central exit and goto?

> >  #ifndef ARCH_LOW_ADDRESS_LIMIT
> > @@ -737,7 +753,9 @@ void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size,
> >  void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
> >  				  unsigned long goal)
> >  {
> > -	return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
> > +	void *ptr =  ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > +	return ptr;
> >  }

Possible slab object.

> > @@ -758,9 +776,13 @@ void * __init __alloc_bootmem_low(unsigned long size, unsigned long align,
> >  void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size,
> >  				       unsigned long align, unsigned long goal)
> >  {
> > +	void *ptr;
> > +
> >  	if (WARN_ON_ONCE(slab_is_available()))
> >  		return kzalloc_node(size, GFP_NOWAIT, pgdat->node_id);
> >  
> > -	return ___alloc_bootmem_node(pgdat->bdata, size, align,
> > -				goal, ARCH_LOW_ADDRESS_LIMIT);
> > +	ptr = ___alloc_bootmem_node(pgdat->bdata, size, align,
> > +				    goal, ARCH_LOW_ADDRESS_LIMIT);
> > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);

These GFP_KERNEL startled me.  We know for sure that this code runs in
earlylog mode only and gfp is unused, right?  Can you perhaps just
pass 0 for gfp instead?

	Hannes

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-07 16:53     ` Johannes Weiner
@ 2009-07-07 22:09       ` Catalin Marinas
  2009-07-08  6:48         ` Pekka Enberg
  2009-07-08  9:46         ` Johannes Weiner
  0 siblings, 2 replies; 21+ messages in thread
From: Catalin Marinas @ 2009-07-07 22:09 UTC (permalink / raw)
  To: Johannes Weiner; +Cc: Pekka Enberg, linux-mm, linux-kernel, Ingo Molnar

On Tue, 2009-07-07 at 18:53 +0200, Johannes Weiner wrote:
> On Tue, Jul 07, 2009 at 10:08:50AM +0300, Pekka Enberg wrote:
> > On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> > > @@ -597,7 +601,9 @@ restart:
> > >  void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
> > >  					unsigned long goal)
> > >  {
> > > -	return ___alloc_bootmem_nopanic(size, align, goal, 0);
> > > +	void *ptr =  ___alloc_bootmem_nopanic(size, align, goal, 0);
> > > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > > +	return ptr;
> 
> You may get an object from kzalloc() here, I don't think you want to
> track that (again), right?

You are write, I missed the alloc_arch_preferred_bootmem() function
which may call kzalloc().

> Pekka already worked out all the central places to catch 'slab already
> available' allocations, they can probably help you place the hooks.

It seems that alloc_bootmem_core() is central to all the bootmem
allocations. Is it OK to place the kmemleak_alloc hook only in this
function?

diff --git a/mm/bootmem.c b/mm/bootmem.c
index 5a649a0..74cbb34 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -520,6 +520,7 @@ find_block:
 		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
 				start_off);
 		memset(region, 0, size);
+		kmemleak_alloc(region, size, 1, 0);
 		return region;
 	}
 

> > > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> 
> These GFP_KERNEL startled me.  We know for sure that this code runs in
> earlylog mode only and gfp is unused, right?  Can you perhaps just
> pass 0 for gfp instead?

Yes, indeed.

Thanks for your comments.

-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-07 13:39       ` Catalin Marinas
@ 2009-07-08  6:40         ` Pekka Enberg
  2009-07-08  9:42           ` Catalin Marinas
  0 siblings, 1 reply; 21+ messages in thread
From: Pekka Enberg @ 2009-07-08  6:40 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: linux-mm, linux-kernel, Ingo Molnar, akpm

On Tue, 2009-07-07 at 14:39 +0100, Catalin Marinas wrote:
> @@ -552,8 +557,27 @@ static void delete_object(unsigned long ptr)
>  	 */
>  	spin_lock_irqsave(&object->lock, flags);
>  	object->flags &= ~OBJECT_ALLOCATED;
> +	start = object->pointer;
> +	end = object->pointer + object->size;
> +	min_count = object->min_count;
>  	spin_unlock_irqrestore(&object->lock, flags);
>  	put_object(object);
> +
> +	if (!size)
> +		return;
> +
> +	/*
> +	 * Partial freeing. Just create one or two objects that may result
> +	 * from the memory block split. Note that partial freeing is only done
> +	 * by free_bootmem() and this happens before kmemleak_init() is
> +	 * called. The path below is only executed during early log recording
> +	 * in kmemleak_init(), so GFP_KERNEL is enough.
> +	 */
> +	if (ptr > start)
> +		create_object(start, ptr - start, min_count, GFP_KERNEL);
> +	if (ptr + size < end)
> +		create_object(ptr + size, end - ptr - size, min_count,
> +			      GFP_KERNEL);
>  }

Looks good to me. I think it would be better to have
delete_object_full() and delete_object_part(), and extract the common
code to __delete_object() or something instead of passing the magic zero
from kmemleak_free().

In any case:

Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>

			Pekka

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-07 22:09       ` Catalin Marinas
@ 2009-07-08  6:48         ` Pekka Enberg
  2009-07-08  9:43           ` Catalin Marinas
  2009-07-08  9:46         ` Johannes Weiner
  1 sibling, 1 reply; 21+ messages in thread
From: Pekka Enberg @ 2009-07-08  6:48 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: Johannes Weiner, linux-mm, linux-kernel, Ingo Molnar

Hi Catalin,

On Tue, 2009-07-07 at 23:09 +0100, Catalin Marinas wrote:
> On Tue, 2009-07-07 at 18:53 +0200, Johannes Weiner wrote:
> > On Tue, Jul 07, 2009 at 10:08:50AM +0300, Pekka Enberg wrote:
> > > On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> > > > @@ -597,7 +601,9 @@ restart:
> > > >  void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
> > > >  					unsigned long goal)
> > > >  {
> > > > -	return ___alloc_bootmem_nopanic(size, align, goal, 0);
> > > > +	void *ptr =  ___alloc_bootmem_nopanic(size, align, goal, 0);
> > > > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > > > +	return ptr;
> > 
> > You may get an object from kzalloc() here, I don't think you want to
> > track that (again), right?
> 
> You are write, I missed the alloc_arch_preferred_bootmem() function
> which may call kzalloc().
> 
> > Pekka already worked out all the central places to catch 'slab already
> > available' allocations, they can probably help you place the hooks.
> 
> It seems that alloc_bootmem_core() is central to all the bootmem
> allocations. Is it OK to place the kmemleak_alloc hook only in this
> function?

I think so. Johannes?

> 
> diff --git a/mm/bootmem.c b/mm/bootmem.c
> index 5a649a0..74cbb34 100644
> --- a/mm/bootmem.c
> +++ b/mm/bootmem.c
> @@ -520,6 +520,7 @@ find_block:
>  		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
>  				start_off);
>  		memset(region, 0, size);
> +		kmemleak_alloc(region, size, 1, 0);
>  		return region;
>  	}


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-08  6:40         ` Pekka Enberg
@ 2009-07-08  9:42           ` Catalin Marinas
  2009-07-08  9:45             ` Pekka Enberg
  0 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-08  9:42 UTC (permalink / raw)
  To: Pekka Enberg; +Cc: linux-mm, linux-kernel, Ingo Molnar, akpm

On Wed, 2009-07-08 at 09:40 +0300, Pekka Enberg wrote:
> Looks good to me. I think it would be better to have
> delete_object_full() and delete_object_part(), and extract the common
> code to __delete_object() or something instead of passing the magic zero
> from kmemleak_free().

Yes, it make sense:


kmemleak: Allow partial freeing of memory blocks

From: Catalin Marinas <catalin.marinas@arm.com>

Functions like free_bootmem() are allowed to free only part of a memory
block. This patch adds support for this via the kmemleak_free_part()
callback which removes the original object and creates one or two
additional objects as a result of the memory block split.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
---
 include/linux/kmemleak.h |    4 ++
 mm/kmemleak.c            |   95 +++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 85 insertions(+), 14 deletions(-)

diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index 7796aed..6a63807 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -27,6 +27,7 @@ extern void kmemleak_init(void);
 extern void kmemleak_alloc(const void *ptr, size_t size, int min_count,
 			   gfp_t gfp);
 extern void kmemleak_free(const void *ptr);
+extern void kmemleak_free_part(const void *ptr, size_t size);
 extern void kmemleak_padding(const void *ptr, unsigned long offset,
 			     size_t size);
 extern void kmemleak_not_leak(const void *ptr);
@@ -71,6 +72,9 @@ static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
 static inline void kmemleak_free(const void *ptr)
 {
 }
+static inline void kmemleak_free_part(const void *ptr, size_t size)
+{
+}
 static inline void kmemleak_free_recursive(const void *ptr, unsigned long flags)
 {
 }
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 5f7d8ae..7b4647d 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -210,6 +210,7 @@ static DEFINE_MUTEX(scan_mutex);
 enum {
 	KMEMLEAK_ALLOC,
 	KMEMLEAK_FREE,
+	KMEMLEAK_FREE_PART,
 	KMEMLEAK_NOT_LEAK,
 	KMEMLEAK_IGNORE,
 	KMEMLEAK_SCAN_AREA,
@@ -524,27 +525,17 @@ out:
  * Remove the metadata (struct kmemleak_object) for a memory block from the
  * object_list and object_tree_root and decrement its use_count.
  */
-static void delete_object(unsigned long ptr)
+static void __delete_object(struct kmemleak_object *object)
 {
 	unsigned long flags;
-	struct kmemleak_object *object;
 
 	write_lock_irqsave(&kmemleak_lock, flags);
-	object = lookup_object(ptr, 0);
-	if (!object) {
-#ifdef DEBUG
-		kmemleak_warn("Freeing unknown object at 0x%08lx\n",
-			      ptr);
-#endif
-		write_unlock_irqrestore(&kmemleak_lock, flags);
-		return;
-	}
 	prio_tree_remove(&object_tree_root, &object->tree_node);
 	list_del_rcu(&object->object_list);
 	write_unlock_irqrestore(&kmemleak_lock, flags);
 
 	WARN_ON(!(object->flags & OBJECT_ALLOCATED));
-	WARN_ON(atomic_read(&object->use_count) < 1);
+	WARN_ON(atomic_read(&object->use_count) < 2);
 
 	/*
 	 * Locking here also ensures that the corresponding memory block
@@ -557,6 +548,64 @@ static void delete_object(unsigned long ptr)
 }
 
 /*
+ * Look up the metadata (struct kmemleak_object) corresponding to ptr and
+ * delete it.
+ */
+static void delete_object_full(unsigned long ptr)
+{
+	struct kmemleak_object *object;
+
+	object = find_and_get_object(ptr, 0);
+	if (!object) {
+#ifdef DEBUG
+		kmemleak_warn("Freeing unknown object at 0x%08lx\n",
+			      ptr);
+#endif
+		return;
+	}
+	__delete_object(object);
+	put_object(object);
+}
+
+/*
+ * Look up the metadata (struct kmemleak_object) corresponding to ptr and
+ * delete it. If the memory block is partially freed, the function may create
+ * additional metadata for the remaining parts of the block.
+ */
+static void delete_object_part(unsigned long ptr, size_t size)
+{
+	struct kmemleak_object *object;
+	unsigned long start, end;
+
+	object = find_and_get_object(ptr, 1);
+	if (!object) {
+#ifdef DEBUG
+		kmemleak_warn("Partially freeing unknown object at 0x%08lx "
+			      "(size %zu)\n", ptr, size);
+#endif
+		return;
+	}
+	__delete_object(object);
+
+	/*
+	 * Create one or two objects that may result from the memory block
+	 * split. Note that partial freeing is only done by free_bootmem() and
+	 * this happens before kmemleak_init() is called. The path below is
+	 * only executed during early log recording in kmemleak_init(), so
+	 * GFP_KERNEL is enough.
+	 */
+	start = object->pointer;
+	end = object->pointer + object->size;
+	if (ptr > start)
+		create_object(start, ptr - start, object->min_count,
+			      GFP_KERNEL);
+	if (ptr + size < end)
+		create_object(ptr + size, end - ptr - size, object->min_count,
+			      GFP_KERNEL);
+
+	put_object(object);
+}
+/*
  * Make a object permanently as gray-colored so that it can no longer be
  * reported as a leak. This is used in general to mark a false positive.
  */
@@ -720,13 +769,28 @@ void kmemleak_free(const void *ptr)
 	pr_debug("%s(0x%p)\n", __func__, ptr);
 
 	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
-		delete_object((unsigned long)ptr);
+		delete_object_full((unsigned long)ptr);
 	else if (atomic_read(&kmemleak_early_log))
 		log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
 }
 EXPORT_SYMBOL_GPL(kmemleak_free);
 
 /*
+ * Partial memory freeing function callback. This function is usually called
+ * from bootmem allocator when (part of) a memory block is freed.
+ */
+void kmemleak_free_part(const void *ptr, size_t size)
+{
+	pr_debug("%s(0x%p)\n", __func__, ptr);
+
+	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
+		delete_object_part((unsigned long)ptr, size);
+	else if (atomic_read(&kmemleak_early_log))
+		log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
+}
+EXPORT_SYMBOL_GPL(kmemleak_free_part);
+
+/*
  * Mark an already allocated memory block as a false positive. This will cause
  * the block to no longer be reported as leak and always be scanned.
  */
@@ -1345,7 +1409,7 @@ static int kmemleak_cleanup_thread(void *arg)
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(object, &object_list, object_list)
-		delete_object(object->pointer);
+		delete_object_full(object->pointer);
 	rcu_read_unlock();
 	mutex_unlock(&scan_mutex);
 
@@ -1440,6 +1504,9 @@ void __init kmemleak_init(void)
 		case KMEMLEAK_FREE:
 			kmemleak_free(log->ptr);
 			break;
+		case KMEMLEAK_FREE_PART:
+			kmemleak_free_part(log->ptr, log->size);
+			break;
 		case KMEMLEAK_NOT_LEAK:
 			kmemleak_not_leak(log->ptr);
 			break;


-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-08  6:48         ` Pekka Enberg
@ 2009-07-08  9:43           ` Catalin Marinas
  2009-07-08 11:46             ` Johannes Weiner
  0 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-08  9:43 UTC (permalink / raw)
  To: Pekka Enberg; +Cc: Johannes Weiner, linux-mm, linux-kernel, Ingo Molnar

On Wed, 2009-07-08 at 09:48 +0300, Pekka Enberg wrote:
> On Tue, 2009-07-07 at 23:09 +0100, Catalin Marinas wrote:
> > On Tue, 2009-07-07 at 18:53 +0200, Johannes Weiner wrote:
> > > On Tue, Jul 07, 2009 at 10:08:50AM +0300, Pekka Enberg wrote:
> > > > On Mon, 2009-07-06 at 11:51 +0100, Catalin Marinas wrote:
> > > > > @@ -597,7 +601,9 @@ restart:
> > > > >  void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align,
> > > > >  					unsigned long goal)
> > > > >  {
> > > > > -	return ___alloc_bootmem_nopanic(size, align, goal, 0);
> > > > > +	void *ptr =  ___alloc_bootmem_nopanic(size, align, goal, 0);
> > > > > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > > > > +	return ptr;
> > > 
> > > You may get an object from kzalloc() here, I don't think you want to
> > > track that (again), right?
> > 
> > You are write, I missed the alloc_arch_preferred_bootmem() function
> > which may call kzalloc().
> > 
> > > Pekka already worked out all the central places to catch 'slab already
> > > available' allocations, they can probably help you place the hooks.
> > 
> > It seems that alloc_bootmem_core() is central to all the bootmem
> > allocations. Is it OK to place the kmemleak_alloc hook only in this
> > function?
> 
> I think so. Johannes?

To get a better view, here's the complete patch:


kmemleak: Add callbacks to the bootmem allocator

From: Catalin Marinas <catalin.marinas@arm.com>

This patch adds kmemleak_alloc/free callbacks to the bootmem allocator.
This would allow scanning of such blocks and help avoiding a whole class
of false positives and more kmemleak annotations.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
---
 mm/bootmem.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/mm/bootmem.c b/mm/bootmem.c
index d2a9ce9..90f3ed0 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -335,6 +335,8 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
 {
 	unsigned long start, end;
 
+	kmemleak_free_part(__va(physaddr), size);
+
 	start = PFN_UP(physaddr);
 	end = PFN_DOWN(physaddr + size);
 
@@ -354,6 +356,8 @@ void __init free_bootmem(unsigned long addr, unsigned long size)
 {
 	unsigned long start, end;
 
+	kmemleak_free_part(__va(addr), size);
+
 	start = PFN_UP(addr);
 	end = PFN_DOWN(addr + size);
 
@@ -516,6 +520,7 @@ find_block:
 		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
 				start_off);
 		memset(region, 0, size);
+		kmemleak_alloc(region, size, 1, 0);
 		return region;
 	}
 

-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks
  2009-07-08  9:42           ` Catalin Marinas
@ 2009-07-08  9:45             ` Pekka Enberg
  0 siblings, 0 replies; 21+ messages in thread
From: Pekka Enberg @ 2009-07-08  9:45 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: linux-mm, linux-kernel, Ingo Molnar, akpm

On Wed, 2009-07-08 at 10:42 +0100, Catalin Marinas wrote:
> kmemleak: Allow partial freeing of memory blocks
> 
> From: Catalin Marinas <catalin.marinas@arm.com>
> 
> Functions like free_bootmem() are allowed to free only part of a memory
> block. This patch adds support for this via the kmemleak_free_part()
> callback which removes the original object and creates one or two
> additional objects as a result of the memory block split.
> 
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Pekka Enberg <penberg@cs.helsinki.fi>

Looks good to me!

Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>

> ---
>  include/linux/kmemleak.h |    4 ++
>  mm/kmemleak.c            |   95 +++++++++++++++++++++++++++++++++++++++-------
>  2 files changed, 85 insertions(+), 14 deletions(-)
> 
> diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
> index 7796aed..6a63807 100644
> --- a/include/linux/kmemleak.h
> +++ b/include/linux/kmemleak.h
> @@ -27,6 +27,7 @@ extern void kmemleak_init(void);
>  extern void kmemleak_alloc(const void *ptr, size_t size, int min_count,
>  			   gfp_t gfp);
>  extern void kmemleak_free(const void *ptr);
> +extern void kmemleak_free_part(const void *ptr, size_t size);
>  extern void kmemleak_padding(const void *ptr, unsigned long offset,
>  			     size_t size);
>  extern void kmemleak_not_leak(const void *ptr);
> @@ -71,6 +72,9 @@ static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
>  static inline void kmemleak_free(const void *ptr)
>  {
>  }
> +static inline void kmemleak_free_part(const void *ptr, size_t size)
> +{
> +}
>  static inline void kmemleak_free_recursive(const void *ptr, unsigned long flags)
>  {
>  }
> diff --git a/mm/kmemleak.c b/mm/kmemleak.c
> index 5f7d8ae..7b4647d 100644
> --- a/mm/kmemleak.c
> +++ b/mm/kmemleak.c
> @@ -210,6 +210,7 @@ static DEFINE_MUTEX(scan_mutex);
>  enum {
>  	KMEMLEAK_ALLOC,
>  	KMEMLEAK_FREE,
> +	KMEMLEAK_FREE_PART,
>  	KMEMLEAK_NOT_LEAK,
>  	KMEMLEAK_IGNORE,
>  	KMEMLEAK_SCAN_AREA,
> @@ -524,27 +525,17 @@ out:
>   * Remove the metadata (struct kmemleak_object) for a memory block from the
>   * object_list and object_tree_root and decrement its use_count.
>   */
> -static void delete_object(unsigned long ptr)
> +static void __delete_object(struct kmemleak_object *object)
>  {
>  	unsigned long flags;
> -	struct kmemleak_object *object;
>  
>  	write_lock_irqsave(&kmemleak_lock, flags);
> -	object = lookup_object(ptr, 0);
> -	if (!object) {
> -#ifdef DEBUG
> -		kmemleak_warn("Freeing unknown object at 0x%08lx\n",
> -			      ptr);
> -#endif
> -		write_unlock_irqrestore(&kmemleak_lock, flags);
> -		return;
> -	}
>  	prio_tree_remove(&object_tree_root, &object->tree_node);
>  	list_del_rcu(&object->object_list);
>  	write_unlock_irqrestore(&kmemleak_lock, flags);
>  
>  	WARN_ON(!(object->flags & OBJECT_ALLOCATED));
> -	WARN_ON(atomic_read(&object->use_count) < 1);
> +	WARN_ON(atomic_read(&object->use_count) < 2);
>  
>  	/*
>  	 * Locking here also ensures that the corresponding memory block
> @@ -557,6 +548,64 @@ static void delete_object(unsigned long ptr)
>  }
>  
>  /*
> + * Look up the metadata (struct kmemleak_object) corresponding to ptr and
> + * delete it.
> + */
> +static void delete_object_full(unsigned long ptr)
> +{
> +	struct kmemleak_object *object;
> +
> +	object = find_and_get_object(ptr, 0);
> +	if (!object) {
> +#ifdef DEBUG
> +		kmemleak_warn("Freeing unknown object at 0x%08lx\n",
> +			      ptr);
> +#endif
> +		return;
> +	}
> +	__delete_object(object);
> +	put_object(object);
> +}
> +
> +/*
> + * Look up the metadata (struct kmemleak_object) corresponding to ptr and
> + * delete it. If the memory block is partially freed, the function may create
> + * additional metadata for the remaining parts of the block.
> + */
> +static void delete_object_part(unsigned long ptr, size_t size)
> +{
> +	struct kmemleak_object *object;
> +	unsigned long start, end;
> +
> +	object = find_and_get_object(ptr, 1);
> +	if (!object) {
> +#ifdef DEBUG
> +		kmemleak_warn("Partially freeing unknown object at 0x%08lx "
> +			      "(size %zu)\n", ptr, size);
> +#endif
> +		return;
> +	}
> +	__delete_object(object);
> +
> +	/*
> +	 * Create one or two objects that may result from the memory block
> +	 * split. Note that partial freeing is only done by free_bootmem() and
> +	 * this happens before kmemleak_init() is called. The path below is
> +	 * only executed during early log recording in kmemleak_init(), so
> +	 * GFP_KERNEL is enough.
> +	 */
> +	start = object->pointer;
> +	end = object->pointer + object->size;
> +	if (ptr > start)
> +		create_object(start, ptr - start, object->min_count,
> +			      GFP_KERNEL);
> +	if (ptr + size < end)
> +		create_object(ptr + size, end - ptr - size, object->min_count,
> +			      GFP_KERNEL);
> +
> +	put_object(object);
> +}
> +/*
>   * Make a object permanently as gray-colored so that it can no longer be
>   * reported as a leak. This is used in general to mark a false positive.
>   */
> @@ -720,13 +769,28 @@ void kmemleak_free(const void *ptr)
>  	pr_debug("%s(0x%p)\n", __func__, ptr);
>  
>  	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
> -		delete_object((unsigned long)ptr);
> +		delete_object_full((unsigned long)ptr);
>  	else if (atomic_read(&kmemleak_early_log))
>  		log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
>  }
>  EXPORT_SYMBOL_GPL(kmemleak_free);
>  
>  /*
> + * Partial memory freeing function callback. This function is usually called
> + * from bootmem allocator when (part of) a memory block is freed.
> + */
> +void kmemleak_free_part(const void *ptr, size_t size)
> +{
> +	pr_debug("%s(0x%p)\n", __func__, ptr);
> +
> +	if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
> +		delete_object_part((unsigned long)ptr, size);
> +	else if (atomic_read(&kmemleak_early_log))
> +		log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
> +}
> +EXPORT_SYMBOL_GPL(kmemleak_free_part);
> +
> +/*
>   * Mark an already allocated memory block as a false positive. This will cause
>   * the block to no longer be reported as leak and always be scanned.
>   */
> @@ -1345,7 +1409,7 @@ static int kmemleak_cleanup_thread(void *arg)
>  
>  	rcu_read_lock();
>  	list_for_each_entry_rcu(object, &object_list, object_list)
> -		delete_object(object->pointer);
> +		delete_object_full(object->pointer);
>  	rcu_read_unlock();
>  	mutex_unlock(&scan_mutex);
>  
> @@ -1440,6 +1504,9 @@ void __init kmemleak_init(void)
>  		case KMEMLEAK_FREE:
>  			kmemleak_free(log->ptr);
>  			break;
> +		case KMEMLEAK_FREE_PART:
> +			kmemleak_free_part(log->ptr, log->size);
> +			break;
>  		case KMEMLEAK_NOT_LEAK:
>  			kmemleak_not_leak(log->ptr);
>  			break;
> 
> 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-07 22:09       ` Catalin Marinas
  2009-07-08  6:48         ` Pekka Enberg
@ 2009-07-08  9:46         ` Johannes Weiner
  2009-07-08 10:02           ` Catalin Marinas
  1 sibling, 1 reply; 21+ messages in thread
From: Johannes Weiner @ 2009-07-08  9:46 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: Pekka Enberg, linux-mm, linux-kernel, Ingo Molnar

On Tue, Jul 07, 2009 at 11:09:46PM +0100, Catalin Marinas wrote:

> It seems that alloc_bootmem_core() is central to all the bootmem
> allocations. Is it OK to place the kmemleak_alloc hook only in this
> function?
> 
> diff --git a/mm/bootmem.c b/mm/bootmem.c
> index 5a649a0..74cbb34 100644
> --- a/mm/bootmem.c
> +++ b/mm/bootmem.c
> @@ -520,6 +520,7 @@ find_block:
>  		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
>  				start_off);
>  		memset(region, 0, size);
> +		kmemleak_alloc(region, size, 1, 0);
>  		return region;
>  	}

Yes, that should work.

> > > > +	kmemleak_alloc(ptr, size, 1, GFP_KERNEL);
> > 
> > These GFP_KERNEL startled me.  We know for sure that this code runs in
> > earlylog mode only and gfp is unused, right?  Can you perhaps just
> > pass 0 for gfp instead?
> 
> Yes, indeed.

Thank you.

	Hannes

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-08  9:46         ` Johannes Weiner
@ 2009-07-08 10:02           ` Catalin Marinas
  2009-07-08 10:03             ` Pekka Enberg
  0 siblings, 1 reply; 21+ messages in thread
From: Catalin Marinas @ 2009-07-08 10:02 UTC (permalink / raw)
  To: Johannes Weiner; +Cc: Pekka Enberg, linux-mm, linux-kernel, Ingo Molnar

On Wed, 2009-07-08 at 11:46 +0200, Johannes Weiner wrote:
> On Tue, Jul 07, 2009 at 11:09:46PM +0100, Catalin Marinas wrote:
> 
> > It seems that alloc_bootmem_core() is central to all the bootmem
> > allocations. Is it OK to place the kmemleak_alloc hook only in this
> > function?
> > 
> > diff --git a/mm/bootmem.c b/mm/bootmem.c
> > index 5a649a0..74cbb34 100644
> > --- a/mm/bootmem.c
> > +++ b/mm/bootmem.c
> > @@ -520,6 +520,7 @@ find_block:
> >  		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
> >  				start_off);
> >  		memset(region, 0, size);
> > +		kmemleak_alloc(region, size, 1, 0);
> >  		return region;
> >  	}
> 
> Yes, that should work.

Thanks. May I add your Acked-by line to the final patch?

-- 
Catalin

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-08 10:02           ` Catalin Marinas
@ 2009-07-08 10:03             ` Pekka Enberg
  0 siblings, 0 replies; 21+ messages in thread
From: Pekka Enberg @ 2009-07-08 10:03 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: Johannes Weiner, linux-mm, linux-kernel, Ingo Molnar

On Wed, 2009-07-08 at 11:02 +0100, Catalin Marinas wrote:
> On Wed, 2009-07-08 at 11:46 +0200, Johannes Weiner wrote:
> > On Tue, Jul 07, 2009 at 11:09:46PM +0100, Catalin Marinas wrote:
> > 
> > > It seems that alloc_bootmem_core() is central to all the bootmem
> > > allocations. Is it OK to place the kmemleak_alloc hook only in this
> > > function?
> > > 
> > > diff --git a/mm/bootmem.c b/mm/bootmem.c
> > > index 5a649a0..74cbb34 100644
> > > --- a/mm/bootmem.c
> > > +++ b/mm/bootmem.c
> > > @@ -520,6 +520,7 @@ find_block:
> > >  		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
> > >  				start_off);
> > >  		memset(region, 0, size);
> > > +		kmemleak_alloc(region, size, 1, 0);
> > >  		return region;
> > >  	}
> > 
> > Yes, that should work.
> 
> Thanks. May I add your Acked-by line to the final patch?

I guess you mean Johannes but while we're at it:

Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator
  2009-07-08  9:43           ` Catalin Marinas
@ 2009-07-08 11:46             ` Johannes Weiner
  0 siblings, 0 replies; 21+ messages in thread
From: Johannes Weiner @ 2009-07-08 11:46 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: Pekka Enberg, linux-mm, linux-kernel, Ingo Molnar

On Wed, Jul 08, 2009 at 10:43:51AM +0100, Catalin Marinas wrote:

> kmemleak: Add callbacks to the bootmem allocator
> 
> From: Catalin Marinas <catalin.marinas@arm.com>
> 
> This patch adds kmemleak_alloc/free callbacks to the bootmem allocator.
> This would allow scanning of such blocks and help avoiding a whole class
> of false positives and more kmemleak annotations.
> 
> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Pekka Enberg <penberg@cs.helsinki.fi>

Reviewed-by: Johannes Weiner <hannes@cmpxchg.org>

> ---
>  mm/bootmem.c |    5 +++++
>  1 files changed, 5 insertions(+), 0 deletions(-)
> 
> diff --git a/mm/bootmem.c b/mm/bootmem.c
> index d2a9ce9..90f3ed0 100644
> --- a/mm/bootmem.c
> +++ b/mm/bootmem.c
> @@ -335,6 +335,8 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
>  {
>  	unsigned long start, end;
>  
> +	kmemleak_free_part(__va(physaddr), size);
> +
>  	start = PFN_UP(physaddr);
>  	end = PFN_DOWN(physaddr + size);
>  
> @@ -354,6 +356,8 @@ void __init free_bootmem(unsigned long addr, unsigned long size)
>  {
>  	unsigned long start, end;
>  
> +	kmemleak_free_part(__va(addr), size);
> +
>  	start = PFN_UP(addr);
>  	end = PFN_DOWN(addr + size);
>  
> @@ -516,6 +520,7 @@ find_block:
>  		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) +
>  				start_off);
>  		memset(region, 0, size);
> +		kmemleak_alloc(region, size, 1, 0);
>  		return region;
>  	}

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2009-07-08 11:38 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-07-06 10:51 [RFC PATCH 0/3] kmemleak: Add support for the bootmem allocator Catalin Marinas
2009-07-06 10:51 ` [RFC PATCH 1/3] kmemleak: Allow partial freeing of memory blocks Catalin Marinas
2009-07-07  7:12   ` Pekka Enberg
2009-07-07  8:42     ` Catalin Marinas
2009-07-07 13:39       ` Catalin Marinas
2009-07-08  6:40         ` Pekka Enberg
2009-07-08  9:42           ` Catalin Marinas
2009-07-08  9:45             ` Pekka Enberg
2009-07-06 10:51 ` [RFC PATCH 2/3] kmemleak: Add callbacks to the bootmem allocator Catalin Marinas
2009-07-06 10:58   ` Catalin Marinas
2009-07-07  7:08   ` Pekka Enberg
2009-07-07 16:53     ` Johannes Weiner
2009-07-07 22:09       ` Catalin Marinas
2009-07-08  6:48         ` Pekka Enberg
2009-07-08  9:43           ` Catalin Marinas
2009-07-08 11:46             ` Johannes Weiner
2009-07-08  9:46         ` Johannes Weiner
2009-07-08 10:02           ` Catalin Marinas
2009-07-08 10:03             ` Pekka Enberg
2009-07-06 10:52 ` [RFC PATCH 3/3] kmemleak: Remove alloc_bootmem annotations introduced in the past Catalin Marinas
2009-07-07  7:12   ` Pekka Enberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox