aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2015-11-23 19:20:06 -0500
committerMike Snitzer <snitzer@redhat.com>2015-12-10 10:38:58 -0500
commit86bad0c7071c24efee0a395d8b622764bdd24320 (patch)
tree49fa95844e79ed05c16735dfa31cb6953683836d
parentf98c8f797021e78ba27143ecd39c51995c47aa8b (diff)
dm bufio: store stacktrace in buffers to help find buffer leaks
The option DM_DEBUG_BLOCK_STACK_TRACING is moved from persistent-data directory to device mapper directory because it will now be used by persistent-data and bufio. When the option is enabled, each bufio buffer stores the stacktrace of the last dm_bufio_get(), dm_bufio_read() or dm_bufio_new() call that increased the hold count to 1. The buffer's stacktrace is printed if the buffer was not released before the bufio client is destroyed. When DM_DEBUG_BLOCK_STACK_TRACING is enabled, any bufio buffer leaks are considered warnings - i.e. the kernel continues afterwards. If not enabled, buffer leaks are considered BUGs and the kernel with crash. Reasoning on this disposition is: if we only ever warned on buffer leaks users would generally ignore them and the problematic code would never get fixed. Successfully used to find source of bufio leaks fixed with commit fce079f63c3 ("dm btree: fix bufio buffer leaks in dm_btree_del() error path"). Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--drivers/md/Kconfig9
-rw-r--r--drivers/md/dm-bufio.c39
-rw-r--r--drivers/md/persistent-data/Kconfig9
3 files changed, 47 insertions, 10 deletions
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 7913fdcfc849..1b69359461e9 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -240,6 +240,15 @@ config DM_BUFIO
240 as a cache, holding recently-read blocks in memory and performing 240 as a cache, holding recently-read blocks in memory and performing
241 delayed writes. 241 delayed writes.
242 242
243config DM_DEBUG_BLOCK_STACK_TRACING
244 bool "Keep stack trace of persistent data block lock holders"
245 depends on STACKTRACE_SUPPORT && DM_BUFIO
246 select STACKTRACE
247 ---help---
248 Enable this for messages that may help debug problems with the
249 block manager locking used by thin provisioning and caching.
250
251 If unsure, say N.
243config DM_BIO_PRISON 252config DM_BIO_PRISON
244 tristate 253 tristate
245 depends on BLK_DEV_DM 254 depends on BLK_DEV_DM
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 7734298bff3c..bccc7e6903e4 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -16,6 +16,7 @@
16#include <linux/shrinker.h> 16#include <linux/shrinker.h>
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/rbtree.h> 18#include <linux/rbtree.h>
19#include <linux/stacktrace.h>
19 20
20#define DM_MSG_PREFIX "bufio" 21#define DM_MSG_PREFIX "bufio"
21 22
@@ -149,6 +150,11 @@ struct dm_buffer {
149 struct list_head write_list; 150 struct list_head write_list;
150 struct bio bio; 151 struct bio bio;
151 struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS]; 152 struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
153#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
154#define MAX_STACK 10
155 struct stack_trace stack_trace;
156 unsigned long stack_entries[MAX_STACK];
157#endif
152}; 158};
153 159
154/*----------------------------------------------------------------*/ 160/*----------------------------------------------------------------*/
@@ -253,6 +259,17 @@ static LIST_HEAD(dm_bufio_all_clients);
253 */ 259 */
254static DEFINE_MUTEX(dm_bufio_clients_lock); 260static DEFINE_MUTEX(dm_bufio_clients_lock);
255 261
262#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
263static void buffer_record_stack(struct dm_buffer *b)
264{
265 b->stack_trace.nr_entries = 0;
266 b->stack_trace.max_entries = MAX_STACK;
267 b->stack_trace.entries = b->stack_entries;
268 b->stack_trace.skip = 2;
269 save_stack_trace(&b->stack_trace);
270}
271#endif
272
256/*---------------------------------------------------------------- 273/*----------------------------------------------------------------
257 * A red/black tree acts as an index for all the buffers. 274 * A red/black tree acts as an index for all the buffers.
258 *--------------------------------------------------------------*/ 275 *--------------------------------------------------------------*/
@@ -454,6 +471,9 @@ static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
454 471
455 adjust_total_allocated(b->data_mode, (long)c->block_size); 472 adjust_total_allocated(b->data_mode, (long)c->block_size);
456 473
474#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
475 memset(&b->stack_trace, 0, sizeof(b->stack_trace));
476#endif
457 return b; 477 return b;
458} 478}
459 479
@@ -1063,6 +1083,10 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
1063 1083
1064 dm_bufio_lock(c); 1084 dm_bufio_lock(c);
1065 b = __bufio_new(c, block, nf, &need_submit, &write_list); 1085 b = __bufio_new(c, block, nf, &need_submit, &write_list);
1086#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
1087 if (b && b->hold_count == 1)
1088 buffer_record_stack(b);
1089#endif
1066 dm_bufio_unlock(c); 1090 dm_bufio_unlock(c);
1067 1091
1068 __flush_write_list(&write_list); 1092 __flush_write_list(&write_list);
@@ -1462,6 +1486,7 @@ static void drop_buffers(struct dm_bufio_client *c)
1462{ 1486{
1463 struct dm_buffer *b; 1487 struct dm_buffer *b;
1464 int i; 1488 int i;
1489 bool warned = false;
1465 1490
1466 BUG_ON(dm_bufio_in_request()); 1491 BUG_ON(dm_bufio_in_request());
1467 1492
@@ -1476,9 +1501,21 @@ static void drop_buffers(struct dm_bufio_client *c)
1476 __free_buffer_wake(b); 1501 __free_buffer_wake(b);
1477 1502
1478 for (i = 0; i < LIST_SIZE; i++) 1503 for (i = 0; i < LIST_SIZE; i++)
1479 list_for_each_entry(b, &c->lru[i], lru_list) 1504 list_for_each_entry(b, &c->lru[i], lru_list) {
1505 WARN_ON(!warned);
1506 warned = true;
1480 DMERR("leaked buffer %llx, hold count %u, list %d", 1507 DMERR("leaked buffer %llx, hold count %u, list %d",
1481 (unsigned long long)b->block, b->hold_count, i); 1508 (unsigned long long)b->block, b->hold_count, i);
1509#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
1510 print_stack_trace(&b->stack_trace, 1);
1511 b->hold_count = 0; /* mark unclaimed to avoid BUG_ON below */
1512#endif
1513 }
1514
1515#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
1516 while ((b = __get_unclaimed_buffer(c)))
1517 __free_buffer_wake(b);
1518#endif
1482 1519
1483 for (i = 0; i < LIST_SIZE; i++) 1520 for (i = 0; i < LIST_SIZE; i++)
1484 BUG_ON(!list_empty(&c->lru[i])); 1521 BUG_ON(!list_empty(&c->lru[i]));
diff --git a/drivers/md/persistent-data/Kconfig b/drivers/md/persistent-data/Kconfig
index 78c74bb71ba4..a53cbc928af1 100644
--- a/drivers/md/persistent-data/Kconfig
+++ b/drivers/md/persistent-data/Kconfig
@@ -7,12 +7,3 @@ config DM_PERSISTENT_DATA
7 Library providing immutable on-disk data structure support for 7 Library providing immutable on-disk data structure support for
8 device-mapper targets such as the thin provisioning target. 8 device-mapper targets such as the thin provisioning target.
9 9
10config DM_DEBUG_BLOCK_STACK_TRACING
11 bool "Keep stack trace of persistent data block lock holders"
12 depends on STACKTRACE_SUPPORT && DM_PERSISTENT_DATA
13 select STACKTRACE
14 ---help---
15 Enable this for messages that may help debug problems with the
16 block manager locking used by thin provisioning and caching.
17
18 If unsure, say N.