Bisected: Massive memory leak in dm-snapshot in 3.1 development introduced
Subsystems (4)
Hi,
How to reproduce:
lvcreate -L30G -n testsnap vg1 # of course substitute VG as appropriate dd if=/dev/zero of=/dev/vg1/testsnap bs=2M # to make things clear lvcreate -L15G -s /dev/vg1/test -n testsnap
dd if=/dev/zero of=/dev/vg1/testsnap bs=2M &
watch free
I noticed roughly 1GB memory vanishing from free / -/+ buffers/cache per 2GB copied.
Bisecting yielded a6e50b409d3f9e0833e69c3c9cca822e8fa4adbb, reverting that from git master cures the memory leak
Signature · 3 lines
MfG, Michael Leun
Making sure that Alasdair sees this too (hopefully he picked up on it from dm-devel, but best send things directly too)
Mikulas, Alasdair? I see the mempool_free() for the "master job", what about everything else? Does the dm_kcopyd_prepare_callback() perhaps need to do a
job->master_job = job;
or similar?
Linus
Quoted 22 lines · Michael Leun
Hi,
How to reproduce:
lvcreate -L30G -n testsnap vg1 # of course substitute VG as appropriate dd if=/dev/zero of=/dev/vg1/testsnap bs=2M # to make things clear lvcreate -L15G -s
/dev/vg1/test-n testsnap
dd if=/dev/zero of=/dev/vg1/testsnap bs=2M &
watch freeI noticed roughly 1GB memory vanishing from free / -/+ buffers/cache per 2GB copied.
Bisecting yielded
a6e50b409d3f9e0833e69c3c9cca822e8fa4adbb, reverting that from git master cures the memory leak--
MfG,Michael Leun
Alasdair G Kergon
maintainerQuoted 7 lines · Linus Torvalds
Mikulas, Alasdair? I see the
mempool_free()for the "master job", what about everything else? Does thedm_kcopyd_prepare_callback()perhaps need to do ajob->master_job = job;or similar?
If you look at the other place that performs that mempool_alloc, it's indeed present there.
Michael - can you test if this solves the problem?
Alasdair
drivers/md/dm-kcopyd.c +1 -0
@@ -628,6 +628,7 @@ void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, job->kc = kc; job->fn = fn; job->context = context; + job->master_job = job;atomic_inc (&kc->nr_jobs);
Quoted 20 lines · On Sun, 23 Oct 2011 14:44:49 +0100 Alasdair G Kergon <[email protected]> wrote:
Quoted 7 lines · Linus Torvalds
Mikulas, Alasdair? I see the
mempool_free()for the "master job", what about everything else? Does thedm_kcopyd_prepare_callback()perhaps need to do ajob->master_job = job;or similar?
If you look at the other place that performs that
mempool_alloc, it's indeed present there.Michael - can you test if this solves the problem?
Alasdair
drivers/md/dm-kcopyd.c void *dm_kcopyd_prepare_callback(struct · line 634
Quoted 7 lines
drivers/md/dm-kcopyd.c +1
-0@@ -628,6 +628,7 @@ void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc, job->kc = kc; job->fn = fn; job->context = context; + job->master_job = job;atomic_inc (&kc->nr_jobs);
Yup, it indeed does.
Signature · 3 lines
MfG, Michael Leun
Alasdair G Kergon
maintainerQuoted 3 lines · Michael Leun
Quoted 1 line · On Sun, 23 Oct 2011 14:44:49 +0100 Alasdair G Kergon <[email protected]> wrote:
Michael - can you test if this solves the problem?
OK, let's go with this patch then.
Linus, you can pull it from here:
http://people.redhat.com/agk/git/linux-dm for-linus
Alasdair G Kergon (1):
dm kcopyd: fix job_pool leak
drivers/md/dm-kcopyd.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
Mikulas Patocka
maintainerPATCH 1/1 check for mempool memory leaks
In reply to Linus TorvaldsQuoted 7 lines · Linus Torvalds
Making sure that Alasdair sees this too (hopefully he picked up on it from dm-devel, but best send things directly too)
Mikulas, Alasdair? I see the
mempool_free()for the "master job", what about everything else? Does thedm_kcopyd_prepare_callback()perhaps need to do a
Quoted 30 lines
job->master_job = job;or similar?
Linus
Quoted 22 lines · Michael Leun
Hi,
How to reproduce:
lvcreate -L30G -n testsnap vg1 # of course substitute VG as appropriate dd if=/dev/zero of=/dev/vg1/testsnap bs=2M # to make things clear lvcreate -L15G -s
/dev/vg1/test-n testsnap
dd if=/dev/zero of=/dev/vg1/testsnap bs=2M &
watch freeI noticed roughly 1GB memory vanishing from free / -/+ buffers/cache per 2GB copied.
Bisecting yielded
a6e50b409d3f9e0833e69c3c9cca822e8fa4adbb, reverting that from git master cures the memory leak--
MfG,Michael Leun
BTW. would you like to accept this patch that enables tracking the number of objects allocated in a mempool and reporting a warning? So that such bugs could be automatically detected in the future?
---
Introduce an option DEBUG_MEMPOOL. It will check that a mempool is empty when destroying the mempool.
--- include/linux/mempool.h | 6 ++++++ lib/Kconfig.debug | 8 ++++++++ mm/mempool.c | 25 +++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-)
lib/Kconfig.debug +8 -0
@@ -411,6 +411,14 @@ config SLUB_STATS out which slabs are relevant to a particular load. Try running: slabinfo -DA +config DEBUG_MEMPOOL + bool "Check memory leaks in mempools" + depends on DEBUG_KERNEL + help + Enable debugging memory leaks in mempools. This options makes + the kernel count the number of objects allocated in a mempool + and check if this number is zero when the mempool is destroyed. + config DEBUG_KMEMLEAK bool "Kernel memory leak detector" depends on DEBUG_KERNEL && EXPERIMENTAL && !MEMORY_HOTPLUG && \
mm/mempool.c +23 -2
@@ -71,6 +71,9 @@ mempool_t *mempool_create_node(int min_nkfree (pool);return NULL ; } +#ifdef CONFIG_DEBUG_MEMPOOL +atomic_long_set (&pool->nr_allocated,0 ); +#endif spin_lock_init (&pool->lock); pool->min_nr = min_nr; pool->pool_data = pool_data;
@@ -183,7 +186,13 @@ EXPORT_SYMBOL(mempool_resize);void mempool_destroy (mempool_t *pool) {/* Check for outstanding elements */ -BUG_ON (pool->curr_nr != pool->min_nr); +#ifdef CONFIG_DEBUG_MEMPOOL +WARN (atomic_long_read (&pool->nr_allocated), +"%ld objects leaked in mempool" , +atomic_long_read (&pool->nr_allocated)); +#else +WARN_ON (pool->curr_nr != pool->min_nr); +#endif free_pool (pool); }EXPORT_SYMBOL (mempool_destroy);
@@ -217,13 +226,20 @@ void * mempool_alloc(mempool_t *pool, gf repeat_alloc: element = pool->alloc (gfp_temp, pool->pool_data); -if (likely (element !=NULL )) +if (likely (element !=NULL )) { +#ifdef CONFIG_DEBUG_MEMPOOL +atomic_long_inc (&pool->nr_allocated); +#endif return element; + }spin_lock_irqsave (&pool->lock, flags);if (likely (pool->curr_nr)) { element =remove_element (pool);spin_unlock_irqrestore (&pool->lock, flags); +#ifdef CONFIG_DEBUG_MEMPOOL +atomic_long_inc (&pool->nr_allocated); +#endif return element; }spin_unlock_irqrestore (&pool->lock, flags);
@@ -265,6 +281,11 @@ void mempool_free(void *element, mempoolif (unlikely (element ==NULL ))return ; +#ifdef CONFIG_DEBUG_MEMPOOL +BUG_ON (!atomic_long_read (&pool->nr_allocated)); +atomic_long_dec (&pool->nr_allocated); +#endif +smp_mb ();if (pool->curr_nr < pool->min_nr) {spin_lock_irqsave (&pool->lock, flags);
include/linux/mempool.h +6 -0
@@ -5,6 +5,9 @@#define _LINUX_MEMPOOL_H#include <linux/wait.h> +#ifdef CONFIG_DEBUG_MEMPOOLS +#include <linux/atomic.h> +#endif struct kmem_cache ;
@@ -12,6 +15,9 @@ typedef void * (mempool_alloc_t)(gfp_t gtypedef void (mempool_free_t )(void *element,void *pool_data); typedef struct mempool_s { +#ifdef CONFIG_DEBUG_MEMPOOL + atomic_long_t nr_allocated; +#endif spinlock_t lock; int min_nr; /* nr of elements at *elements */ int curr_nr; /* Current nr of elements at *elements */