Last active
September 30, 2015 03:38
-
-
Save huwan/b03a16c49e61b69cfa67 to your computer and use it in GitHub Desktop.
[PATCH] Buffer-layer tracing support for jbd/jbd2 related repo: https://github.com/huwan/linux-buffer-debug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 279cb88d74c75e5dc4876a2dcd1f1aefc62d60ba Mon Sep 17 00:00:00 2001 | |
From: Wan Hu <[email protected]> | |
Date: Thu, 3 Sep 2015 20:41:09 +0800 | |
Subject: [PATCH] Buffer-layer tracing support for ext4/jbd2 | |
--- | |
fs/Kconfig | 4 + | |
fs/Makefile | 1 + | |
fs/jbd2-kernel.c | 254 +++++++++++++++++++++++++++++++++++++++++++ | |
include/linux/buffer-trace.h | 52 +++++++++ | |
include/linux/buffer_head.h | 6 +- | |
include/linux/config.h | 8 ++ | |
include/linux/jbd.h | 2 +- | |
include/linux/jbd2.h | 30 +++++ | |
8 files changed, 355 insertions(+), 2 deletions(-) | |
create mode 100644 fs/jbd2-kernel.c | |
create mode 100644 include/linux/buffer-trace.h | |
create mode 100644 include/linux/config.h | |
diff --git a/fs/Kconfig b/fs/Kconfig | |
index 64d44ef..a4e21ed 100644 | |
--- a/fs/Kconfig | |
+++ b/fs/Kconfig | |
@@ -19,6 +19,10 @@ config FS_XIP | |
source "fs/jbd/Kconfig" | |
source "fs/jbd2/Kconfig" | |
+config BUFFER_DEBUG | |
+ bool "buffer-layer tracing" | |
+ depends on JBD2 | |
+ | |
config FS_MBCACHE | |
# Meta block cache for Extended Attributes (ext2/ext3/ext4) | |
tristate | |
diff --git a/fs/Makefile b/fs/Makefile | |
index af6d047..3e42bb7 100644 | |
--- a/fs/Makefile | |
+++ b/fs/Makefile | |
@@ -36,6 +36,7 @@ obj-y += $(nfsd-y) $(nfsd-m) | |
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o | |
obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o | |
obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o | |
+obj-$(CONFIG_BUFFER_DEBUG) += jbd2-kernel.o | |
# binfmt_script is always there | |
obj-y += binfmt_script.o | |
diff --git a/fs/jbd2-kernel.c b/fs/jbd2-kernel.c | |
new file mode 100644 | |
index 0000000..ffb224c | |
--- /dev/null | |
+++ b/fs/jbd2-kernel.c | |
@@ -0,0 +1,254 @@ | |
+/* | |
+ * fs/jbd2/jbd2-kernel.c | |
+ * | |
+ * Support code for the Journalling Block Device layer. | |
+ * This file contains things which have to be in-kernel when | |
+ * JBD2 is a module. | |
+ * | |
+ * 15 May 2001 Andrew Morton <andrewm@xxxxxxxxxx> | |
+ * Created | |
+ */ | |
+ | |
+#include <linux/config.h> | |
+#include <linux/fs.h> | |
+#include <linux/jbd2.h> | |
+#include <linux/module.h> | |
+#include <linux/sched.h> | |
+#include <linux/mm.h> | |
+ | |
+/* | |
+ * Some sanity testing which is called from mark_buffer_clean(), | |
+ * and must be present in the main kernel. | |
+ */ | |
+ | |
+void jbd_preclean_buffer_check(struct buffer_head *bh) | |
+{ | |
+ if (buffer_jbd(bh)) { | |
+ struct journal_head *jh = bh2jh(bh); | |
+ | |
+ transaction_t *transaction = jh->b_transaction; | |
+ journal_t *journal; | |
+ | |
+ if (jh->b_jlist == 0 && transaction == NULL) | |
+ return; | |
+ | |
+ J_ASSERT_JH(jh, transaction != NULL); | |
+ /* The kernel may be unmapping old data. We expect it | |
+ * to be dirty in that case, unless the buffer has | |
+ * already been forgotten by a transaction. */ | |
+ if (jh->b_jlist != BJ_Forget) { | |
+#if 1 | |
+ if (!buffer_dirty(bh)) { | |
+ printk("%s: clean of clean buffer\n", | |
+ __FUNCTION__); | |
+ print_buffer_trace(bh); | |
+ return; | |
+ } | |
+#endif | |
+ J_ASSERT_BH(bh, buffer_dirty(bh)); | |
+ } | |
+ | |
+ journal = transaction->t_journal; | |
+ J_ASSERT_JH(jh, | |
+ transaction == journal->j_running_transaction || | |
+ transaction == journal->j_committing_transaction); | |
+ } | |
+} | |
+EXPORT_SYMBOL(jbd_preclean_buffer_check); | |
+ | |
+/* | |
+ * Support functions for BUFFER_TRACE() | |
+ */ | |
+ | |
+static spinlock_t trace_lock = SPIN_LOCK_UNLOCKED; | |
+ | |
+void buffer_trace(const char *function, struct buffer_head *dest, | |
+ struct buffer_head *src, char *info) | |
+{ | |
+ struct buffer_history_item *bhist_i; | |
+ struct page *page; | |
+ unsigned long flags; | |
+ | |
+ if (dest == 0 || src == 0) | |
+ return; | |
+ | |
+ spin_lock_irqsave(&trace_lock, flags); | |
+ | |
+ /* | |
+ * Sometimes we don't initialise the ring pointers. (locally declared | |
+ * temp buffer_heads). Feebly attempt to detect and correct that here. | |
+ */ | |
+ if ((dest->b_history.b_history_head - dest->b_history.b_history_tail > | |
+ BUFFER_HISTORY_SIZE)) { | |
+ dest->b_history.b_history_head = 0; | |
+ dest->b_history.b_history_tail = 0; | |
+ } | |
+ bhist_i = dest->b_history.b + | |
+ (dest->b_history.b_history_head & (BUFFER_HISTORY_SIZE - 1)); | |
+ bhist_i->function = function; | |
+ bhist_i->info = info; | |
+ bhist_i->b_state = src->b_state; | |
+ page = src->b_page; | |
+ if (page) | |
+ bhist_i->pg_dirty = !!PageDirty(page); | |
+ else | |
+ bhist_i->pg_dirty = 0; | |
+ | |
+#if defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) | |
+ bhist_i->b_trans_is_running = 0; | |
+ bhist_i->b_trans_is_committing = 0; | |
+ bhist_i->b_blocknr = src->b_blocknr; | |
+ if (buffer_jbd(src)) { | |
+ struct journal_head *jh; | |
+ journal_t *journal; | |
+ transaction_t *transaction; | |
+ | |
+ /* Footwork to avoid racing with journal_remove_journal_head */ | |
+ jh = src->b_private; | |
+ if (jh == 0) | |
+ goto raced; | |
+ transaction = jh->b_transaction; | |
+ if (src->b_private == 0) | |
+ goto raced; | |
+ bhist_i->b_jcount = jh->b_jcount; | |
+ bhist_i->b_jbd = 1; | |
+ bhist_i->b_jlist = jh->b_jlist; | |
+ bhist_i->b_frozen_data = jh->b_frozen_data; | |
+ bhist_i->b_committed_data = jh->b_committed_data; | |
+ bhist_i->b_transaction = !!jh->b_transaction; | |
+ bhist_i->b_next_transaction = !!jh->b_next_transaction; | |
+ bhist_i->b_cp_transaction = !!jh->b_cp_transaction; | |
+ | |
+ if (transaction) { | |
+ journal = transaction->t_journal; | |
+ bhist_i->b_trans_is_running = transaction == | |
+ journal->j_running_transaction; | |
+ bhist_i->b_trans_is_committing = transaction == | |
+ journal->j_committing_transaction; | |
+ } | |
+ } else { | |
+raced: | |
+ bhist_i->b_jcount = 0; | |
+ bhist_i->b_jbd = 0; | |
+ bhist_i->b_jlist = 0; | |
+ bhist_i->b_frozen_data = 0; | |
+ bhist_i->b_committed_data = 0; | |
+ bhist_i->b_transaction = 0; | |
+ bhist_i->b_next_transaction = 0; | |
+ bhist_i->b_cp_transaction = 0; | |
+ } | |
+#endif /* defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) */ | |
+ | |
+ bhist_i->cpu = smp_processor_id(); | |
+ bhist_i->b_count = atomic_read(&src->b_count); | |
+ | |
+ dest->b_history.b_history_head++; | |
+ if (dest->b_history.b_history_head - dest->b_history.b_history_tail > | |
+ BUFFER_HISTORY_SIZE) | |
+ dest->b_history.b_history_tail = | |
+ dest->b_history.b_history_head - BUFFER_HISTORY_SIZE; | |
+ | |
+ spin_unlock_irqrestore(&trace_lock, flags); | |
+} | |
+ | |
+static const char *b_jlist_to_string(unsigned int b_list) | |
+{ | |
+ switch (b_list) { | |
+#if defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) | |
+ case BJ_None: return "BJ_None"; | |
+ case BJ_Metadata: return "BJ_Metadata"; | |
+ case BJ_Forget: return "BJ_Forget"; | |
+ case BJ_IO: return "BJ_IO"; | |
+ case BJ_Shadow: return "BJ_Shadow"; | |
+ case BJ_LogCtl: return "BJ_LogCtl"; | |
+ case BJ_Reserved: return "BJ_Reserved"; | |
+#endif | |
+ default: return "Bad b_jlist"; | |
+ } | |
+} | |
+ | |
+static void print_one_hist(struct buffer_history_item *bhist_i) | |
+{ | |
+ printk(" %s():%s\n", bhist_i->function, bhist_i->info); | |
+ printk(" b_state:0x%lx b_jlist:%s cpu:%d b_count:%d b_blocknr:%lu\n", | |
+ bhist_i->b_state, | |
+ b_jlist_to_string(bhist_i->b_jlist), | |
+ bhist_i->cpu, | |
+ bhist_i->b_count, | |
+ bhist_i->b_blocknr); | |
+#if defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) | |
+ printk(" b_jbd:%u b_frozen_data:%p b_committed_data:%p\n", | |
+ bhist_i->b_jbd, | |
+ bhist_i->b_frozen_data, | |
+ bhist_i->b_committed_data); | |
+ printk(" b_transaction:%u b_next_transaction:%u " | |
+ "b_cp_transaction:%u b_trans_is_running:%u\n", | |
+ bhist_i->b_transaction, | |
+ bhist_i->b_next_transaction, | |
+ bhist_i->b_cp_transaction, | |
+ bhist_i->b_trans_is_running); | |
+ printk(" b_trans_is_comitting:%u b_jcount:%u pg_dirty:%u", | |
+ bhist_i->b_trans_is_committing, | |
+ bhist_i->b_jcount, | |
+ bhist_i->pg_dirty); | |
+#endif | |
+ printk("\n"); | |
+} | |
+ | |
+void print_buffer_fields(struct buffer_head *bh) | |
+{ | |
+ printk("b_blocknr:%llu b_count:%d\n", | |
+ (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); | |
+ printk("b_this_page:%p b_data:%p b_page:%p\n", | |
+ bh->b_this_page, bh->b_data, bh->b_page); | |
+#if defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) | |
+ if (buffer_jbd(bh)) { | |
+ struct journal_head *jh = bh2jh(bh); | |
+ | |
+ printk("b_jlist:%u b_frozen_data:%p b_committed_data:%p\n", | |
+ jh->b_jlist, jh->b_frozen_data, jh->b_committed_data); | |
+ printk(" b_transaction:%p b_next_transaction:%p " | |
+ "b_cp_transaction:%p\n", | |
+ jh->b_transaction, jh->b_next_transaction, | |
+ jh->b_cp_transaction); | |
+ printk("b_cpnext:%p b_cpprev:%p\n", | |
+ jh->b_cpnext, jh->b_cpprev); | |
+ } | |
+#endif | |
+} | |
+ | |
+void print_buffer_trace(struct buffer_head *bh) | |
+{ | |
+ unsigned long idx, count; | |
+ unsigned long flags; | |
+ | |
+ printk("buffer trace for buffer at 0x%p (I am CPU %d)\n", | |
+ bh, smp_processor_id()); | |
+ BUFFER_TRACE(bh, ""); /* Record state now */ | |
+ | |
+ spin_lock_irqsave(&trace_lock, flags); | |
+ for ( idx = bh->b_history.b_history_tail, count = 0; | |
+ idx < bh->b_history.b_history_head && | |
+ count < BUFFER_HISTORY_SIZE; | |
+ idx++, count++) | |
+ print_one_hist(bh->b_history.b + | |
+ (idx & (BUFFER_HISTORY_SIZE - 1))); | |
+ | |
+ print_buffer_fields(bh); | |
+ spin_unlock_irqrestore(&trace_lock, flags); | |
+ dump_stack(); | |
+ printk("\n"); | |
+} | |
+ | |
+static struct buffer_head *failed_buffer_head; /* For access with debuggers */ | |
+ | |
+void buffer_assertion_failure(struct buffer_head *bh) | |
+{ | |
+ console_verbose(); | |
+ failed_buffer_head = bh; | |
+ print_buffer_trace(bh); | |
+} | |
+EXPORT_SYMBOL(buffer_trace); | |
+EXPORT_SYMBOL(print_buffer_trace); | |
+EXPORT_SYMBOL(buffer_assertion_failure); | |
+EXPORT_SYMBOL(print_buffer_fields); | |
diff --git a/include/linux/buffer-trace.h b/include/linux/buffer-trace.h | |
new file mode 100644 | |
index 0000000..39951e1 | |
--- /dev/null | |
+++ b/include/linux/buffer-trace.h | |
@@ -0,0 +1,52 @@ | |
+/* | |
+ * include/linux/buffer-trace.h | |
+ * | |
+ * Debugging support for recording buffer_head state transitions | |
+ * | |
+ * May 2001, akpm | |
+ * Created | |
+ */ | |
+ | |
+#ifndef BUFFER_TRACE_H_INCLUDED | |
+#define BUFFER_TRACE_H_INCLUDED | |
+ | |
+#include <linux/config.h> | |
+ | |
+#ifdef CONFIG_BUFFER_DEBUG | |
+ | |
+/* The number of records per buffer_head. Must be a power of two */ | |
+#define BUFFER_HISTORY_SIZE 32 | |
+ | |
+struct buffer_head; | |
+ | |
+/* This gets embedded in struct buffer_head */ | |
+struct buffer_history { | |
+ struct buffer_history_item { | |
+ const char *function; | |
+ char *info; | |
+ unsigned long b_state; | |
+ unsigned b_list:3; | |
+ unsigned b_jlist:4; | |
+ unsigned pg_dirty:1; | |
+ unsigned cpu:3; | |
+ unsigned b_count:8; | |
+ unsigned long b_blocknr; /* For src != dest */ | |
+#if defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) | |
+ unsigned b_jcount:4; | |
+ unsigned b_jbd:1; | |
+ unsigned b_transaction:1; | |
+ unsigned b_next_transaction:1; | |
+ unsigned b_cp_transaction:1; | |
+ unsigned b_trans_is_running:1; | |
+ unsigned b_trans_is_committing:1; | |
+ void *b_frozen_data; | |
+ void *b_committed_data; | |
+#endif | |
+ } b[BUFFER_HISTORY_SIZE]; | |
+ unsigned long b_history_head; /* Next place to write */ | |
+ unsigned long b_history_tail; /* Oldest valid entry */ | |
+}; | |
+ | |
+#endif /* CONFIG_BUFFER_DEBUG */ | |
+ | |
+#endif /* BUFFER_TRACE_H_INCLUDED */ | |
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h | |
index 16ed028..c814b3b 100644 | |
--- a/include/linux/buffer_head.h | |
+++ b/include/linux/buffer_head.h | |
@@ -13,6 +13,7 @@ | |
#include <linux/pagemap.h> | |
#include <linux/wait.h> | |
#include <asm/atomic.h> | |
+#include <linux/buffer-trace.h> | |
#ifdef CONFIG_BLOCK | |
@@ -69,11 +70,14 @@ struct buffer_head { | |
struct block_device *b_bdev; | |
bh_end_io_t *b_end_io; /* I/O completion */ | |
- void *b_private; /* reserved for b_end_io */ | |
+ void *b_private; /* reserved for b_end_io */ | |
struct list_head b_assoc_buffers; /* associated with another mapping */ | |
struct address_space *b_assoc_map; /* mapping this buffer is | |
associated with */ | |
atomic_t b_count; /* users using this buffer_head */ | |
+ #ifdef CONFIG_BUFFER_DEBUG | |
+ struct buffer_history b_history; | |
+ #endif | |
}; | |
/* | |
diff --git a/include/linux/config.h b/include/linux/config.h | |
new file mode 100644 | |
index 0000000..a91f5e5 | |
--- /dev/null | |
+++ b/include/linux/config.h | |
@@ -0,0 +1,8 @@ | |
+#ifndef _LINUX_CONFIG_H | |
+#define _LINUX_CONFIG_H | |
+/* This file is no longer in use and kept only for backward compatibility. | |
+ * autoconf.h is now included via -imacros on the commandline | |
+ */ | |
+#include <linux/autoconf.h> | |
+ | |
+#endif | |
diff --git a/include/linux/jbd.h b/include/linux/jbd.h | |
index 331530c..5ba749e 100644 | |
--- a/include/linux/jbd.h | |
+++ b/include/linux/jbd.h | |
@@ -246,7 +246,7 @@ typedef struct journal_superblock_s | |
#define J_ASSERT(assert) BUG_ON(!(assert)) | |
-#if defined(CONFIG_BUFFER_DEBUG) | |
+#if defined(CONFIG_BUFFER_DEBUG2) | |
void buffer_assertion_failure(struct buffer_head *bh); | |
#define J_ASSERT_BH(bh, expr) \ | |
do { \ | |
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h | |
index 638ce45..58b1bbe 100644 | |
--- a/include/linux/jbd2.h | |
+++ b/include/linux/jbd2.h | |
@@ -296,6 +296,7 @@ void buffer_assertion_failure(struct buffer_head *bh); | |
#else | |
#define J_ASSERT_BH(bh, expr) J_ASSERT(expr) | |
#define J_ASSERT_JH(jh, expr) J_ASSERT(expr) | |
+#define buffer_assertion_failure(bh) do { } while (0) | |
#endif | |
#if defined(JBD2_PARANOID_IOFAIL) | |
@@ -1296,6 +1297,33 @@ extern int jbd_blocks_per_page(struct inode *inode); | |
#ifdef __KERNEL__ | |
+#ifdef CONFIG_BUFFER_DEBUG | |
+ | |
+static inline void buffer_trace_init(struct buffer_history *bhist) | |
+{ | |
+ bhist->b_history_head = 0; | |
+ bhist->b_history_tail = 0; | |
+} | |
+extern void buffer_trace(const char *function, struct buffer_head *dest, | |
+ struct buffer_head *src, char *info); | |
+extern void print_buffer_fields(struct buffer_head *bh); | |
+extern void print_buffer_trace(struct buffer_head *bh); | |
+ | |
+#define BUFFER_STRINGIFY2(X) #X | |
+#define BUFFER_STRINGIFY(X) BUFFER_STRINGIFY2(X) | |
+ | |
+#define BUFFER_TRACE2(dest, src, info) \ | |
+ do { \ | |
+ buffer_trace(__FUNCTION__, (dest), (src), \ | |
+ "["__FILE__":" \ | |
+ BUFFER_STRINGIFY(__LINE__)"] " info); \ | |
+ } while (0) | |
+ | |
+#define BUFFER_TRACE(bh, info) BUFFER_TRACE2(bh, bh, info) | |
+#define JBUFFER_TRACE(jh, info) BUFFER_TRACE(jh2bh(jh), info) | |
+ | |
+#else /* CONFIG_BUFFER_DEBUG */ | |
+ | |
#define buffer_trace_init(bh) do {} while (0) | |
#define print_buffer_fields(bh) do {} while (0) | |
#define print_buffer_trace(bh) do {} while (0) | |
@@ -1303,6 +1331,8 @@ extern int jbd_blocks_per_page(struct inode *inode); | |
#define BUFFER_TRACE2(bh, bh2, info) do {} while (0) | |
#define JBUFFER_TRACE(jh, info) do {} while (0) | |
+#endif /* CONFIG_BUFFER_DEBUG */ | |
+ | |
/* | |
* jbd2_dev_to_name is a utility function used by the jbd2 and ext4 | |
* tracing infrastructure to map a dev_t to a device name. | |
-- | |
1.9.1 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From e69ac38b1d3aef77fd35433a9989c1803be892db Mon Sep 17 00:00:00 2001 | |
From: Wan Hu <[email protected]> | |
Date: Wed, 30 Sep 2015 11:08:42 +0800 | |
Subject: [PATCH] Buffer-layer-tracing-support-for-JBD | |
--- | |
fs/Kconfig | 4 + | |
fs/Makefile | 1 + | |
fs/jbd-kernel.c | 253 ++++++++++++++++++++++++++++++++++++++++++ | |
include/linux/buffer-trace.h | 50 ++++++++ | |
include/linux/buffer_head.h | 4 + | |
include/linux/jbd.h | 30 +++++ | |
include/linux/jbd2.h | 2 +- | |
7 files changed, 343 insertions(+), 1 deletions(-) | |
create mode 100644 fs/jbd-kernel.c | |
create mode 100644 include/linux/buffer-trace.h | |
diff --git a/fs/Kconfig b/fs/Kconfig | |
index 64d44ef..6407afd 100644 | |
--- a/fs/Kconfig | |
+++ b/fs/Kconfig | |
@@ -19,6 +19,10 @@ config FS_XIP | |
source "fs/jbd/Kconfig" | |
source "fs/jbd2/Kconfig" | |
+config BUFFER_DEBUG | |
+ bool "buffer-layer tracing" | |
+ depends on JBD | |
+ | |
config FS_MBCACHE | |
# Meta block cache for Extended Attributes (ext2/ext3/ext4) | |
tristate | |
diff --git a/fs/Makefile b/fs/Makefile | |
index af6d047..623731d 100644 | |
--- a/fs/Makefile | |
+++ b/fs/Makefile | |
@@ -36,6 +36,7 @@ obj-y += $(nfsd-y) $(nfsd-m) | |
obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o | |
obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o | |
obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o | |
+obj-$(CONFIG_BUFFER_DEBUG) += jbd-kernel.o | |
# binfmt_script is always there | |
obj-y += binfmt_script.o | |
diff --git a/fs/jbd-kernel.c b/fs/jbd-kernel.c | |
new file mode 100644 | |
index 0000000..882f047 | |
--- /dev/null | |
+++ b/fs/jbd-kernel.c | |
@@ -0,0 +1,253 @@ | |
+/* | |
+ * fs/jbd-kernel.c | |
+ * | |
+ * Support code for the Journalling Block Device layer. | |
+ * This file contains things which have to be in-kernel when | |
+ * JBD is a module. | |
+ * | |
+ * 15 May 2001 Andrew Morton <andrewm@xxxxxxxxxx> | |
+ * Created | |
+ */ | |
+ | |
+#include <linux/fs.h> | |
+#include <linux/jbd.h> | |
+#include <linux/module.h> | |
+#include <linux/sched.h> | |
+#include <linux/mm.h> | |
+ | |
+/* | |
+ * Some sanity testing which is called from mark_buffer_clean(), | |
+ * and must be present in the main kernel. | |
+ */ | |
+ | |
+void jbd_preclean_buffer_check(struct buffer_head *bh) | |
+{ | |
+ if (buffer_jbd(bh)) { | |
+ struct journal_head *jh = bh2jh(bh); | |
+ | |
+ transaction_t *transaction = jh->b_transaction; | |
+ journal_t *journal; | |
+ | |
+ if (jh->b_jlist == 0 && transaction == NULL) | |
+ return; | |
+ | |
+ J_ASSERT_JH(jh, transaction != NULL); | |
+ /* The kernel may be unmapping old data. We expect it | |
+ * to be dirty in that case, unless the buffer has | |
+ * already been forgotten by a transaction. */ | |
+ if (jh->b_jlist != BJ_Forget) { | |
+#if 1 | |
+ if (!buffer_dirty(bh)) { | |
+ printk("%s: clean of clean buffer\n", | |
+ __FUNCTION__); | |
+ print_buffer_trace(bh); | |
+ return; | |
+ } | |
+#endif | |
+ J_ASSERT_BH(bh, buffer_dirty(bh)); | |
+ } | |
+ | |
+ journal = transaction->t_journal; | |
+ J_ASSERT_JH(jh, | |
+ transaction == journal->j_running_transaction || | |
+ transaction == journal->j_committing_transaction); | |
+ } | |
+} | |
+ | |
+EXPORT_SYMBOL(jbd_preclean_buffer_check); | |
+ | |
+/* | |
+ * Support functions for BUFFER_TRACE() | |
+ */ | |
+ | |
+static spinlock_t trace_lock = SPIN_LOCK_UNLOCKED; | |
+ | |
+void buffer_trace(const char *function, struct buffer_head *dest, | |
+ struct buffer_head *src, char *info) | |
+{ | |
+ struct buffer_history_item *bhist_i; | |
+ unsigned long flags; | |
+ | |
+ if (dest == 0 || src == 0) | |
+ return; | |
+ | |
+ spin_lock_irqsave(&trace_lock, flags); | |
+ | |
+ /* | |
+ * Sometimes we don't initialise the ring pointers. (locally declared | |
+ * temp buffer_heads). Feebly attempt to detect and correct that here. | |
+ */ | |
+ if ((dest->b_history.b_history_head - dest->b_history.b_history_tail > | |
+ BUFFER_HISTORY_SIZE)) { | |
+ dest->b_history.b_history_head = 0; | |
+ dest->b_history.b_history_tail = 0; | |
+ } | |
+ bhist_i = dest->b_history.b + | |
+ (dest->b_history.b_history_head & (BUFFER_HISTORY_SIZE - 1)); | |
+ bhist_i->function = function; | |
+ bhist_i->info = info; | |
+ bhist_i->b_state = src->b_state; | |
+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) | |
+ bhist_i->b_trans_is_running = 0; | |
+ bhist_i->b_trans_is_committing = 0; | |
+ bhist_i->b_blocknr = src->b_blocknr; | |
+ if (buffer_jbd(src)) { | |
+ struct journal_head *jh; | |
+ journal_t *journal; | |
+ transaction_t *transaction; | |
+ | |
+ /* Footwork to avoid racing with journal_remove_journal_head */ | |
+ jh = src->b_private; | |
+ if (jh == 0) | |
+ goto raced; | |
+ transaction = jh->b_transaction; | |
+ if (src->b_private == 0) | |
+ goto raced; | |
+ bhist_i->b_jcount = jh->b_jcount; | |
+ bhist_i->b_jbd = 1; | |
+ bhist_i->b_jlist = jh->b_jlist; | |
+ bhist_i->b_frozen_data = jh->b_frozen_data; | |
+ bhist_i->b_committed_data = jh->b_committed_data; | |
+ bhist_i->b_transaction = !!jh->b_transaction; | |
+ bhist_i->b_next_transaction = !!jh->b_next_transaction; | |
+ bhist_i->b_cp_transaction = !!jh->b_cp_transaction; | |
+ | |
+ if (transaction) { | |
+ journal = transaction->t_journal; | |
+ bhist_i->b_trans_is_running = transaction == | |
+ journal->j_running_transaction; | |
+ bhist_i->b_trans_is_committing = transaction == | |
+ journal->j_committing_transaction; | |
+ } | |
+ } else { | |
+raced: | |
+ bhist_i->b_jcount = 0; | |
+ bhist_i->b_jbd = 0; | |
+ bhist_i->b_jlist = 0; | |
+ bhist_i->b_frozen_data = 0; | |
+ bhist_i->b_committed_data = 0; | |
+ bhist_i->b_transaction = 0; | |
+ bhist_i->b_next_transaction = 0; | |
+ bhist_i->b_cp_transaction = 0; | |
+ } | |
+#endif /* defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) */ | |
+ | |
+ bhist_i->cpu = smp_processor_id(); | |
+ bhist_i->b_count = atomic_read(&src->b_count); | |
+ | |
+ dest->b_history.b_history_head++; | |
+ if (dest->b_history.b_history_head - dest->b_history.b_history_tail > | |
+ BUFFER_HISTORY_SIZE) | |
+ dest->b_history.b_history_tail = | |
+ dest->b_history.b_history_head - BUFFER_HISTORY_SIZE; | |
+ | |
+ spin_unlock_irqrestore(&trace_lock, flags); | |
+} | |
+ | |
+static const char *b_jlist_to_string(unsigned int b_list) | |
+{ | |
+ switch (b_list) { | |
+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) | |
+ case BJ_None: | |
+ return "BJ_None"; | |
+ case BJ_SyncData: | |
+ return "BJ_SyncData"; | |
+ case BJ_Metadata: | |
+ return "BJ_Metadata"; | |
+ case BJ_Forget: | |
+ return "BJ_Forget"; | |
+ case BJ_IO: | |
+ return "BJ_IO"; | |
+ case BJ_Shadow: | |
+ return "BJ_Shadow"; | |
+ case BJ_LogCtl: | |
+ return "BJ_LogCtl"; | |
+ case BJ_Reserved: | |
+ return "BJ_Reserved"; | |
+ case BJ_Locked: | |
+ return "BJ_Locked"; | |
+#endif | |
+ default: | |
+ return "Bad b_jlist"; | |
+ } | |
+} | |
+ | |
+static void print_one_hist(struct buffer_history_item *bhist_i) | |
+{ | |
+ printk(" %s():%s\n", bhist_i->function, bhist_i->info); | |
+ printk(" b_state:0x%lx b_jlist:%s cpu:%d b_count:%d b_blocknr:%lu\n", | |
+ bhist_i->b_state, | |
+ b_jlist_to_string(bhist_i->b_jlist), | |
+ bhist_i->cpu, bhist_i->b_count, bhist_i->b_blocknr); | |
+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) | |
+ printk(" b_jbd:%u b_frozen_data:%p b_committed_data:%p\n", | |
+ bhist_i->b_jbd, | |
+ bhist_i->b_frozen_data, bhist_i->b_committed_data); | |
+ printk(" b_transaction:%u b_next_transaction:%u " | |
+ "b_cp_transaction:%u b_trans_is_running:%u\n", | |
+ bhist_i->b_transaction, | |
+ bhist_i->b_next_transaction, | |
+ bhist_i->b_cp_transaction, bhist_i->b_trans_is_running); | |
+ printk(" b_trans_is_comitting:%u b_jcount:%u pg_dirty:%u", | |
+ bhist_i->b_trans_is_committing, | |
+ bhist_i->b_jcount, bhist_i->pg_dirty); | |
+#endif | |
+ printk("\n"); | |
+} | |
+ | |
+void print_buffer_fields(struct buffer_head *bh) | |
+{ | |
+ printk("b_blocknr:%llu b_count:%d\n", | |
+ (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); | |
+ printk("b_this_page:%p b_data:%p b_page:%p\n", | |
+ bh->b_this_page, bh->b_data, bh->b_page); | |
+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) | |
+ if (buffer_jbd(bh)) { | |
+ struct journal_head *jh = bh2jh(bh); | |
+ | |
+ printk("b_jlist:%u b_frozen_data:%p b_committed_data:%p\n", | |
+ jh->b_jlist, jh->b_frozen_data, jh->b_committed_data); | |
+ printk(" b_transaction:%p b_next_transaction:%p " | |
+ "b_cp_transaction:%p\n", | |
+ jh->b_transaction, jh->b_next_transaction, | |
+ jh->b_cp_transaction); | |
+ printk("b_cpnext:%p b_cpprev:%p\n", jh->b_cpnext, jh->b_cpprev); | |
+ } | |
+#endif | |
+} | |
+ | |
+void print_buffer_trace(struct buffer_head *bh) | |
+{ | |
+ unsigned long idx, count; | |
+ unsigned long flags; | |
+ | |
+ printk("buffer trace for buffer at 0x%p (I am CPU %d)\n", | |
+ bh, smp_processor_id()); | |
+ BUFFER_TRACE(bh, ""); /* Record state now */ | |
+ | |
+ spin_lock_irqsave(&trace_lock, flags); | |
+ for (idx = bh->b_history.b_history_tail, count = 0; | |
+ idx < bh->b_history.b_history_head && | |
+ count < BUFFER_HISTORY_SIZE; idx++, count++) | |
+ print_one_hist(bh->b_history.b + | |
+ (idx & (BUFFER_HISTORY_SIZE - 1))); | |
+ | |
+ print_buffer_fields(bh); | |
+ spin_unlock_irqrestore(&trace_lock, flags); | |
+ dump_stack(); | |
+ printk("\n"); | |
+} | |
+ | |
+static struct buffer_head *failed_buffer_head; /* For access with debuggers */ | |
+ | |
+void buffer_assertion_failure(struct buffer_head *bh) | |
+{ | |
+ console_verbose(); | |
+ failed_buffer_head = bh; | |
+ print_buffer_trace(bh); | |
+} | |
+ | |
+EXPORT_SYMBOL(buffer_trace); | |
+EXPORT_SYMBOL(print_buffer_trace); | |
+EXPORT_SYMBOL(buffer_assertion_failure); | |
+EXPORT_SYMBOL(print_buffer_fields); | |
diff --git a/include/linux/buffer-trace.h b/include/linux/buffer-trace.h | |
new file mode 100644 | |
index 0000000..22ca43b | |
--- /dev/null | |
+++ b/include/linux/buffer-trace.h | |
@@ -0,0 +1,50 @@ | |
+/* | |
+ * include/linux/buffer-trace.h | |
+ * | |
+ * Debugging support for recording buffer_head state transitions | |
+ * | |
+ * May 2001, akpm | |
+ * Created | |
+ */ | |
+ | |
+#ifndef BUFFER_TRACE_H_INCLUDED | |
+#define BUFFER_TRACE_H_INCLUDED | |
+ | |
+#ifdef CONFIG_BUFFER_DEBUG | |
+ | |
+/* The number of records per buffer_head. Must be a power of two */ | |
+#define BUFFER_HISTORY_SIZE 32 | |
+ | |
+struct buffer_head; | |
+ | |
+/* This gets embedded in struct buffer_head */ | |
+struct buffer_history { | |
+ struct buffer_history_item { | |
+ const char *function; | |
+ char *info; | |
+ unsigned long b_state; | |
+ unsigned b_list:3; | |
+ unsigned b_jlist:4; | |
+ unsigned pg_dirty:1; | |
+ unsigned cpu:3; | |
+ unsigned b_count:8; | |
+ unsigned long b_blocknr; /* For src != dest */ | |
+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) | |
+ unsigned b_jcount:4; | |
+ unsigned b_jbd:1; | |
+ unsigned b_transaction:1; | |
+ unsigned b_next_transaction:1; | |
+ unsigned b_cp_transaction:1; | |
+ unsigned b_trans_is_running:1; | |
+ unsigned b_trans_is_committing:1; | |
+ void *b_frozen_data; | |
+ void *b_committed_data; | |
+#endif | |
+ } b[BUFFER_HISTORY_SIZE]; | |
+ unsigned long b_history_head; /* Next place to write */ | |
+ unsigned long b_history_tail; /* Oldest valid entry */ | |
+}; | |
+ | |
+#endif /* CONFIG_BUFFER_DEBUG */ | |
+ | |
+#endif /* BUFFER_TRACE_H_INCLUDED */ | |
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h | |
index 16ed028..d0b717d 100644 | |
--- a/include/linux/buffer_head.h | |
+++ b/include/linux/buffer_head.h | |
@@ -13,6 +13,7 @@ | |
#include <linux/pagemap.h> | |
#include <linux/wait.h> | |
#include <asm/atomic.h> | |
+#include <linux/buffer-trace.h> | |
#ifdef CONFIG_BLOCK | |
@@ -74,6 +75,9 @@ struct buffer_head { | |
struct address_space *b_assoc_map; /* mapping this buffer is | |
associated with */ | |
atomic_t b_count; /* users using this buffer_head */ | |
+#ifdef CONFIG_BUFFER_DEBUG | |
+ struct buffer_history b_history; | |
+#endif | |
}; | |
/* | |
diff --git a/include/linux/jbd.h b/include/linux/jbd.h | |
index 331530c..b666b8b 100644 | |
--- a/include/linux/jbd.h | |
+++ b/include/linux/jbd.h | |
@@ -258,6 +258,7 @@ void buffer_assertion_failure(struct buffer_head *bh); | |
#else | |
#define J_ASSERT_BH(bh, expr) J_ASSERT(expr) | |
#define J_ASSERT_JH(jh, expr) J_ASSERT(expr) | |
+#define buffer_assertion_failure(bh) do { } while (0) | |
#endif | |
#if defined(JBD_PARANOID_IOFAIL) | |
@@ -1094,6 +1095,33 @@ extern int jbd_blocks_per_page(struct inode *inode); | |
#ifdef __KERNEL__ | |
+#ifdef CONFIG_BUFFER_DEBUG | |
+ | |
+static inline void buffer_trace_init(struct buffer_history *bhist) | |
+{ | |
+ bhist->b_history_head = 0; | |
+ bhist->b_history_tail = 0; | |
+} | |
+extern void buffer_trace(const char *function, struct buffer_head *dest, | |
+ struct buffer_head *src, char *info); | |
+extern void print_buffer_fields(struct buffer_head *bh); | |
+extern void print_buffer_trace(struct buffer_head *bh); | |
+ | |
+#define BUFFER_STRINGIFY2(X) #X | |
+#define BUFFER_STRINGIFY(X) BUFFER_STRINGIFY2(X) | |
+ | |
+#define BUFFER_TRACE2(dest, src, info) \ | |
+ do { \ | |
+ buffer_trace(__FUNCTION__, (dest), (src), \ | |
+ "["__FILE__":" \ | |
+ BUFFER_STRINGIFY(__LINE__)"] " info); \ | |
+ } while (0) | |
+ | |
+#define BUFFER_TRACE(bh, info) BUFFER_TRACE2(bh, bh, info) | |
+#define JBUFFER_TRACE(jh, info) BUFFER_TRACE(jh2bh(jh), info) | |
+ | |
+#else /* CONFIG_BUFFER_DEBUG */ | |
+ | |
#define buffer_trace_init(bh) do {} while (0) | |
#define print_buffer_fields(bh) do {} while (0) | |
#define print_buffer_trace(bh) do {} while (0) | |
@@ -1101,6 +1129,8 @@ extern int jbd_blocks_per_page(struct inode *inode); | |
#define BUFFER_TRACE2(bh, bh2, info) do {} while (0) | |
#define JBUFFER_TRACE(jh, info) do {} while (0) | |
+#endif /* CONFIG_BUFFER_DEBUG */ | |
+ | |
#endif /* __KERNEL__ */ | |
#endif /* _LINUX_JBD_H */ | |
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h | |
index f1011f7..4842b1c 100644 | |
--- a/include/linux/jbd2.h | |
+++ b/include/linux/jbd2.h | |
@@ -284,7 +284,7 @@ typedef struct journal_superblock_s | |
#define J_ASSERT(assert) BUG_ON(!(assert)) | |
-#if defined(CONFIG_BUFFER_DEBUG) | |
+#if defined(CONFIG_BUFFER_DEBUG2) | |
void buffer_assertion_failure(struct buffer_head *bh); | |
#define J_ASSERT_BH(bh, expr) \ | |
do { \ | |
-- | |
1.7.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment