|
@@ -16,6 +16,7 @@
|
|
|
#include <linux/shrinker.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/rbtree.h>
|
|
|
+#include <linux/stacktrace.h>
|
|
|
|
|
|
#define DM_MSG_PREFIX "bufio"
|
|
|
|
|
@@ -149,6 +150,11 @@ struct dm_buffer {
|
|
|
struct list_head write_list;
|
|
|
struct bio bio;
|
|
|
struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
|
|
|
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
|
|
|
+#define MAX_STACK 10
|
|
|
+ struct stack_trace stack_trace;
|
|
|
+ unsigned long stack_entries[MAX_STACK];
|
|
|
+#endif
|
|
|
};
|
|
|
|
|
|
/*----------------------------------------------------------------*/
|
|
@@ -253,6 +259,17 @@ static LIST_HEAD(dm_bufio_all_clients);
|
|
|
*/
|
|
|
static DEFINE_MUTEX(dm_bufio_clients_lock);
|
|
|
|
|
|
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
|
|
|
+static void buffer_record_stack(struct dm_buffer *b)
|
|
|
+{
|
|
|
+ b->stack_trace.nr_entries = 0;
|
|
|
+ b->stack_trace.max_entries = MAX_STACK;
|
|
|
+ b->stack_trace.entries = b->stack_entries;
|
|
|
+ b->stack_trace.skip = 2;
|
|
|
+ save_stack_trace(&b->stack_trace);
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/*----------------------------------------------------------------
|
|
|
* A red/black tree acts as an index for all the buffers.
|
|
|
*--------------------------------------------------------------*/
|
|
@@ -454,6 +471,9 @@ static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
|
|
|
|
|
|
adjust_total_allocated(b->data_mode, (long)c->block_size);
|
|
|
|
|
|
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
|
|
|
+ memset(&b->stack_trace, 0, sizeof(b->stack_trace));
|
|
|
+#endif
|
|
|
return b;
|
|
|
}
|
|
|
|
|
@@ -1063,6 +1083,10 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
|
|
|
|
|
|
dm_bufio_lock(c);
|
|
|
b = __bufio_new(c, block, nf, &need_submit, &write_list);
|
|
|
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
|
|
|
+ if (b && b->hold_count == 1)
|
|
|
+ buffer_record_stack(b);
|
|
|
+#endif
|
|
|
dm_bufio_unlock(c);
|
|
|
|
|
|
__flush_write_list(&write_list);
|
|
@@ -1462,6 +1486,7 @@ static void drop_buffers(struct dm_bufio_client *c)
|
|
|
{
|
|
|
struct dm_buffer *b;
|
|
|
int i;
|
|
|
+ bool warned = false;
|
|
|
|
|
|
BUG_ON(dm_bufio_in_request());
|
|
|
|
|
@@ -1476,9 +1501,21 @@ static void drop_buffers(struct dm_bufio_client *c)
|
|
|
__free_buffer_wake(b);
|
|
|
|
|
|
for (i = 0; i < LIST_SIZE; i++)
|
|
|
- list_for_each_entry(b, &c->lru[i], lru_list)
|
|
|
+ list_for_each_entry(b, &c->lru[i], lru_list) {
|
|
|
+ WARN_ON(!warned);
|
|
|
+ warned = true;
|
|
|
DMERR("leaked buffer %llx, hold count %u, list %d",
|
|
|
(unsigned long long)b->block, b->hold_count, i);
|
|
|
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
|
|
|
+ print_stack_trace(&b->stack_trace, 1);
|
|
|
+ b->hold_count = 0; /* mark unclaimed to avoid BUG_ON below */
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
|
|
|
+ while ((b = __get_unclaimed_buffer(c)))
|
|
|
+ __free_buffer_wake(b);
|
|
|
+#endif
|
|
|
|
|
|
for (i = 0; i < LIST_SIZE; i++)
|
|
|
BUG_ON(!list_empty(&c->lru[i]));
|