Merge remote-tracking branch 'linusw-gpio/for-next' into devm_gpiochip
[cascardo/linux.git] / drivers / md / dm-bufio.c
index 2dd3308..cd77216 100644 (file)
@@ -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;
 }
 
@@ -630,7 +650,7 @@ static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block,
        do {
                if (!bio_add_page(&b->bio, virt_to_page(ptr),
                                  len < PAGE_SIZE ? len : PAGE_SIZE,
-                                 virt_to_phys(ptr) & (PAGE_SIZE - 1))) {
+                                 offset_in_page(ptr))) {
                        BUG_ON(b->c->block_size <= PAGE_SIZE);
                        use_dmio(b, rw, block, end_io);
                        return;
@@ -1063,12 +1083,16 @@ 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);
 
        if (!b)
-               return b;
+               return NULL;
 
        if (need_submit)
                submit_io(b, READ, b->block, read_endio);
@@ -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]));
@@ -1891,8 +1928,7 @@ static void __exit dm_bufio_exit(void)
                bug = 1;
        }
 
-       if (bug)
-               BUG();
+       BUG_ON(bug);
 }
 
 module_init(dm_bufio_init)