btrfs: move leak debug code to functions

Clean up the leak debugging in extent_io.c by moving
the debug code into functions.  This also removes the
list_heads used for debugging from the extent_buffer
and extent_state structures when debug is not enabled.

Since we need a global debug config to do that last
part, implement CONFIG_BTRFS_DEBUG to accommodate.

Thanks to Dave Sterba for the Kconfig bit.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
This commit is contained in:
Eric Sandeen 2013-04-22 16:12:31 +00:00 committed by Josef Bacik
parent ace68bac61
commit 6d49ba1b47
3 changed files with 72 additions and 56 deletions

View file

@ -63,3 +63,12 @@ config BTRFS_FS_RUN_SANITY_TESTS
If unsure, say N. If unsure, say N.
config BTRFS_DEBUG
bool "Btrfs debugging support"
depends on BTRFS_FS
help
Enable run-time debugging support for the btrfs filesystem. This may
enable additional and expensive checks with negative impact on
performance, or export extra information via sysfs.
If unsure, say N.

View file

@ -24,12 +24,62 @@
static struct kmem_cache *extent_state_cache; static struct kmem_cache *extent_state_cache;
static struct kmem_cache *extent_buffer_cache; static struct kmem_cache *extent_buffer_cache;
#ifdef CONFIG_BTRFS_DEBUG
static LIST_HEAD(buffers); static LIST_HEAD(buffers);
static LIST_HEAD(states); static LIST_HEAD(states);
#define LEAK_DEBUG 0
#if LEAK_DEBUG
static DEFINE_SPINLOCK(leak_lock); static DEFINE_SPINLOCK(leak_lock);
static inline
void btrfs_leak_debug_add(struct list_head *new, struct list_head *head)
{
unsigned long flags;
spin_lock_irqsave(&leak_lock, flags);
list_add(new, head);
spin_unlock_irqrestore(&leak_lock, flags);
}
static inline
void btrfs_leak_debug_del(struct list_head *entry)
{
unsigned long flags;
spin_lock_irqsave(&leak_lock, flags);
list_del(entry);
spin_unlock_irqrestore(&leak_lock, flags);
}
static inline
void btrfs_leak_debug_check(void)
{
struct extent_state *state;
struct extent_buffer *eb;
while (!list_empty(&states)) {
state = list_entry(states.next, struct extent_state, leak_list);
printk(KERN_ERR "btrfs state leak: start %llu end %llu "
"state %lu in tree %p refs %d\n",
(unsigned long long)state->start,
(unsigned long long)state->end,
state->state, state->tree, atomic_read(&state->refs));
list_del(&state->leak_list);
kmem_cache_free(extent_state_cache, state);
}
while (!list_empty(&buffers)) {
eb = list_entry(buffers.next, struct extent_buffer, leak_list);
printk(KERN_ERR "btrfs buffer leak start %llu len %lu "
"refs %d\n", (unsigned long long)eb->start,
eb->len, atomic_read(&eb->refs));
list_del(&eb->leak_list);
kmem_cache_free(extent_buffer_cache, eb);
}
}
#else
#define btrfs_leak_debug_add(new, head) do {} while (0)
#define btrfs_leak_debug_del(entry) do {} while (0)
#define btrfs_leak_debug_check() do {} while (0)
#endif #endif
#define BUFFER_LRU_MAX 64 #define BUFFER_LRU_MAX 64
@ -84,29 +134,7 @@ int __init extent_io_init(void)
void extent_io_exit(void) void extent_io_exit(void)
{ {
struct extent_state *state; btrfs_leak_debug_check();
struct extent_buffer *eb;
while (!list_empty(&states)) {
state = list_entry(states.next, struct extent_state, leak_list);
printk(KERN_ERR "btrfs state leak: start %llu end %llu "
"state %lu in tree %p refs %d\n",
(unsigned long long)state->start,
(unsigned long long)state->end,
state->state, state->tree, atomic_read(&state->refs));
list_del(&state->leak_list);
kmem_cache_free(extent_state_cache, state);
}
while (!list_empty(&buffers)) {
eb = list_entry(buffers.next, struct extent_buffer, leak_list);
printk(KERN_ERR "btrfs buffer leak start %llu len %lu "
"refs %d\n", (unsigned long long)eb->start,
eb->len, atomic_read(&eb->refs));
list_del(&eb->leak_list);
kmem_cache_free(extent_buffer_cache, eb);
}
/* /*
* Make sure all delayed rcu free are flushed before we * Make sure all delayed rcu free are flushed before we
@ -134,9 +162,6 @@ void extent_io_tree_init(struct extent_io_tree *tree,
static struct extent_state *alloc_extent_state(gfp_t mask) static struct extent_state *alloc_extent_state(gfp_t mask)
{ {
struct extent_state *state; struct extent_state *state;
#if LEAK_DEBUG
unsigned long flags;
#endif
state = kmem_cache_alloc(extent_state_cache, mask); state = kmem_cache_alloc(extent_state_cache, mask);
if (!state) if (!state)
@ -144,11 +169,7 @@ static struct extent_state *alloc_extent_state(gfp_t mask)
state->state = 0; state->state = 0;
state->private = 0; state->private = 0;
state->tree = NULL; state->tree = NULL;
#if LEAK_DEBUG btrfs_leak_debug_add(&state->leak_list, &states);
spin_lock_irqsave(&leak_lock, flags);
list_add(&state->leak_list, &states);
spin_unlock_irqrestore(&leak_lock, flags);
#endif
atomic_set(&state->refs, 1); atomic_set(&state->refs, 1);
init_waitqueue_head(&state->wq); init_waitqueue_head(&state->wq);
trace_alloc_extent_state(state, mask, _RET_IP_); trace_alloc_extent_state(state, mask, _RET_IP_);
@ -160,15 +181,8 @@ void free_extent_state(struct extent_state *state)
if (!state) if (!state)
return; return;
if (atomic_dec_and_test(&state->refs)) { if (atomic_dec_and_test(&state->refs)) {
#if LEAK_DEBUG
unsigned long flags;
#endif
WARN_ON(state->tree); WARN_ON(state->tree);
#if LEAK_DEBUG btrfs_leak_debug_del(&state->leak_list);
spin_lock_irqsave(&leak_lock, flags);
list_del(&state->leak_list);
spin_unlock_irqrestore(&leak_lock, flags);
#endif
trace_free_extent_state(state, _RET_IP_); trace_free_extent_state(state, _RET_IP_);
kmem_cache_free(extent_state_cache, state); kmem_cache_free(extent_state_cache, state);
} }
@ -4065,12 +4079,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
static void __free_extent_buffer(struct extent_buffer *eb) static void __free_extent_buffer(struct extent_buffer *eb)
{ {
#if LEAK_DEBUG btrfs_leak_debug_del(&eb->leak_list);
unsigned long flags;
spin_lock_irqsave(&leak_lock, flags);
list_del(&eb->leak_list);
spin_unlock_irqrestore(&leak_lock, flags);
#endif
kmem_cache_free(extent_buffer_cache, eb); kmem_cache_free(extent_buffer_cache, eb);
} }
@ -4080,9 +4089,6 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
gfp_t mask) gfp_t mask)
{ {
struct extent_buffer *eb = NULL; struct extent_buffer *eb = NULL;
#if LEAK_DEBUG
unsigned long flags;
#endif
eb = kmem_cache_zalloc(extent_buffer_cache, mask); eb = kmem_cache_zalloc(extent_buffer_cache, mask);
if (eb == NULL) if (eb == NULL)
@ -4102,11 +4108,8 @@ static struct extent_buffer *__alloc_extent_buffer(struct extent_io_tree *tree,
init_waitqueue_head(&eb->write_lock_wq); init_waitqueue_head(&eb->write_lock_wq);
init_waitqueue_head(&eb->read_lock_wq); init_waitqueue_head(&eb->read_lock_wq);
#if LEAK_DEBUG btrfs_leak_debug_add(&eb->leak_list, &buffers);
spin_lock_irqsave(&leak_lock, flags);
list_add(&eb->leak_list, &buffers);
spin_unlock_irqrestore(&leak_lock, flags);
#endif
spin_lock_init(&eb->refs_lock); spin_lock_init(&eb->refs_lock);
atomic_set(&eb->refs, 1); atomic_set(&eb->refs, 1);
atomic_set(&eb->io_pages, 0); atomic_set(&eb->io_pages, 0);

View file

@ -116,7 +116,9 @@ struct extent_state {
/* for use by the FS */ /* for use by the FS */
u64 private; u64 private;
#ifdef CONFIG_BTRFS_DEBUG
struct list_head leak_list; struct list_head leak_list;
#endif
}; };
#define INLINE_EXTENT_BUFFER_PAGES 16 #define INLINE_EXTENT_BUFFER_PAGES 16
@ -132,7 +134,6 @@ struct extent_buffer {
atomic_t refs; atomic_t refs;
atomic_t io_pages; atomic_t io_pages;
int read_mirror; int read_mirror;
struct list_head leak_list;
struct rcu_head rcu_head; struct rcu_head rcu_head;
pid_t lock_owner; pid_t lock_owner;
@ -159,6 +160,9 @@ struct extent_buffer {
wait_queue_head_t read_lock_wq; wait_queue_head_t read_lock_wq;
wait_queue_head_t lock_wq; wait_queue_head_t lock_wq;
struct page *pages[INLINE_EXTENT_BUFFER_PAGES]; struct page *pages[INLINE_EXTENT_BUFFER_PAGES];
#ifdef CONFIG_BTRFS_DEBUG
struct list_head leak_list;
#endif
}; };
static inline void extent_set_compress_type(unsigned long *bio_flags, static inline void extent_set_compress_type(unsigned long *bio_flags,