2010-06-24 03:21:57

by Theodore Ts'o

[permalink] [raw]
Subject: [PATCH, RFC] ext4: Store basic fs error information in the superblock

Provide better RAS serviceability by stashing some basic file system
error information in the ext4 superblock.

This can help provide better hints as to what might have happened to a
file system that contains errors when, for whatever reason,
/var/log/messages or /var/log/kmsgs is not available. Perhaps the
hard drive got detached from computer containing the logs; perhaps the
file system error was from long enough ago that the /var/log/messages
information is no longer available or perhaps the user simply didn't
bother to send them along.

Signed-off-by: "Theodore Ts'o" <[email protected]>
---
fs/ext4/block_validity.c | 8 ++-
fs/ext4/dir.c | 8 ++-
fs/ext4/ext4.h | 64 +++++++++++++++++--------
fs/ext4/ext4_jbd2.c | 50 ++++++++++---------
fs/ext4/ext4_jbd2.h | 47 ++++++++++---------
fs/ext4/extents.c | 10 ++--
fs/ext4/inode.c | 21 +++++---
fs/ext4/mballoc.c | 15 +++---
fs/ext4/move_extent.c | 10 ++--
fs/ext4/namei.c | 14 +++--
fs/ext4/super.c | 117 +++++++++++++++++++++++++++++----------------
11 files changed, 218 insertions(+), 146 deletions(-)

diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
index 5b6973f..d587dae 100644
--- a/fs/ext4/block_validity.c
+++ b/fs/ext4/block_validity.c
@@ -229,16 +229,20 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,

if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
(start_blk + count < start_blk) ||
- (start_blk + count > ext4_blocks_count(sbi->s_es)))
+ (start_blk + count > ext4_blocks_count(sbi->s_es))) {
+ sbi->s_es->s_error_block = cpu_to_le64(start_blk);
return 0;
+ }
while (n) {
entry = rb_entry(n, struct ext4_system_zone, node);
if (start_blk + count - 1 < entry->start_blk)
n = n->rb_left;
else if (start_blk >= (entry->start_blk + entry->count))
n = n->rb_right;
- else
+ else {
+ sbi->s_es->s_error_block = cpu_to_le64(start_blk);
return 0;
+ }
}
return 1;
}
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index 2965c39..8c929ce 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -61,7 +61,8 @@ static unsigned char get_dtype(struct super_block *sb, int filetype)
}


