Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Mikulas Patocka and committed by
Mike Snitzer
86bad0c7 f98c8f79

+47 -10
+9
drivers/md/Kconfig
··· 240 240 as a cache, holding recently-read blocks in memory and performing 241 241 delayed writes. 242 242 243 + config 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. 243 252 config DM_BIO_PRISON 244 253 tristate 245 254 depends on BLK_DEV_DM
+38 -1
drivers/md/dm-bufio.c
··· 16 16 #include <linux/shrinker.h> 17 17 #include <linux/module.h> 18 18 #include <linux/rbtree.h> 19 + #include <linux/stacktrace.h> 19 20 20 21 #define DM_MSG_PREFIX "bufio" 21 22 ··· 150 149 struct list_head write_list; 151 150 struct bio bio; 152 151 struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS]; 152 + #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING 153 + #define MAX_STACK 10 154 + struct stack_trace stack_trace; 155 + unsigned long stack_entries[MAX_STACK]; 156 + #endif 153 157 }; 154 158 155 159 /*----------------------------------------------------------------*/ ··· 258 252 * dm_bufio_cache_size_per_client and dm_bufio_client_count 259 253 */ 260 254 static DEFINE_MUTEX(dm_bufio_clients_lock); 255 + 256 + #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING 257 + static void buffer_record_stack(struct dm_buffer *b) 258 + { 259 + b->stack_trace.nr_entries = 0; 260 + b->stack_trace.max_entries = MAX_STACK; 261 + b->stack_trace.entries = b->stack_entries; 262 + b->stack_trace.skip = 2; 263 + save_stack_trace(&b->stack_trace); 264 + } 265 + #endif 261 266 262 267 /*---------------------------------------------------------------- 263 268 * A red/black tree acts as an index for all the buffers. ··· 471 454 472 455 adjust_total_allocated(b->data_mode, (long)c->block_size); 473 456 457 + #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING 458 + memset(&b->stack_trace, 0, sizeof(b->stack_trace)); 459 + #endif 474 460 return b; 475 461 } 476 462 ··· 1083 1063 1084 1064 dm_bufio_lock(c); 1085 1065 b = __bufio_new(c, block, nf, &need_submit, &write_list); 1066 + #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING 1067 + if (b && b->hold_count == 1) 1068 + buffer_record_stack(b); 1069 + #endif 1086 1070 dm_bufio_unlock(c); 1087 1071 1088 1072 __flush_write_list(&write_list); ··· 1486 1462 { 1487 1463 struct dm_buffer *b; 1488 1464 int i; 1465 + bool warned = false; 1489 1466 1490 1467 BUG_ON(dm_bufio_in_request()); 1491 1468 ··· 1501 1476 __free_buffer_wake(b); 1502 1477 1503 1478 for (i = 0; i < LIST_SIZE; i++) 1504 - list_for_each_entry(b, &c->lru[i], lru_list) 1479 + list_for_each_entry(b, &c->lru[i], lru_list) { 1480 + WARN_ON(!warned); 1481 + warned = true; 1505 1482 DMERR("leaked buffer %llx, hold count %u, list %d", 1506 1483 (unsigned long long)b->block, b->hold_count, i); 1484 + #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING 1485 + print_stack_trace(&b->stack_trace, 1); 1486 + b->hold_count = 0; /* mark unclaimed to avoid BUG_ON below */ 1487 + #endif 1488 + } 1489 + 1490 + #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING 1491 + while ((b = __get_unclaimed_buffer(c))) 1492 + __free_buffer_wake(b); 1493 + #endif 1507 1494 1508 1495 for (i = 0; i < LIST_SIZE; i++) 1509 1496 BUG_ON(!list_empty(&c->lru[i]));
-9
drivers/md/persistent-data/Kconfig
··· 7 7 Library providing immutable on-disk data structure support for 8 8 device-mapper targets such as the thin provisioning target. 9 9 10 - config 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.