-int ext4_check_dir_entry(const char *function, struct inode *dir,
+int ext4_check_dir_entry(const char *function, unsigned int line,
+ struct inode *dir,
struct ext4_dir_entry_2 *de,
struct buffer_head *bh,
unsigned int offset)
@@ -83,7 +84,7 @@ int ext4_check_dir_entry(const char *function, struct inode *dir,
error_msg = "inode out of bounds";

if (error_msg != NULL)
- ext4_error_inode(function, dir,
+ ext4_error_inode(function, line, dir,
"bad entry in directory: %s - block=%llu"
"offset=%u(%u), inode=%u, rec_len=%d, name_len=%d",
error_msg, (unsigned long long) bh->b_blocknr,
@@ -194,7 +195,8 @@ revalidate:
while (!error && filp->f_pos < inode->i_size
&& offset < sb->s_blocksize) {
de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
- if (!ext4_check_dir_entry("ext4_readdir", inode, de,
+ if (!ext4_check_dir_entry(__func__, __LINE__,
+ inode, de,
bh, offset)) {
/*
* On error, skip the f_pos to the next block
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 8b6d297..f51d961 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -57,10 +57,10 @@
#endif

#define EXT4_ERROR_INODE(inode, fmt, a...) \
- ext4_error_inode(__func__, (inode), (fmt), ## a)
+ ext4_error_inode(__func__, __LINE__, (inode), (fmt), ## a)

#define EXT4_ERROR_FILE(file, fmt, a...) \
- ext4_error_file(__func__, (file), (fmt), ## a)
+ ext4_error_file(__func__, __LINE__, (file), (fmt), ## a)

/* data type for block offset of block group */
typedef int ext4_grpblk_t;
@@ -1002,7 +1002,19 @@ struct ext4_super_block {
__u8 s_reserved_char_pad2;
__le16 s_reserved_pad;
__le64 s_kbytes_written; /* nr of lifetime kilobytes written */
- __u32 s_reserved[160]; /* Padding to the end of the block */
+ __le32 s_snapshot_inum; /* Inode number of active snapshot */
+ __le32 s_snapshot_id; /* sequential ID of active snapshot */
+ __le64 s_snapshot_r_blocks_count; /* reserved blocks for active
+ snapshot's future use */
+ __le32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */
+ __le32 s_first_error_time; /* first time an error happened */
+ __le32 s_last_error_time; /* most recent time of an error */
+ __le32 s_error_ino; /* inode involved in last error */
+ __le64 s_error_block; /* block involved of last error */
+ __le32 s_error_count; /* number of fs errors */
+ char s_error_func[32]; /* function where the error happened */
+ __le32 s_error_line; /* line number where error happened */
+ __le32 s_reserved[140]; /* Padding to the end of the block */
};

#ifdef __KERNEL__
@@ -1509,7 +1521,7 @@ extern unsigned ext4_init_block_bitmap(struct super_block *sb,
ext4_init_block_bitmap(sb, NULL, group, desc)

/* dir.c */
-extern int ext4_check_dir_entry(const char *, struct inode *,
+extern int ext4_check_dir_entry(const char *, unsigned int, struct inode *,
struct ext4_dir_entry_2 *,
struct buffer_head *, unsigned int);
extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
@@ -1615,25 +1627,35 @@ extern int ext4_group_extend(struct super_block *sb,
ext4_fsblk_t n_blocks_count);

/* super.c */
-extern void __ext4_error(struct super_block *, const char *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-#define ext4_error(sb, message...) __ext4_error(sb, __func__, ## message)
-extern void ext4_error_inode(const char *, struct inode *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern void ext4_error_file(const char *, struct file *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern void __ext4_std_error(struct super_block *, const char *, int);
-extern void ext4_abort(struct super_block *, const char *, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-extern void __ext4_warning(struct super_block *, const char *,
+extern void __ext4_error(struct super_block *, const char *, unsigned int, const char *, ...)
+ __attribute__ ((format (printf, 4, 5)));
+#define ext4_error(sb, message...) __ext4_error(sb, __func__, __LINE__, ## message)
+extern void ext4_error_inode(const char *, unsigned int, struct inode *,
+ const char *, ...)
+ __attribute__ ((format (printf, 4, 5)));
+extern void ext4_error_file(const char *, unsigned int, struct file *,
+ const char *, ...)
+ __attribute__ ((format (printf, 4, 5)));
+extern void __ext4_std_error(struct super_block *, const char *,
+ unsigned int, int);
+extern void __ext4_abort(struct super_block *, const char *, unsigned int,
+ const char *, ...)
+ __attribute__ ((format (printf, 4, 5)));
+#define ext4_abort(sb, message...) __ext4_abort(sb, __func__, \
+ __LINE__, ## message)
+extern void __ext4_warning(struct super_block *, const char *, unsigned int,
const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
-#define ext4_warning(sb, message...) __ext4_warning(sb, __func__, ## message)
+ __attribute__ ((format (printf, 4, 5)));
+#define ext4_warning(sb, message...) __ext4_warning(sb, __func__, \
+ __LINE__, ## message)
extern void ext4_msg(struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
-extern void ext4_grp_locked_error(struct super_block *, ext4_group_t,
- const char *, const char *, ...)
- __attribute__ ((format (printf, 4, 5)));
+extern void __ext4_grp_locked_error(const char *, unsigned int, \
+ struct super_block *, ext4_group_t, \
+ const char *, ...)
+ __attribute__ ((format (printf, 5, 6)));
+#define ext4_grp_locked_error(sb, grp, message...) \
+ __ext4_grp_locked_error(__func__, __LINE__, (sb), (grp), ## message)
extern void ext4_update_dynamic_rev(struct super_block *sb);
extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb,
__u32 compat);
@@ -1767,7 +1789,7 @@ static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi)
#define ext4_std_error(sb, errno) \
do { \
if ((errno)) \
- __ext4_std_error((sb), __func__, (errno)); \
+ __ext4_std_error((sb), __func__, __LINE__, (errno)); \
} while (0)

#ifdef CONFIG_SMP
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index cfd27b3..93b83e8 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -6,29 +6,29 @@

#include <trace/events/ext4.h>

-int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
- struct buffer_head *bh)
+int __ext4_journal_get_undo_access(const char *where, unsigned int line,
+ handle_t *handle, struct buffer_head *bh)
{
int err = 0;

if (ext4_handle_valid(handle)) {
err = jbd2_journal_get_undo_access(handle, bh);
if (err)
- ext4_journal_abort_handle(where, __func__, bh,
+ ext4_journal_abort_handle(where, line, __func__, bh,
handle, err);
}
return err;
}

-int __ext4_journal_get_write_access(const char *where, handle_t *handle,
- struct buffer_head *bh)
+int __ext4_journal_get_write_access(const char *where, unsigned int line,
+ handle_t *handle, struct buffer_head *bh)
{
int err = 0;

if (ext4_handle_valid(handle)) {
err = jbd2_journal_get_write_access(handle, bh);
if (err)
- ext4_journal_abort_handle(where, __func__, bh,
+ ext4_journal_abort_handle(where, line, __func__, bh,
handle, err);
}
return err;
@@ -46,9 +46,9 @@ int __ext4_journal_get_write_access(const char *where, handle_t *handle,
* If the handle isn't valid we're not journaling, but we still need to
* call into ext4_journal_revoke() to put the buffer head.
*/
-int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
- struct inode *inode, struct buffer_head *bh,
- ext4_fsblk_t blocknr)
+int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
+ int is_metadata, struct inode *inode,
+ struct buffer_head *bh, ext4_fsblk_t blocknr)
{
int err;

@@ -79,8 +79,8 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
BUFFER_TRACE(bh, "call jbd2_journal_forget");
err = jbd2_journal_forget(handle, bh);
if (err)
- ext4_journal_abort_handle(where, __func__, bh,
- handle, err);
+ ext4_journal_abort_handle(where, line, __func__,
+ bh, handle, err);
return err;
}
return 0;
@@ -92,15 +92,16 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
BUFFER_TRACE(bh, "call jbd2_journal_revoke");
err = jbd2_journal_revoke(handle, blocknr, bh);
if (err) {
- ext4_journal_abort_handle(where, __func__, bh, handle, err);
- ext4_abort(inode->i_sb, __func__,
+ ext4_journal_abort_handle(where, line, __func__,
+ bh, handle, err);
+ __ext4_abort(inode->i_sb, where, line,
"error %d when attempting revoke", err);
}
BUFFER_TRACE(bh, "exit");
return err;
}

-int __ext4_journal_get_create_access(const char *where,
+int __ext4_journal_get_create_access(const char *where, unsigned int line,
handle_t *handle, struct buffer_head *bh)
{
int err = 0;
@@ -108,22 +109,23 @@ int __ext4_journal_get_create_access(const char *where,
if (ext4_handle_valid(handle)) {
err = jbd2_journal_get_create_access(handle, bh);
if (err)
- ext4_journal_abort_handle(where, __func__, bh,
- handle, err);
+ ext4_journal_abort_handle(where, line, __func__,
+ bh, handle, err);
}
return err;
}

-int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
- struct inode *inode, struct buffer_head *bh)
+int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
+ handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
{
int err = 0;

if (ext4_handle_valid(handle)) {
err = jbd2_journal_dirty_metadata(handle, bh);
if (err)
- ext4_journal_abort_handle(where, __func__, bh,
- handle, err);
+ ext4_journal_abort_handle(where, line,__func__,
+ bh, handle, err);
} else {
if (inode)
mark_buffer_dirty_inode(bh, inode);
@@ -144,8 +146,8 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
return err;
}

-int __ext4_handle_dirty_super(const char *where, handle_t *handle,
- struct super_block *sb)
+int __ext4_handle_dirty_super(const char *where, unsigned int line,
+ handle_t *handle, struct super_block *sb)
{
struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
int err = 0;
@@ -153,8 +155,8 @@ int __ext4_handle_dirty_super(const char *where, handle_t *handle,
if (ext4_handle_valid(handle)) {
err = jbd2_journal_dirty_metadata(handle, bh);
if (err)
- ext4_journal_abort_handle(where, __func__, bh,
- handle, err);
+ ext4_journal_abort_handle(where, line, __func__,
+ bh, handle, err);
} else
sb->s_dirt = 1;
return err;
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 38d1e66..b0bd792 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -122,44 +122,47 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode);
/*
* Wrapper functions with which ext4 calls into JBD.
*/
-void ext4_journal_abort_handle(const char *caller, const char *err_fn,
+void ext4_journal_abort_handle(const char *caller, unsigned int line,
+ const char *err_fn,
struct buffer_head *bh, handle_t *handle, int err);

-int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
- struct buffer_head *bh);
+int __ext4_journal_get_undo_access(const char *where, unsigned int line,
+ handle_t *handle, struct buffer_head *bh);

-int __ext4_journal_get_write_access(const char *where, handle_t *handle,
- struct buffer_head *bh);
+int __ext4_journal_get_write_access(const char *where, unsigned int line,
+ handle_t *handle, struct buffer_head *bh);

-int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
- struct inode *inode, struct buffer_head *bh,
- ext4_fsblk_t blocknr);
+int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
+ int is_metadata, struct inode *inode,
+ struct buffer_head *bh, ext4_fsblk_t blocknr);

-int __ext4_journal_get_create_access(const char *where,
+int __ext4_journal_get_create_access(const char *where, unsigned int line,
handle_t *handle, struct buffer_head *bh);

-int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
- struct inode *inode, struct buffer_head *bh);
+int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
+ handle_t *handle, struct inode *inode,
+ struct buffer_head *bh);

-int __ext4_handle_dirty_super(const char *where, handle_t *handle,
- struct super_block *sb);
+int __ext4_handle_dirty_super(const char *where, unsigned int line,
+ handle_t *handle, struct super_block *sb);

#define ext4_journal_get_undo_access(handle, bh) \
- __ext4_journal_get_undo_access(__func__, (handle), (bh))
+ __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
#define ext4_journal_get_write_access(handle, bh) \
- __ext4_journal_get_write_access(__func__, (handle), (bh))
+ __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
#define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
- __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh),\
- (block_nr))
+ __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \
+ (bh), (block_nr))
#define ext4_journal_get_create_access(handle, bh) \
- __ext4_journal_get_create_access(__func__, (handle), (bh))
+ __ext4_journal_get_create_access(__func__, __LINE__, (handle), (bh))
#define ext4_handle_dirty_metadata(handle, inode, bh) \
- __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh))
+ __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
+ (bh))
#define ext4_handle_dirty_super(handle, sb) \
- __ext4_handle_dirty_super(__func__, (handle), (sb))
+ __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))

handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
-int __ext4_journal_stop(const char *where, handle_t *handle);
+int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);

#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)

@@ -212,7 +215,7 @@ static inline handle_t *ext4_journal_start(struct inode *inode, int nblocks)
}

#define ext4_journal_stop(handle) \
- __ext4_journal_stop(__func__, (handle))
+ __ext4_journal_stop(__func__, __LINE__, (handle))

static inline handle_t *ext4_journal_current_handle(void)
{
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 346de3d..fac5516 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -401,9 +401,9 @@ static int ext4_valid_extent_entries(struct inode *inode,
return 1;
}

-static int __ext4_ext_check(const char *function, struct inode *inode,
- struct ext4_extent_header *eh,
- int depth)
+static int __ext4_ext_check(const char *function, unsigned int line,
+ struct inode *inode, struct ext4_extent_header *eh,
+ int depth)
{
const char *error_msg;
int max = 0;
@@ -436,7 +436,7 @@ static int __ext4_ext_check(const char *function, struct inode *inode,
return 0;

corrupted:
- ext4_error_inode(function, inode,
+ ext4_error_inode(function, line, inode,
"bad header/extent: %s - magic %x, "
"entries %u, max %u(%u), depth %u(%u)",
error_msg, le16_to_cpu(eh->eh_magic),
@@ -447,7 +447,7 @@ corrupted:
}

#define ext4_ext_check(inode, eh, depth) \
- __ext4_ext_check(__func__, inode, eh, depth)
+ __ext4_ext_check(__func__, __LINE__, inode, eh, depth)

int ext4_ext_check_inode(struct inode *inode)
{
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index b485987..aade5e4 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -337,9 +337,11 @@ static int ext4_block_to_path(struct inode *inode,
return n;
}

-static int __ext4_check_blockref(const char *function, struct inode *inode,
+static int __ext4_check_blockref(const char *function, unsigned int line,
+ struct inode *inode,
__le32 *p, unsigned int max)
{
+ struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
__le32 *bref = p;
unsigned int blk;

@@ -348,7 +350,8 @@ static int __ext4_check_blockref(const char *function, struct inode *inode,
if (blk &&
unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
blk, 1))) {
- ext4_error_inode(function, inode,
+ es->s_error_block = cpu_to_le64(blk);
+ ext4_error_inode(function, line, inode,
"invalid block reference %u", blk);
return -EIO;
}
@@ -358,11 +361,13 @@ static int __ext4_check_blockref(const char *function, struct inode *inode,


#define ext4_check_indirect_blockref(inode, bh) \
- __ext4_check_blockref(__func__, inode, (__le32 *)(bh)->b_data, \
+ __ext4_check_blockref(__func__, __LINE__, inode, \
+ (__le32 *)(bh)->b_data, \
EXT4_ADDR_PER_BLOCK((inode)->i_sb))

#define ext4_check_inode_blockref(inode) \
- __ext4_check_blockref(__func__, inode, EXT4_I(inode)->i_data, \
+ __ext4_check_blockref(__func__, __LINE__, inode, \
+ EXT4_I(inode)->i_data, \
EXT4_NDIR_BLOCKS)

/**
@@ -1129,11 +1134,12 @@ void ext4_da_update_reserve_space(struct inode *inode,
}

static int check_block_validity(struct inode *inode, const char *func,
+ unsigned int line,
struct ext4_map_blocks *map)
{
if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk,
map->m_len)) {
- ext4_error_inode(func, inode,
+ ext4_error_inode(func, line, inode,
"lblock %lu mapped to illegal pblock %llu "
"(length %d)", (unsigned long) map->m_lblk,
map->m_pblk, map->m_len);
@@ -1244,7 +1250,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
up_read((&EXT4_I(inode)->i_data_sem));

if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
- int ret = check_block_validity(inode, __func__, map);
+ int ret = check_block_validity(inode, __func__, __LINE__, map);
if (ret != 0)
return ret;
}
@@ -1324,8 +1330,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,

up_write((&EXT4_I(inode)->i_data_sem));
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
- int ret = check_block_validity(inode,
- "ext4_map_blocks_after_alloc",
+ int ret = check_block_validity(inode, __func__, __LINE__,
map);
if (ret != 0)
return ret;
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index b2948b0..92ae5e8 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -446,7 +446,7 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b,
blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
blocknr += first + i;
ext4_grp_locked_error(sb, e4b->bd_group,
- __func__, "double-free of inode"
+ "double-free of inode"
" %lu's block %llu(bit %u in group %u)",
inode ? inode->i_ino : 0, blocknr,
first + i, e4b->bd_group);
@@ -712,7 +712,7 @@ void ext4_mb_generate_buddy(struct super_block *sb,
grp->bb_fragments = fragments;

if (free != grp->bb_free) {
- ext4_grp_locked_error(sb, group, __func__,
+ ext4_grp_locked_error(sb, group,
"EXT4-fs: group %u: %u blocks in bitmap, %u in gd",
group, free, grp->bb_free);
/*
@@ -1296,7 +1296,7 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
blocknr += block;
ext4_grp_locked_error(sb, e4b->bd_group,
- __func__, "double-free of inode"
+ "double-free of inode"
" %lu's block %llu(bit %u in group %u)",
inode ? inode->i_ino : 0, blocknr, block,
e4b->bd_group);
@@ -1789,7 +1789,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
* we have free blocks
*/
ext4_grp_locked_error(sb, e4b->bd_group,
- __func__, "%d free blocks as per "
+ "%d free blocks as per "
"group info. But bitmap says 0",
free);
break;
@@ -1799,7 +1799,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
BUG_ON(ex.fe_len <= 0);
if (free < ex.fe_len) {
ext4_grp_locked_error(sb, e4b->bd_group,
- __func__, "%d free blocks as per "
+ "%d free blocks as per "
"group info. But got %d blocks",
free, ex.fe_len);
/*
@@ -3584,8 +3584,7 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh,
pa, (unsigned long) pa->pa_lstart,
(unsigned long) pa->pa_pstart,
(unsigned long) pa->pa_len);
- ext4_grp_locked_error(sb, group,
- __func__, "free %u, pa_free %u",
+ ext4_grp_locked_error(sb, group, "free %u, pa_free %u",
free, pa->pa_free);
/*
* pa is already deleted so we use the value obtained
@@ -4427,7 +4426,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
else if (block >= (entry->start_blk + entry->count))
n = &(*n)->rb_right;
else {
- ext4_grp_locked_error(sb, e4b->bd_group, __func__,
+ ext4_grp_locked_error(sb, e4b->bd_group,
"Double free of blocks %d (%d %d)",
block, entry->start_blk, entry->count);
return 0;
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 52abfa1..5f1ed9f 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -148,17 +148,17 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
*/
static int
mext_check_null_inode(struct inode *inode1, struct inode *inode2,
- const char *function)
+ const char *function, unsigned int line)
{
int ret = 0;

if (inode1 == NULL) {
- __ext4_error(inode2->i_sb, function,
+ __ext4_error(inode2->i_sb, function, line,
"Both inodes should not be NULL: "
"inode1 NULL inode2 %lu", inode2->i_ino);
ret = -EIO;
} else if (inode2 == NULL) {
- __ext4_error(inode1->i_sb, function,
+ __ext4_error(inode1->i_sb, function, line,
"Both inodes should not be NULL: "
"inode1 %lu inode2 NULL", inode1->i_ino);
ret = -EIO;
@@ -1084,7 +1084,7 @@ mext_inode_double_lock(struct inode *inode1, struct inode *inode2)

BUG_ON(inode1 == NULL && inode2 == NULL);

- ret = mext_check_null_inode(inode1, inode2, __func__);
+ ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__);
if (ret < 0)
goto out;

@@ -1121,7 +1121,7 @@ mext_inode_double_unlock(struct inode *inode1, struct inode *inode2)

BUG_ON(inode1 == NULL && inode2 == NULL);

- ret = mext_check_null_inode(inode1, inode2, __func__);
+ ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__);
if (ret < 0)
goto out;

diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 5a61f77..66b6e1c 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -605,7 +605,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
dir->i_sb->s_blocksize -
EXT4_DIR_REC_LEN(0));
for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
- if (!ext4_check_dir_entry("htree_dirblock_to_tree", dir, de, bh,
+ if (!ext4_check_dir_entry(__func__, __LINE__, dir, de, bh,
(block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
+((char *)de - bh->b_data))) {
/* On error, skip the f_pos to the next block. */
@@ -844,7 +844,7 @@ static inline int search_dirblock(struct buffer_head *bh,
if ((char *) de + namelen <= dlimit &&
ext4_match (namelen, name, de)) {
/* found a match - just to be sure, do a full check */
- if (!ext4_check_dir_entry("ext4_find_entry",
+ if (!ext4_check_dir_entry(__func__, __LINE__,
dir, de, bh, offset))
return -1;
*res_dir = de;
@@ -1019,7 +1019,8 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
int off = (block << EXT4_BLOCK_SIZE_BITS(sb))
+ ((char *) de - bh->b_data);

- if (!ext4_check_dir_entry(__func__, dir, de, bh, off)) {
+ if (!ext4_check_dir_entry(__func__, __LINE__, dir,
+ de, bh, off)) {
brelse(bh);
*err = ERR_BAD_DX_DIR;
goto errout;
@@ -1303,7 +1304,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
de = (struct ext4_dir_entry_2 *)bh->b_data;
top = bh->b_data + blocksize - reclen;
while ((char *) de <= top) {
- if (!ext4_check_dir_entry("ext4_add_entry", dir, de,
+ if (!ext4_check_dir_entry(__func__, __LINE__, dir, de,
bh, offset))
return -EIO;
if (ext4_match(namelen, name, de))
@@ -1671,7 +1672,7 @@ static int ext4_delete_entry(handle_t *handle,
pde = NULL;
de = (struct ext4_dir_entry_2 *) bh->b_data;
while (i < bh->b_size) {
- if (!ext4_check_dir_entry("ext4_delete_entry", dir, de, bh, i))
+ if (!ext4_check_dir_entry(__func__, __LINE__, dir, de, bh, i))
return -EIO;
if (de == de_del) {
BUFFER_TRACE(bh, "get_write_access");
@@ -1954,7 +1955,8 @@ static int empty_dir(struct inode *inode)
}
de = (struct ext4_dir_entry_2 *) bh->b_data;
}
- if (!ext4_check_dir_entry("empty_dir", inode, de, bh, offset)) {
+ if (!ext4_check_dir_entry(__func__, __LINE__, inode,
+ de, bh, offset)) {
de = (struct ext4_dir_entry_2 *)(bh->b_data +
sb->s_blocksize);
offset = (offset | (sb->s_blocksize - 1)) + 1;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 422a4ce..0c689fb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -248,7 +248,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
journal = EXT4_SB(sb)->s_journal;
if (journal) {
if (is_journal_aborted(journal)) {
- ext4_abort(sb, __func__, "Detected aborted journal");
+ ext4_abort(sb, "Detected aborted journal");
return ERR_PTR(-EROFS);
}
return jbd2_journal_start(journal, nblocks);
@@ -262,7 +262,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
* that sync() will call the filesystem's write_super callback if
* appropriate.
*/
-int __ext4_journal_stop(const char *where, handle_t *handle)
+int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
{
struct super_block *sb;
int err;
@@ -279,12 +279,13 @@ int __ext4_journal_stop(const char *where, handle_t *handle)
if (!err)
err = rc;
if (err)
- __ext4_std_error(sb, where, err);
+ __ext4_std_error(sb, where, line, err);
return err;
}

-void ext4_journal_abort_handle(const char *caller, const char *err_fn,
- struct buffer_head *bh, handle_t *handle, int err)
+void ext4_journal_abort_handle(const char *caller, unsigned int line,
+ const char *err_fn, struct buffer_head *bh,
+ handle_t *handle, int err)
{
char nbuf[16];
const char *errstr = ext4_decode_error(NULL, err, nbuf);
@@ -300,8 +301,8 @@ void ext4_journal_abort_handle(const char *caller, const char *err_fn,
if (is_handle_aborted(handle))
return;

- printk(KERN_ERR "%s: aborting transaction: %s in %s\n",
- caller, errstr, err_fn);
+ printk(KERN_ERR "%s:%d: aborting transaction: %s in %s\n",
+ caller, line, errstr, err_fn);

jbd2_journal_abort_handle(handle);
}
@@ -327,6 +328,10 @@ static void ext4_handle_error(struct super_block *sb)

EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
+ es->s_last_error_time = cpu_to_le32(get_seconds());
+ if (!es->s_first_error_time)
+ es->s_first_error_time = es->s_last_error_time;
+ es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1);

if (sb->s_flags & MS_RDONLY)
return;
@@ -349,12 +354,16 @@ static void ext4_handle_error(struct super_block *sb)
}

void __ext4_error(struct super_block *sb, const char *function,
- const char *fmt, ...)
+ unsigned int line, const char *fmt, ...)
{
va_list args;
+ struct ext4_super_block *es = EXT4_SB(sb)->s_es;

+ strncpy(es->s_error_func, function, sizeof(es->s_error_func));
+ es->s_error_line = cpu_to_le32(line);
va_start(args, fmt);
- printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
+ printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
+ function, line);
vprintk(fmt, args);
printk("\n");
va_end(args);
@@ -362,14 +371,18 @@ void __ext4_error(struct super_block *sb, const char *function,
ext4_handle_error(sb);
}

-void ext4_error_inode(const char *function, struct inode *inode,
- const char *fmt, ...)
+void ext4_error_inode(const char *function, unsigned int line,
+ struct inode *inode, const char *fmt, ...)
{
va_list args;
+ struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;

+ strncpy(es->s_error_func, function, sizeof(es->s_error_func));
+ es->s_error_line = cpu_to_le32(line);
va_start(args, fmt);
- printk(KERN_CRIT "EXT4-fs error (device %s): %s: inode #%lu: (comm %s) ",
- inode->i_sb->s_id, function, inode->i_ino, current->comm);
+ printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: "
+ "(comm %s) ",
+ inode->i_sb->s_id, function, line, inode->i_ino, current->comm);
vprintk(fmt, args);
printk("\n");
va_end(args);
@@ -377,20 +390,26 @@ void ext4_error_inode(const char *function, struct inode *inode,
ext4_handle_error(inode->i_sb);
}

-void ext4_error_file(const char *function, struct file *file,
- const char *fmt, ...)
+void ext4_error_file(const char *function, unsigned int line,
+ struct file *file, const char *fmt, ...)
{
va_list args;
+ struct ext4_super_block *es;
struct inode *inode = file->f_dentry->d_inode;
char pathname[80], *path;

+ es = EXT4_SB(inode->i_sb)->s_es;
+ strncpy(es->s_error_func, function, sizeof(es->s_error_func));
+ es->s_error_line = cpu_to_le32(line);
va_start(args, fmt);
path = d_path(&(file->f_path), pathname, sizeof(pathname));
if (!path)
path = "(unknown)";
printk(KERN_CRIT
- "EXT4-fs error (device %s): %s: inode #%lu (comm %s path %s): ",
- inode->i_sb->s_id, function, inode->i_ino, current->comm, path);
+ "EXT4-fs error (device %s): %s:%d: inode #%lu "
+ "(comm %s path %s): ",
+ inode->i_sb->s_id, function, line, inode->i_ino,
+ current->comm, path);
vprintk(fmt, args);
printk("\n");
va_end(args);
@@ -435,10 +454,12 @@ static const char *ext4_decode_error(struct super_block *sb, int errno,
/* __ext4_std_error decodes expected errors from journaling functions
* automatically and invokes the appropriate error response. */

-void __ext4_std_error(struct super_block *sb, const char *function, int errno)
+void __ext4_std_error(struct super_block *sb, const char *function,
+ unsigned int line, int errno)
{
char nbuf[16];
const char *errstr;
+ struct ext4_super_block *es = EXT4_SB(sb)->s_es;

/* Special case: if the error is EROFS, and we're not already
* inside a transaction, then there's really no point in logging
@@ -448,8 +469,10 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno)
return;

errstr = ext4_decode_error(sb, errno, nbuf);
- printk(KERN_CRIT "EXT4-fs error (device %s) in %s: %s\n",
- sb->s_id, function, errstr);
+ printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
+ sb->s_id, function, line, errstr);
+ strncpy(es->s_error_func, function, sizeof(es->s_error_func));
+ es->s_error_line = cpu_to_le32(line);

ext4_handle_error(sb);
}
@@ -464,29 +487,32 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno)
* case we take the easy way out and panic immediately.
*/

-void ext4_abort(struct super_block *sb, const char *function,
- const char *fmt, ...)
+void __ext4_abort(struct super_block *sb, const char *function,
+ unsigned int line, const char *fmt, ...)
{
va_list args;
+ struct ext4_super_block *es = EXT4_SB(sb)->s_es;

va_start(args, fmt);
- printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
+ printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
+ function, line);
vprintk(fmt, args);
printk("\n");
va_end(args);

+ if ((sb->s_flags & MS_RDONLY) == 0) {
+ ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
+ EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
+ sb->s_flags |= MS_RDONLY;
+ EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
+ if (EXT4_SB(sb)->s_journal)
+ jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
+ strncpy(es->s_error_func, function, sizeof(es->s_error_func));
+ es->s_error_line = cpu_to_le32(line);
+ ext4_commit_super(sb, 1);
+ }
if (test_opt(sb, ERRORS_PANIC))
panic("EXT4-fs panic from previous error\n");
-
- if (sb->s_flags & MS_RDONLY)
- return;
-
- ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
- EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
- sb->s_flags |= MS_RDONLY;
- EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
- if (EXT4_SB(sb)->s_journal)
- jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
}

void ext4_msg (struct super_block * sb, const char *prefix,
@@ -502,20 +528,21 @@ void ext4_msg (struct super_block * sb, const char *prefix,
}

void __ext4_warning(struct super_block *sb, const char *function,
- const char *fmt, ...)
+ unsigned int line, const char *fmt, ...)
{
va_list args;

va_start(args, fmt);
- printk(KERN_WARNING "EXT4-fs warning (device %s): %s: ",
- sb->s_id, function);
+ printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: ",
+ sb->s_id, function, line);
vprintk(fmt, args);
printk("\n");
va_end(args);
}

-void ext4_grp_locked_error(struct super_block *sb, ext4_group_t grp,
- const char *function, const char *fmt, ...)
+void __ext4_grp_locked_error(const char *function, unsigned int line,
+ struct super_block *sb, ext4_group_t grp,
+ const char *fmt, ...)
__releases(bitlock)
__acquires(bitlock)
{
@@ -523,14 +550,21 @@ __acquires(bitlock)
struct ext4_super_block *es = EXT4_SB(sb)->s_es;

va_start(args, fmt);
- printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
+ printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
+ function, line);
vprintk(fmt, args);
printk("\n");
va_end(args);
+ strncpy(es->s_error_func, function, sizeof(es->s_error_func));
+ es->s_error_line = cpu_to_le32(line);

if (test_opt(sb, ERRORS_CONT)) {
EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
+ es->s_last_error_time = cpu_to_le32(get_seconds());
+ if (!es->s_first_error_time)
+ es->s_first_error_time = es->s_last_error_time;
+ es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1);
ext4_commit_super(sb, 0);
return;
}
@@ -660,8 +694,7 @@ static void ext4_put_super(struct super_block *sb)
err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
if (err < 0)
- ext4_abort(sb, __func__,
- "Couldn't clean up the journal");
+ ext4_abort(sb, "Couldn't clean up the journal");
}

ext4_release_system_zone(sb);
@@ -3605,7 +3638,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
}

if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
- ext4_abort(sb, __func__, "Abort forced by user");
+ ext4_abort(sb, "Abort forced by user");

sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
(test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
--
1.7.0.4



2010-06-24 10:52:05

by Dmitry Monakhov

[permalink] [raw]
Subject: Re: [PATCH, RFC] ext4: Store basic fs error information in the superblock

Theodore Ts'o <[email protected]> writes:

> Provide better RAS serviceability by stashing some basic file system
> error information in the ext4 superblock.
>
> This can help provide better hints as to what might have happened to a
> file system that contains errors when, for whatever reason,
> /var/log/messages or /var/log/kmsgs is not available. Perhaps the
> hard drive got detached from computer containing the logs; perhaps the
> file system error was from long enough ago that the /var/log/messages
> information is no longer available or perhaps the user simply didn't
> bother to send them along.
Excellent choice. s_error_XXX fields finally allow one to detect errors
in determinant meaner. And most stress test finally will do it after
umount :).
Minor question: What snapshot staff is doing in that patch?
>
> Signed-off-by: "Theodore Ts'o" <[email protected]>
> ---
> fs/ext4/block_validity.c | 8 ++-
> fs/ext4/dir.c | 8 ++-
> fs/ext4/ext4.h | 64 +++++++++++++++++--------
> fs/ext4/ext4_jbd2.c | 50 ++++++++++---------
> fs/ext4/ext4_jbd2.h | 47 ++++++++++---------
> fs/ext4/extents.c | 10 ++--
> fs/ext4/inode.c | 21 +++++---
> fs/ext4/mballoc.c | 15 +++---
> fs/ext4/move_extent.c | 10 ++--
> fs/ext4/namei.c | 14 +++--
> fs/ext4/super.c | 117 +++++++++++++++++++++++++++++----------------
> 11 files changed, 218 insertions(+), 146 deletions(-)
>
> diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
> index 5b6973f..d587dae 100644
> --- a/fs/ext4/block_validity.c
> +++ b/fs/ext4/block_validity.c
> @@ -229,16 +229,20 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
>
> if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
> (start_blk + count < start_blk) ||
> - (start_blk + count > ext4_blocks_count(sbi->s_es)))
> + (start_blk + count > ext4_blocks_count(sbi->s_es))) {
> + sbi->s_es->s_error_block = cpu_to_le64(start_blk);
> return 0;
> + }
> while (n) {
> entry = rb_entry(n, struct ext4_system_zone, node);
> if (start_blk + count - 1 < entry->start_blk)
> n = n->rb_left;
> else if (start_blk >= (entry->start_blk + entry->count))
> n = n->rb_right;
> - else
> + else {
> + sbi->s_es->s_error_block = cpu_to_le64(start_blk);
> return 0;
> + }
> }
> return 1;
> }
> diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
> index 2965c39..8c929ce 100644
> --- a/fs/ext4/dir.c
> +++ b/fs/ext4/dir.c
> @@ -61,7 +61,8 @@ static unsigned char get_dtype(struct super_block *sb, int filetype)
> }
>
>
> -int ext4_check_dir_entry(const char *function, struct inode *dir,
> +int ext4_check_dir_entry(const char *function, unsigned int line,
> + struct inode *dir,
> struct ext4_dir_entry_2 *de,
> struct buffer_head *bh,
> unsigned int offset)
> @@ -83,7 +84,7 @@ int ext4_check_dir_entry(const char *function, struct inode *dir,
> error_msg = "inode out of bounds";
>
> if (error_msg != NULL)
> - ext4_error_inode(function, dir,
> + ext4_error_inode(function, line, dir,
> "bad entry in directory: %s - block=%llu"
> "offset=%u(%u), inode=%u, rec_len=%d, name_len=%d",
> error_msg, (unsigned long long) bh->b_blocknr,
> @@ -194,7 +195,8 @@ revalidate:
> while (!error && filp->f_pos < inode->i_size
> && offset < sb->s_blocksize) {
> de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
> - if (!ext4_check_dir_entry("ext4_readdir", inode, de,
> + if (!ext4_check_dir_entry(__func__, __LINE__,
> + inode, de,
> bh, offset)) {
> /*
> * On error, skip the f_pos to the next block
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 8b6d297..f51d961 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -57,10 +57,10 @@
> #endif
>
> #define EXT4_ERROR_INODE(inode, fmt, a...) \
> - ext4_error_inode(__func__, (inode), (fmt), ## a)
> + ext4_error_inode(__func__, __LINE__, (inode), (fmt), ## a)
>
> #define EXT4_ERROR_FILE(file, fmt, a...) \
> - ext4_error_file(__func__, (file), (fmt), ## a)
> + ext4_error_file(__func__, __LINE__, (file), (fmt), ## a)
>
> /* data type for block offset of block group */
> typedef int ext4_grpblk_t;
> @@ -1002,7 +1002,19 @@ struct ext4_super_block {
> __u8 s_reserved_char_pad2;
> __le16 s_reserved_pad;
> __le64 s_kbytes_written; /* nr of lifetime kilobytes written */
> - __u32 s_reserved[160]; /* Padding to the end of the block */
> + __le32 s_snapshot_inum; /* Inode number of active snapshot */
> + __le32 s_snapshot_id; /* sequential ID of active snapshot */
> + __le64 s_snapshot_r_blocks_count; /* reserved blocks for active
> + snapshot's future use */
> + __le32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */
> + __le32 s_first_error_time; /* first time an error happened */
> + __le32 s_last_error_time; /* most recent time of an error */
> + __le32 s_error_ino; /* inode involved in last error */
> + __le64 s_error_block; /* block involved of last error */
> + __le32 s_error_count; /* number of fs errors */
> + char s_error_func[32]; /* function where the error happened */
> + __le32 s_error_line; /* line number where error happened */
> + __le32 s_reserved[140]; /* Padding to the end of the block */
> };
>
> #ifdef __KERNEL__
> @@ -1509,7 +1521,7 @@ extern unsigned ext4_init_block_bitmap(struct super_block *sb,
> ext4_init_block_bitmap(sb, NULL, group, desc)
>
> /* dir.c */
> -extern int ext4_check_dir_entry(const char *, struct inode *,
> +extern int ext4_check_dir_entry(const char *, unsigned int, struct inode *,
> struct ext4_dir_entry_2 *,
> struct buffer_head *, unsigned int);
> extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
> @@ -1615,25 +1627,35 @@ extern int ext4_group_extend(struct super_block *sb,
> ext4_fsblk_t n_blocks_count);
>
> /* super.c */
> -extern void __ext4_error(struct super_block *, const char *, const char *, ...)
> - __attribute__ ((format (printf, 3, 4)));
> -#define ext4_error(sb, message...) __ext4_error(sb, __func__, ## message)
> -extern void ext4_error_inode(const char *, struct inode *, const char *, ...)
> - __attribute__ ((format (printf, 3, 4)));
> -extern void ext4_error_file(const char *, struct file *, const char *, ...)
> - __attribute__ ((format (printf, 3, 4)));
> -extern void __ext4_std_error(struct super_block *, const char *, int);
> -extern void ext4_abort(struct super_block *, const char *, const char *, ...)
> - __attribute__ ((format (printf, 3, 4)));
> -extern void __ext4_warning(struct super_block *, const char *,
> +extern void __ext4_error(struct super_block *, const char *, unsigned int, const char *, ...)
> + __attribute__ ((format (printf, 4, 5)));
> +#define ext4_error(sb, message...) __ext4_error(sb, __func__, __LINE__, ## message)
> +extern void ext4_error_inode(const char *, unsigned int, struct inode *,
> + const char *, ...)
> + __attribute__ ((format (printf, 4, 5)));
> +extern void ext4_error_file(const char *, unsigned int, struct file *,
> + const char *, ...)
> + __attribute__ ((format (printf, 4, 5)));
> +extern void __ext4_std_error(struct super_block *, const char *,
> + unsigned int, int);
> +extern void __ext4_abort(struct super_block *, const char *, unsigned int,
> + const char *, ...)
> + __attribute__ ((format (printf, 4, 5)));
> +#define ext4_abort(sb, message...) __ext4_abort(sb, __func__, \
> + __LINE__, ## message)
> +extern void __ext4_warning(struct super_block *, const char *, unsigned int,
> const char *, ...)
> - __attribute__ ((format (printf, 3, 4)));
> -#define ext4_warning(sb, message...) __ext4_warning(sb, __func__, ## message)
> + __attribute__ ((format (printf, 4, 5)));
> +#define ext4_warning(sb, message...) __ext4_warning(sb, __func__, \
> + __LINE__, ## message)
> extern void ext4_msg(struct super_block *, const char *, const char *, ...)
> __attribute__ ((format (printf, 3, 4)));
> -extern void ext4_grp_locked_error(struct super_block *, ext4_group_t,
> - const char *, const char *, ...)
> - __attribute__ ((format (printf, 4, 5)));
> +extern void __ext4_grp_locked_error(const char *, unsigned int, \
> + struct super_block *, ext4_group_t, \
> + const char *, ...)
> + __attribute__ ((format (printf, 5, 6)));
> +#define ext4_grp_locked_error(sb, grp, message...) \
> + __ext4_grp_locked_error(__func__, __LINE__, (sb), (grp), ## message)
> extern void ext4_update_dynamic_rev(struct super_block *sb);
> extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb,
> __u32 compat);
> @@ -1767,7 +1789,7 @@ static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi)
> #define ext4_std_error(sb, errno) \
> do { \
> if ((errno)) \
> - __ext4_std_error((sb), __func__, (errno)); \
> + __ext4_std_error((sb), __func__, __LINE__, (errno)); \
> } while (0)
>
> #ifdef CONFIG_SMP
> diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
> index cfd27b3..93b83e8 100644
> --- a/fs/ext4/ext4_jbd2.c
> +++ b/fs/ext4/ext4_jbd2.c
> @@ -6,29 +6,29 @@
>
> #include <trace/events/ext4.h>
>
> -int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
> - struct buffer_head *bh)
> +int __ext4_journal_get_undo_access(const char *where, unsigned int line,
> + handle_t *handle, struct buffer_head *bh)
> {
> int err = 0;
>
> if (ext4_handle_valid(handle)) {
> err = jbd2_journal_get_undo_access(handle, bh);
> if (err)
> - ext4_journal_abort_handle(where, __func__, bh,
> + ext4_journal_abort_handle(where, line, __func__, bh,
> handle, err);
> }
> return err;
> }
>
> -int __ext4_journal_get_write_access(const char *where, handle_t *handle,
> - struct buffer_head *bh)
> +int __ext4_journal_get_write_access(const char *where, unsigned int line,
> + handle_t *handle, struct buffer_head *bh)
> {
> int err = 0;
>
> if (ext4_handle_valid(handle)) {
> err = jbd2_journal_get_write_access(handle, bh);
> if (err)
> - ext4_journal_abort_handle(where, __func__, bh,
> + ext4_journal_abort_handle(where, line, __func__, bh,
> handle, err);
> }
> return err;
> @@ -46,9 +46,9 @@ int __ext4_journal_get_write_access(const char *where, handle_t *handle,
> * If the handle isn't valid we're not journaling, but we still need to
> * call into ext4_journal_revoke() to put the buffer head.
> */
> -int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
> - struct inode *inode, struct buffer_head *bh,
> - ext4_fsblk_t blocknr)
> +int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
> + int is_metadata, struct inode *inode,
> + struct buffer_head *bh, ext4_fsblk_t blocknr)
> {
> int err;
>
> @@ -79,8 +79,8 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
> BUFFER_TRACE(bh, "call jbd2_journal_forget");
> err = jbd2_journal_forget(handle, bh);
> if (err)
> - ext4_journal_abort_handle(where, __func__, bh,
> - handle, err);
> + ext4_journal_abort_handle(where, line, __func__,
> + bh, handle, err);
> return err;
> }
> return 0;
> @@ -92,15 +92,16 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
> BUFFER_TRACE(bh, "call jbd2_journal_revoke");
> err = jbd2_journal_revoke(handle, blocknr, bh);
> if (err) {
> - ext4_journal_abort_handle(where, __func__, bh, handle, err);
> - ext4_abort(inode->i_sb, __func__,
> + ext4_journal_abort_handle(where, line, __func__,
> + bh, handle, err);
> + __ext4_abort(inode->i_sb, where, line,
> "error %d when attempting revoke", err);
> }
> BUFFER_TRACE(bh, "exit");
> return err;
> }
>
> -int __ext4_journal_get_create_access(const char *where,
> +int __ext4_journal_get_create_access(const char *where, unsigned int line,
> handle_t *handle, struct buffer_head *bh)
> {
> int err = 0;
> @@ -108,22 +109,23 @@ int __ext4_journal_get_create_access(const char *where,
> if (ext4_handle_valid(handle)) {
> err = jbd2_journal_get_create_access(handle, bh);
> if (err)
> - ext4_journal_abort_handle(where, __func__, bh,
> - handle, err);
> + ext4_journal_abort_handle(where, line, __func__,
> + bh, handle, err);
> }
> return err;
> }
>
> -int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
> - struct inode *inode, struct buffer_head *bh)
> +int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
> + handle_t *handle, struct inode *inode,
> + struct buffer_head *bh)
> {
> int err = 0;
>
> if (ext4_handle_valid(handle)) {
> err = jbd2_journal_dirty_metadata(handle, bh);
> if (err)
> - ext4_journal_abort_handle(where, __func__, bh,
> - handle, err);
> + ext4_journal_abort_handle(where, line,__func__,
> + bh, handle, err);
> } else {
> if (inode)
> mark_buffer_dirty_inode(bh, inode);
> @@ -144,8 +146,8 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
> return err;
> }
>
> -int __ext4_handle_dirty_super(const char *where, handle_t *handle,
> - struct super_block *sb)
> +int __ext4_handle_dirty_super(const char *where, unsigned int line,
> + handle_t *handle, struct super_block *sb)
> {
> struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
> int err = 0;
> @@ -153,8 +155,8 @@ int __ext4_handle_dirty_super(const char *where, handle_t *handle,
> if (ext4_handle_valid(handle)) {
> err = jbd2_journal_dirty_metadata(handle, bh);
> if (err)
> - ext4_journal_abort_handle(where, __func__, bh,
> - handle, err);
> + ext4_journal_abort_handle(where, line, __func__,
> + bh, handle, err);
> } else
> sb->s_dirt = 1;
> return err;
> diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
> index 38d1e66..b0bd792 100644
> --- a/fs/ext4/ext4_jbd2.h
> +++ b/fs/ext4/ext4_jbd2.h
> @@ -122,44 +122,47 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode);
> /*
> * Wrapper functions with which ext4 calls into JBD.
> */
> -void ext4_journal_abort_handle(const char *caller, const char *err_fn,
> +void ext4_journal_abort_handle(const char *caller, unsigned int line,
> + const char *err_fn,
> struct buffer_head *bh, handle_t *handle, int err);
>
> -int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
> - struct buffer_head *bh);
> +int __ext4_journal_get_undo_access(const char *where, unsigned int line,
> + handle_t *handle, struct buffer_head *bh);
>
> -int __ext4_journal_get_write_access(const char *where, handle_t *handle,
> - struct buffer_head *bh);
> +int __ext4_journal_get_write_access(const char *where, unsigned int line,
> + handle_t *handle, struct buffer_head *bh);
>
> -int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
> - struct inode *inode, struct buffer_head *bh,
> - ext4_fsblk_t blocknr);
> +int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
> + int is_metadata, struct inode *inode,
> + struct buffer_head *bh, ext4_fsblk_t blocknr);
>
> -int __ext4_journal_get_create_access(const char *where,
> +int __ext4_journal_get_create_access(const char *where, unsigned int line,
> handle_t *handle, struct buffer_head *bh);
>
> -int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
> - struct inode *inode, struct buffer_head *bh);
> +int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
> + handle_t *handle, struct inode *inode,
> + struct buffer_head *bh);
>
> -int __ext4_handle_dirty_super(const char *where, handle_t *handle,
> - struct super_block *sb);
> +int __ext4_handle_dirty_super(const char *where, unsigned int line,
> + handle_t *handle, struct super_block *sb);
>
> #define ext4_journal_get_undo_access(handle, bh) \
> - __ext4_journal_get_undo_access(__func__, (handle), (bh))
> + __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh))
> #define ext4_journal_get_write_access(handle, bh) \
> - __ext4_journal_get_write_access(__func__, (handle), (bh))
> + __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
> #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
> - __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh),\
> - (block_nr))
> + __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \
> + (bh), (block_nr))
> #define ext4_journal_get_create_access(handle, bh) \
> - __ext4_journal_get_create_access(__func__, (handle), (bh))
> + __ext4_journal_get_create_access(__func__, __LINE__, (handle), (bh))
> #define ext4_handle_dirty_metadata(handle, inode, bh) \
> - __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh))
> + __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
> + (bh))
> #define ext4_handle_dirty_super(handle, sb) \
> - __ext4_handle_dirty_super(__func__, (handle), (sb))
> + __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
>
> handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks);
> -int __ext4_journal_stop(const char *where, handle_t *handle);
> +int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
>
> #define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)
>
> @@ -212,7 +215,7 @@ static inline handle_t *ext4_journal_start(struct inode *inode, int nblocks)
> }
>
> #define ext4_journal_stop(handle) \
> - __ext4_journal_stop(__func__, (handle))
> + __ext4_journal_stop(__func__, __LINE__, (handle))
>
> static inline handle_t *ext4_journal_current_handle(void)
> {
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 346de3d..fac5516 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -401,9 +401,9 @@ static int ext4_valid_extent_entries(struct inode *inode,
> return 1;
> }
>
> -static int __ext4_ext_check(const char *function, struct inode *inode,
> - struct ext4_extent_header *eh,
> - int depth)
> +static int __ext4_ext_check(const char *function, unsigned int line,
> + struct inode *inode, struct ext4_extent_header *eh,
> + int depth)
> {
> const char *error_msg;
> int max = 0;
> @@ -436,7 +436,7 @@ static int __ext4_ext_check(const char *function, struct inode *inode,
> return 0;
>
> corrupted:
> - ext4_error_inode(function, inode,
> + ext4_error_inode(function, line, inode,
> "bad header/extent: %s - magic %x, "
> "entries %u, max %u(%u), depth %u(%u)",
> error_msg, le16_to_cpu(eh->eh_magic),
> @@ -447,7 +447,7 @@ corrupted:
> }
>
> #define ext4_ext_check(inode, eh, depth) \
> - __ext4_ext_check(__func__, inode, eh, depth)
> + __ext4_ext_check(__func__, __LINE__, inode, eh, depth)
>
> int ext4_ext_check_inode(struct inode *inode)
> {
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index b485987..aade5e4 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -337,9 +337,11 @@ static int ext4_block_to_path(struct inode *inode,
> return n;
> }
>
> -static int __ext4_check_blockref(const char *function, struct inode *inode,
> +static int __ext4_check_blockref(const char *function, unsigned int line,
> + struct inode *inode,
> __le32 *p, unsigned int max)
> {
> + struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
> __le32 *bref = p;
> unsigned int blk;
>
> @@ -348,7 +350,8 @@ static int __ext4_check_blockref(const char *function, struct inode *inode,
> if (blk &&
> unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
> blk, 1))) {
> - ext4_error_inode(function, inode,
> + es->s_error_block = cpu_to_le64(blk);
> + ext4_error_inode(function, line, inode,
> "invalid block reference %u", blk);
> return -EIO;
> }
> @@ -358,11 +361,13 @@ static int __ext4_check_blockref(const char *function, struct inode *inode,
>
>
> #define ext4_check_indirect_blockref(inode, bh) \
> - __ext4_check_blockref(__func__, inode, (__le32 *)(bh)->b_data, \
> + __ext4_check_blockref(__func__, __LINE__, inode, \
> + (__le32 *)(bh)->b_data, \
> EXT4_ADDR_PER_BLOCK((inode)->i_sb))
>
> #define ext4_check_inode_blockref(inode) \
> - __ext4_check_blockref(__func__, inode, EXT4_I(inode)->i_data, \
> + __ext4_check_blockref(__func__, __LINE__, inode, \
> + EXT4_I(inode)->i_data, \
> EXT4_NDIR_BLOCKS)
>
> /**
> @@ -1129,11 +1134,12 @@ void ext4_da_update_reserve_space(struct inode *inode,
> }
>
> static int check_block_validity(struct inode *inode, const char *func,
> + unsigned int line,
> struct ext4_map_blocks *map)
> {
> if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk,
> map->m_len)) {
> - ext4_error_inode(func, inode,
> + ext4_error_inode(func, line, inode,
> "lblock %lu mapped to illegal pblock %llu "
> "(length %d)", (unsigned long) map->m_lblk,
> map->m_pblk, map->m_len);
> @@ -1244,7 +1250,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
> up_read((&EXT4_I(inode)->i_data_sem));
>
> if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
> - int ret = check_block_validity(inode, __func__, map);
> + int ret = check_block_validity(inode, __func__, __LINE__, map);
> if (ret != 0)
> return ret;
> }
> @@ -1324,8 +1330,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
>
> up_write((&EXT4_I(inode)->i_data_sem));
> if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
> - int ret = check_block_validity(inode,
> - "ext4_map_blocks_after_alloc",
> + int ret = check_block_validity(inode, __func__, __LINE__,
> map);
> if (ret != 0)
> return ret;
> diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
> index b2948b0..92ae5e8 100644
> --- a/fs/ext4/mballoc.c
> +++ b/fs/ext4/mballoc.c
> @@ -446,7 +446,7 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b,
> blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
> blocknr += first + i;
> ext4_grp_locked_error(sb, e4b->bd_group,
> - __func__, "double-free of inode"
> + "double-free of inode"
> " %lu's block %llu(bit %u in group %u)",
> inode ? inode->i_ino : 0, blocknr,
> first + i, e4b->bd_group);
> @@ -712,7 +712,7 @@ void ext4_mb_generate_buddy(struct super_block *sb,
> grp->bb_fragments = fragments;
>
> if (free != grp->bb_free) {
> - ext4_grp_locked_error(sb, group, __func__,
> + ext4_grp_locked_error(sb, group,
> "EXT4-fs: group %u: %u blocks in bitmap, %u in gd",
> group, free, grp->bb_free);
> /*
> @@ -1296,7 +1296,7 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
> blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
> blocknr += block;
> ext4_grp_locked_error(sb, e4b->bd_group,
> - __func__, "double-free of inode"
> + "double-free of inode"
> " %lu's block %llu(bit %u in group %u)",
> inode ? inode->i_ino : 0, blocknr, block,
> e4b->bd_group);
> @@ -1789,7 +1789,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
> * we have free blocks
> */
> ext4_grp_locked_error(sb, e4b->bd_group,
> - __func__, "%d free blocks as per "
> + "%d free blocks as per "
> "group info. But bitmap says 0",
> free);
> break;
> @@ -1799,7 +1799,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
> BUG_ON(ex.fe_len <= 0);
> if (free < ex.fe_len) {
> ext4_grp_locked_error(sb, e4b->bd_group,
> - __func__, "%d free blocks as per "
> + "%d free blocks as per "
> "group info. But got %d blocks",
> free, ex.fe_len);
> /*
> @@ -3584,8 +3584,7 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh,
> pa, (unsigned long) pa->pa_lstart,
> (unsigned long) pa->pa_pstart,
> (unsigned long) pa->pa_len);
> - ext4_grp_locked_error(sb, group,
> - __func__, "free %u, pa_free %u",
> + ext4_grp_locked_error(sb, group, "free %u, pa_free %u",
> free, pa->pa_free);
> /*
> * pa is already deleted so we use the value obtained
> @@ -4427,7 +4426,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
> else if (block >= (entry->start_blk + entry->count))
> n = &(*n)->rb_right;
> else {
> - ext4_grp_locked_error(sb, e4b->bd_group, __func__,
> + ext4_grp_locked_error(sb, e4b->bd_group,
> "Double free of blocks %d (%d %d)",
> block, entry->start_blk, entry->count);
> return 0;
> diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
> index 52abfa1..5f1ed9f 100644
> --- a/fs/ext4/move_extent.c
> +++ b/fs/ext4/move_extent.c
> @@ -148,17 +148,17 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
> */
> static int
> mext_check_null_inode(struct inode *inode1, struct inode *inode2,
> - const char *function)
> + const char *function, unsigned int line)
> {
> int ret = 0;
>
> if (inode1 == NULL) {
> - __ext4_error(inode2->i_sb, function,
> + __ext4_error(inode2->i_sb, function, line,
> "Both inodes should not be NULL: "
> "inode1 NULL inode2 %lu", inode2->i_ino);
> ret = -EIO;
> } else if (inode2 == NULL) {
> - __ext4_error(inode1->i_sb, function,
> + __ext4_error(inode1->i_sb, function, line,
> "Both inodes should not be NULL: "
> "inode1 %lu inode2 NULL", inode1->i_ino);
> ret = -EIO;
> @@ -1084,7 +1084,7 @@ mext_inode_double_lock(struct inode *inode1, struct inode *inode2)
>
> BUG_ON(inode1 == NULL && inode2 == NULL);
>
> - ret = mext_check_null_inode(inode1, inode2, __func__);
> + ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__);
> if (ret < 0)
> goto out;
>
> @@ -1121,7 +1121,7 @@ mext_inode_double_unlock(struct inode *inode1, struct inode *inode2)
>
> BUG_ON(inode1 == NULL && inode2 == NULL);
>
> - ret = mext_check_null_inode(inode1, inode2, __func__);
> + ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__);
> if (ret < 0)
> goto out;
>
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index 5a61f77..66b6e1c 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -605,7 +605,7 @@ static int htree_dirblock_to_tree(struct file *dir_file,
> dir->i_sb->s_blocksize -
> EXT4_DIR_REC_LEN(0));
> for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
> - if (!ext4_check_dir_entry("htree_dirblock_to_tree", dir, de, bh,
> + if (!ext4_check_dir_entry(__func__, __LINE__, dir, de, bh,
> (block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
> +((char *)de - bh->b_data))) {
> /* On error, skip the f_pos to the next block. */
> @@ -844,7 +844,7 @@ static inline int search_dirblock(struct buffer_head *bh,
> if ((char *) de + namelen <= dlimit &&
> ext4_match (namelen, name, de)) {
> /* found a match - just to be sure, do a full check */
> - if (!ext4_check_dir_entry("ext4_find_entry",
> + if (!ext4_check_dir_entry(__func__, __LINE__,
> dir, de, bh, offset))
> return -1;
> *res_dir = de;
> @@ -1019,7 +1019,8 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
> int off = (block << EXT4_BLOCK_SIZE_BITS(sb))
> + ((char *) de - bh->b_data);
>
> - if (!ext4_check_dir_entry(__func__, dir, de, bh, off)) {
> + if (!ext4_check_dir_entry(__func__, __LINE__, dir,
> + de, bh, off)) {
> brelse(bh);
> *err = ERR_BAD_DX_DIR;
> goto errout;
> @@ -1303,7 +1304,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
> de = (struct ext4_dir_entry_2 *)bh->b_data;
> top = bh->b_data + blocksize - reclen;
> while ((char *) de <= top) {
> - if (!ext4_check_dir_entry("ext4_add_entry", dir, de,
> + if (!ext4_check_dir_entry(__func__, __LINE__, dir, de,
> bh, offset))
> return -EIO;
> if (ext4_match(namelen, name, de))
> @@ -1671,7 +1672,7 @@ static int ext4_delete_entry(handle_t *handle,
> pde = NULL;
> de = (struct ext4_dir_entry_2 *) bh->b_data;
> while (i < bh->b_size) {
> - if (!ext4_check_dir_entry("ext4_delete_entry", dir, de, bh, i))
> + if (!ext4_check_dir_entry(__func__, __LINE__, dir, de, bh, i))
> return -EIO;
> if (de == de_del) {
> BUFFER_TRACE(bh, "get_write_access");
> @@ -1954,7 +1955,8 @@ static int empty_dir(struct inode *inode)
> }
> de = (struct ext4_dir_entry_2 *) bh->b_data;
> }
> - if (!ext4_check_dir_entry("empty_dir", inode, de, bh, offset)) {
> + if (!ext4_check_dir_entry(__func__, __LINE__, inode,
> + de, bh, offset)) {
> de = (struct ext4_dir_entry_2 *)(bh->b_data +
> sb->s_blocksize);
> offset = (offset | (sb->s_blocksize - 1)) + 1;
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 422a4ce..0c689fb 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -248,7 +248,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
> journal = EXT4_SB(sb)->s_journal;
> if (journal) {
> if (is_journal_aborted(journal)) {
> - ext4_abort(sb, __func__, "Detected aborted journal");
> + ext4_abort(sb, "Detected aborted journal");
> return ERR_PTR(-EROFS);
> }
> return jbd2_journal_start(journal, nblocks);
> @@ -262,7 +262,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
> * that sync() will call the filesystem's write_super callback if
> * appropriate.
> */
> -int __ext4_journal_stop(const char *where, handle_t *handle)
> +int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
> {
> struct super_block *sb;
> int err;
> @@ -279,12 +279,13 @@ int __ext4_journal_stop(const char *where, handle_t *handle)
> if (!err)
> err = rc;
> if (err)
> - __ext4_std_error(sb, where, err);
> + __ext4_std_error(sb, where, line, err);
> return err;
> }
>
> -void ext4_journal_abort_handle(const char *caller, const char *err_fn,
> - struct buffer_head *bh, handle_t *handle, int err)
> +void ext4_journal_abort_handle(const char *caller, unsigned int line,
> + const char *err_fn, struct buffer_head *bh,
> + handle_t *handle, int err)
> {
> char nbuf[16];
> const char *errstr = ext4_decode_error(NULL, err, nbuf);
> @@ -300,8 +301,8 @@ void ext4_journal_abort_handle(const char *caller, const char *err_fn,
> if (is_handle_aborted(handle))
> return;
>
> - printk(KERN_ERR "%s: aborting transaction: %s in %s\n",
> - caller, errstr, err_fn);
> + printk(KERN_ERR "%s:%d: aborting transaction: %s in %s\n",
> + caller, line, errstr, err_fn);
>
> jbd2_journal_abort_handle(handle);
> }
> @@ -327,6 +328,10 @@ static void ext4_handle_error(struct super_block *sb)
>
> EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
> es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
> + es->s_last_error_time = cpu_to_le32(get_seconds());
> + if (!es->s_first_error_time)
> + es->s_first_error_time = es->s_last_error_time;
> + es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1);
>
> if (sb->s_flags & MS_RDONLY)
> return;
> @@ -349,12 +354,16 @@ static void ext4_handle_error(struct super_block *sb)
> }
>
> void __ext4_error(struct super_block *sb, const char *function,
> - const char *fmt, ...)
> + unsigned int line, const char *fmt, ...)
> {
> va_list args;
> + struct ext4_super_block *es = EXT4_SB(sb)->s_es;
>
> + strncpy(es->s_error_func, function, sizeof(es->s_error_func));
> + es->s_error_line = cpu_to_le32(line);
> va_start(args, fmt);
> - printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
> + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
> + function, line);
> vprintk(fmt, args);
> printk("\n");
> va_end(args);
> @@ -362,14 +371,18 @@ void __ext4_error(struct super_block *sb, const char *function,
> ext4_handle_error(sb);
> }
>
> -void ext4_error_inode(const char *function, struct inode *inode,
> - const char *fmt, ...)
> +void ext4_error_inode(const char *function, unsigned int line,
> + struct inode *inode, const char *fmt, ...)
> {
> va_list args;
> + struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
>
> + strncpy(es->s_error_func, function, sizeof(es->s_error_func));
> + es->s_error_line = cpu_to_le32(line);
> va_start(args, fmt);
> - printk(KERN_CRIT "EXT4-fs error (device %s): %s: inode #%lu: (comm %s) ",
> - inode->i_sb->s_id, function, inode->i_ino, current->comm);
> + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: "
> + "(comm %s) ",
> + inode->i_sb->s_id, function, line, inode->i_ino, current->comm);
> vprintk(fmt, args);
> printk("\n");
> va_end(args);
> @@ -377,20 +390,26 @@ void ext4_error_inode(const char *function, struct inode *inode,
> ext4_handle_error(inode->i_sb);
> }
>
> -void ext4_error_file(const char *function, struct file *file,
> - const char *fmt, ...)
> +void ext4_error_file(const char *function, unsigned int line,
> + struct file *file, const char *fmt, ...)
> {
> va_list args;
> + struct ext4_super_block *es;
> struct inode *inode = file->f_dentry->d_inode;
> char pathname[80], *path;
>
> + es = EXT4_SB(inode->i_sb)->s_es;
> + strncpy(es->s_error_func, function, sizeof(es->s_error_func));
> + es->s_error_line = cpu_to_le32(line);
> va_start(args, fmt);
> path = d_path(&(file->f_path), pathname, sizeof(pathname));
> if (!path)
> path = "(unknown)";
> printk(KERN_CRIT
> - "EXT4-fs error (device %s): %s: inode #%lu (comm %s path %s): ",
> - inode->i_sb->s_id, function, inode->i_ino, current->comm, path);
> + "EXT4-fs error (device %s): %s:%d: inode #%lu "
> + "(comm %s path %s): ",
> + inode->i_sb->s_id, function, line, inode->i_ino,
> + current->comm, path);
> vprintk(fmt, args);
> printk("\n");
> va_end(args);
> @@ -435,10 +454,12 @@ static const char *ext4_decode_error(struct super_block *sb, int errno,
> /* __ext4_std_error decodes expected errors from journaling functions
> * automatically and invokes the appropriate error response. */
>
> -void __ext4_std_error(struct super_block *sb, const char *function, int errno)
> +void __ext4_std_error(struct super_block *sb, const char *function,
> + unsigned int line, int errno)
> {
> char nbuf[16];
> const char *errstr;
> + struct ext4_super_block *es = EXT4_SB(sb)->s_es;
>
> /* Special case: if the error is EROFS, and we're not already
> * inside a transaction, then there's really no point in logging
> @@ -448,8 +469,10 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno)
> return;
>
> errstr = ext4_decode_error(sb, errno, nbuf);
> - printk(KERN_CRIT "EXT4-fs error (device %s) in %s: %s\n",
> - sb->s_id, function, errstr);
> + printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
> + sb->s_id, function, line, errstr);
> + strncpy(es->s_error_func, function, sizeof(es->s_error_func));
> + es->s_error_line = cpu_to_le32(line);
>
> ext4_handle_error(sb);
> }
> @@ -464,29 +487,32 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno)
> * case we take the easy way out and panic immediately.
> */
>
> -void ext4_abort(struct super_block *sb, const char *function,
> - const char *fmt, ...)
> +void __ext4_abort(struct super_block *sb, const char *function,
> + unsigned int line, const char *fmt, ...)
> {
> va_list args;
> + struct ext4_super_block *es = EXT4_SB(sb)->s_es;
>
> va_start(args, fmt);
> - printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
> + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
> + function, line);
> vprintk(fmt, args);
> printk("\n");
> va_end(args);
>
> + if ((sb->s_flags & MS_RDONLY) == 0) {
> + ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
> + EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
> + sb->s_flags |= MS_RDONLY;
> + EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
> + if (EXT4_SB(sb)->s_journal)
> + jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
> + strncpy(es->s_error_func, function, sizeof(es->s_error_func));
> + es->s_error_line = cpu_to_le32(line);
> + ext4_commit_super(sb, 1);
> + }
> if (test_opt(sb, ERRORS_PANIC))
> panic("EXT4-fs panic from previous error\n");
> -
> - if (sb->s_flags & MS_RDONLY)
> - return;
> -
> - ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
> - EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
> - sb->s_flags |= MS_RDONLY;
> - EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
> - if (EXT4_SB(sb)->s_journal)
> - jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
> }
>
> void ext4_msg (struct super_block * sb, const char *prefix,
> @@ -502,20 +528,21 @@ void ext4_msg (struct super_block * sb, const char *prefix,
> }
>
> void __ext4_warning(struct super_block *sb, const char *function,
> - const char *fmt, ...)
> + unsigned int line, const char *fmt, ...)
> {
> va_list args;
>
> va_start(args, fmt);
> - printk(KERN_WARNING "EXT4-fs warning (device %s): %s: ",
> - sb->s_id, function);
> + printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: ",
> + sb->s_id, function, line);
> vprintk(fmt, args);
> printk("\n");
> va_end(args);
> }
>
> -void ext4_grp_locked_error(struct super_block *sb, ext4_group_t grp,
> - const char *function, const char *fmt, ...)
> +void __ext4_grp_locked_error(const char *function, unsigned int line,
> + struct super_block *sb, ext4_group_t grp,
> + const char *fmt, ...)
> __releases(bitlock)
> __acquires(bitlock)
> {
> @@ -523,14 +550,21 @@ __acquires(bitlock)
> struct ext4_super_block *es = EXT4_SB(sb)->s_es;
>
> va_start(args, fmt);
> - printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
> + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
> + function, line);
> vprintk(fmt, args);
> printk("\n");
> va_end(args);
> + strncpy(es->s_error_func, function, sizeof(es->s_error_func));
> + es->s_error_line = cpu_to_le32(line);
>
> if (test_opt(sb, ERRORS_CONT)) {
> EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
> es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
> + es->s_last_error_time = cpu_to_le32(get_seconds());
> + if (!es->s_first_error_time)
> + es->s_first_error_time = es->s_last_error_time;
> + es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1);
> ext4_commit_super(sb, 0);
> return;
> }
> @@ -660,8 +694,7 @@ static void ext4_put_super(struct super_block *sb)
> err = jbd2_journal_destroy(sbi->s_journal);
> sbi->s_journal = NULL;
> if (err < 0)
> - ext4_abort(sb, __func__,
> - "Couldn't clean up the journal");
> + ext4_abort(sb, "Couldn't clean up the journal");
> }
>
> ext4_release_system_zone(sb);
> @@ -3605,7 +3638,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
> }
>
> if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
> - ext4_abort(sb, __func__, "Abort forced by user");
> + ext4_abort(sb, "Abort forced by user");
>
> sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
> (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);

2010-06-24 12:09:19

by Amir G.

[permalink] [raw]
Subject: Re: [PATCH, RFC] ext4: Store basic fs error information in the superblock

Hi Ted,

I saw your patch to store fs error information in the superblock.
I think it is a very useful feature and I have implemented something similar in
next3_snapshot_journal_error.patch and e2fs_next3_message_buffer.patch
(attached).

There is one big problem I encountered with this feature:
If the file system error behavior is set to "abort" or "remount-ro",
the journal recovery on the next mount will most likely write over the
superblock with the errors information.

To solve this problem I stored the errors message buffer in the
journal superblock
and copied the message buffer to the filesystem superblock on journal
recovery (both on mount and fsck).
fsck also displays the errors buffer and clears it.

This feature helped me hunt down some rare bugs that happened on beta
sites, which I had to analyse post-mortem.
fsck simply gives me the first few error messages after the last time
fsck was run.

Amir.


On Wed, May 5, 2010 at 9:28 PM, Amir Goldstein <[email protected]> wrote:
> Next3 error messages are recorded in a 2K message buffer after the
> journal super block. ?On journal recovery, the journal message buffer
> is copied to the file system message buffer. ?On fsck, if the message
> buffer is not empty, the recorded messages are printed to stdout and
> the buffer is cleared.
> Next3 supports only block size of 4K, so there is always 2K of free
> space for the message buffer after the 1K super block.
>
> Signed-off-by: Amir Goldstein <[email protected]>
> ---
> ?e2fsck/journal.c | ? 14 ++++++++++++++
> ?e2fsck/super.c ? | ? 42 ++++++++++++++++++++++++++++++++++++++++++
> ?2 files changed, 56 insertions(+), 0 deletions(-)
>
> diff --git a/e2fsck/journal.c b/e2fsck/journal.c
> index 57783eb..72d2ea0 100644
> --- a/e2fsck/journal.c
> +++ b/e2fsck/journal.c
> @@ -836,6 +836,20 @@ static errcode_t recover_ext3_journal(e2fsck_t ctx)
>
>
> ? ? ? ?if (journal->j_superblock->s_errno) {
> + ? ? ? ? ? ? ? /* journal message buffer at journal super block + 1K */
> + ? ? ? ? ? ? ? char *buf = ((char *) journal->j_superblock) +
> + ? ? ? ? ? ? ? ? ? ? ? SUPERBLOCK_OFFSET;
> + ? ? ? ? ? ? ? int len = ctx->fs->blocksize - 2*SUPERBLOCK_OFFSET;
> +
> + ? ? ? ? ? ? ? if (len >= 2*SUPERBLOCK_OFFSET && *buf) {
> + ? ? ? ? ? ? ? ? ? ? ? /* write journal message buffer to super block + 2K */
> + ? ? ? ? ? ? ? ? ? ? ? io_channel_set_blksize(ctx->fs->io, SUPERBLOCK_OFFSET);
> + ? ? ? ? ? ? ? ? ? ? ? retval = io_channel_write_blk(ctx->fs->io, 2, 2, buf);
> + ? ? ? ? ? ? ? ? ? ? ? io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
> + ? ? ? ? ? ? ? ? ? ? ? /* clear journal message buffer */
> + ? ? ? ? ? ? ? ? ? ? ? memset(buf, 0, len);
> + ? ? ? ? ? ? ? }
> +
> ? ? ? ? ? ? ? ?ctx->fs->super->s_state |= EXT2_ERROR_FS;
> ? ? ? ? ? ? ? ?ext2fs_mark_super_dirty(ctx->fs);
> ? ? ? ? ? ? ? ?journal->j_superblock->s_errno = 0;
> diff --git a/e2fsck/super.c b/e2fsck/super.c
> index f66ce9d..4a830bc 100644
> --- a/e2fsck/super.c
> +++ b/e2fsck/super.c
> @@ -584,6 +584,43 @@ static void e2fsck_fix_dirhash_hint(e2fsck_t ctx)
> ? ? ? ?}
> ?}
>
> +/*
> + * This function prints the message buffer at the end of super block.
> + */
> +static void e2fsck_print_message_buffer(e2fsck_t ctx)
> +{
> + ? ? ? char *buf;
> + ? ? ? int len = ctx->fs->blocksize - 2*SUPERBLOCK_OFFSET;
> + ? ? ? unsigned offset = 0;
> + ? ? ? int retval;
> +#define MSGLEN 256
> +
> + ? ? ? if (len < 2*SUPERBLOCK_OFFSET)
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? buf = (char *) e2fsck_allocate_memory(ctx, len, "message buffer");
> +
> + ? ? ? io_channel_set_blksize(ctx->fs->io, SUPERBLOCK_OFFSET);
> + ? ? ? /* read message buffer from super block + 2K */
> + ? ? ? retval = io_channel_read_blk(ctx->fs->io, 2, 2, buf);
> + ? ? ? if (retval || !*buf)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? /* print messages in buffer */
> + ? ? ? puts("Error messages recorded in message buffer:");
> + ? ? ? while (offset < len && buf[offset]) {
> + ? ? ? ? ? ? ? printf(buf+offset);
> + ? ? ? ? ? ? ? offset += MSGLEN;
> + ? ? ? }
> + ? ? ? /* clear message buffer */
> + ? ? ? memset(buf, 0, len);
> + ? ? ? retval = io_channel_write_blk(ctx->fs->io, 2, 2, buf);
> + ? ? ? puts("End of message buffer.");
> +out:
> + ? ? ? io_channel_set_blksize(ctx->fs->io, ctx->fs->blocksize);
> + ? ? ? ext2fs_free_mem(&buf);
> +}
> +
>
> ?void check_super_block(e2fsck_t ctx)
> ?{
> @@ -998,6 +1035,11 @@ void check_super_block(e2fsck_t ctx)
> ? ? ? ? */
> ? ? ? ?e2fsck_fix_dirhash_hint(ctx);
>
> + ? ? ? /*
> + ? ? ? ?* Print message buffer if necessary
> + ? ? ? ?*/
> + ? ? ? e2fsck_print_message_buffer(ctx);
> +
> ? ? ? ?return;
> ?}
>
> --
> 1.6.6
>
>

2010-06-24 13:17:15

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH, RFC] ext4: Store basic fs error information in the superblock

On Thu, Jun 24, 2010 at 02:52:01PM +0400, Dmitry Monakhov wrote:
> Excellent choice. s_error_XXX fields finally allow one to detect errors
> in determinant meaner. And most stress test finally will do it after
> umount :).

This was something you could do before simply by testing for the
ERRORS_FS bit. (You'll see it if you do a dumpe2fs -h on the file
system.)

> Minor question: What snapshot staff is doing in that patch?

Those were fields that were reserved for Amir's Next3 snapshot effort.
I probably should split this patch up, but this was a first pass
effort, and I did it all in one go...

- Ted

2010-06-24 13:27:47

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH, RFC] ext4: Store basic fs error information in the superblock

On Thu, Jun 24, 2010 at 03:09:16PM +0300, Amir G. wrote:
> Hi Ted,
>
> I saw your patch to store fs error information in the superblock.
> I think it is a very useful feature and I have implemented something similar in
> next3_snapshot_journal_error.patch and e2fs_next3_message_buffer.patch
> (attached).
>
> There is one big problem I encountered with this feature:
> If the file system error behavior is set to "abort" or "remount-ro",
> the journal recovery on the next mount will most likely write over the
> superblock with the errors information.

True, thanks for pointing that out; the simplest way to solve this for
my purposes is to snapshot those superblock fields and restore them
after replaying the journal.

> To solve this problem I stored the errors message buffer in the
> journal superblock
> and copied the message buffer to the filesystem superblock on journal
> recovery (both on mount and fsck).
> fsck also displays the errors buffer and clears it.

That's an interesting approach, although as you point out it only
works on file systems with a 4k block size. Your design seems to be
focused on recording only the most recent logs, which makes sense in a
debugging environment. My assumption was that the most recent
problems would probably be recorded in /var/log/messages, although if
the problem occurred on a single-disk system, that assumption probably
wouldn't hold true. I wonder if the a better solution for this
particular use case is much larger ring buffer, and a hook into the
printk system which is guaranteed to record *everything*, even after a
panic or after the journal has been aborted and the file system has
been remounted read-only.

For the patch I wrote, my intention was as a supplement to
/var/log/messages --- where s_first_error_time might be from long
after /var/log/messages had rolled over. So I was trying to solve a
somewhat different problem. (Hmm, actually, it would probably be good
to save both details about the first as well as the most recent error.)

- Ted

2010-06-24 14:44:47

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH, RFC] ext4: Store basic fs error information in the superblock

Dmitry Monakhov <[email protected]> writes:
>> bother to send them along.
> Excellent choice. s_error_XXX fields finally allow one to detect errors
> in determinant meaner. And most stress test finally will do it after
> umount :).

... assuming you can still write the super block after the error.
-Andi


--
[email protected] -- Speaking for myself only.

2010-06-26 01:16:38

by Amir G.

[permalink] [raw]
Subject: Re: [PATCH, RFC] ext4: Store basic fs error information in the superblock

On Thu, Jun 24, 2010 at 4:27 PM, <[email protected]> wrote:
> True, thanks for pointing that out; the simplest way to solve this for
> my purposes is to snapshot those superblock fields and restore them
> after replaying the journal.
>

I guess that should work.
I wonder why the ERROR_FS flag is not snapshotted on mount
and the file system relies on the journal abort flag to re-set the ERROR_FS.

> I wonder if the a better solution for this
> particular use case is much larger ring buffer, and a hook into the
> printk system which is guaranteed to record *everything*, even after a
> panic or after the journal has been aborted and the file system has
> been remounted read-only.
>

sounds like a good feature which would be hard to implement...
BTW, I think that if the file system error behavior is set to "remount-ro"
a file system with ERROR_FS, should be remounted read-only on mount time.
this is the only way to prevent a file system from getting over corrupted
and I don't see why there is no way to enforce this with existing
error behavior options.
We've implemented this logic at application level in our appliances.

> For the patch I wrote, my intention was as a supplement to
> /var/log/messages --- where s_first_error_time might be from long
> after /var/log/messages had rolled over. ?So I was trying to solve a
> somewhat different problem. ?(Hmm, actually, it would probably be good
> to save both details about the first as well as the most recent error.)
>

One thing that is missing from the error info is its severity level.
If I would have to save just one error info, it would be the first
error after fsck
(i.e. transition from healthy to sick file system), but I would
override it if a message
of higher severity occurs.

Amir.