ext4: save the error code which triggered an ext4_error() in the superblock
This allows the cause of an ext4_error() report to be categorized based on whether it was triggered due to an I/O error, or an memory allocation error, or other possible causes. Most errors are caused by a detected file system inconsistency, so the default code stored in the superblock will be EXT4_ERR_EFSCORRUPTED. Link: https://lore.kernel.org/r/20191204032335.7683-1-tytso@mit.edu Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
a562c687d1
commit
878520ac45
@ -506,6 +506,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
|
||||
return -EFSCORRUPTED;
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ext4_set_errno(sb, EIO);
|
||||
ext4_error(sb, "Cannot read block bitmap - "
|
||||
"block_group = %u, block_bitmap = %llu",
|
||||
block_group, (unsigned long long) bh->b_blocknr);
|
||||
|
@ -1343,7 +1343,8 @@ struct ext4_super_block {
|
||||
__u8 s_lastcheck_hi;
|
||||
__u8 s_first_error_time_hi;
|
||||
__u8 s_last_error_time_hi;
|
||||
__u8 s_pad[2];
|
||||
__u8 s_first_error_errcode;
|
||||
__u8 s_last_error_errcode;
|
||||
__le16 s_encoding; /* Filename charset encoding */
|
||||
__le16 s_encoding_flags; /* Filename charset encoding flags */
|
||||
__le32 s_reserved[95]; /* Padding to the end of the block */
|
||||
@ -1574,6 +1575,32 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
|
||||
ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
|
||||
}
|
||||
|
||||
/*
|
||||
* Error number codes for s_{first,last}_error_errno
|
||||
*
|
||||
* Linux errno numbers are architecture specific, so we need to translate
|
||||
* them into something which is architecture independent. We don't define
|
||||
* codes for all errno's; just the ones which are most likely to be the cause
|
||||
* of an ext4_error() call.
|
||||
*/
|
||||
#define EXT4_ERR_UNKNOWN 1
|
||||
#define EXT4_ERR_EIO 2
|
||||
#define EXT4_ERR_ENOMEM 3
|
||||
#define EXT4_ERR_EFSBADCRC 4
|
||||
#define EXT4_ERR_EFSCORRUPTED 5
|
||||
#define EXT4_ERR_ENOSPC 6
|
||||
#define EXT4_ERR_ENOKEY 7
|
||||
#define EXT4_ERR_EROFS 8
|
||||
#define EXT4_ERR_EFBIG 9
|
||||
#define EXT4_ERR_EEXIST 10
|
||||
#define EXT4_ERR_ERANGE 11
|
||||
#define EXT4_ERR_EOVERFLOW 12
|
||||
#define EXT4_ERR_EBUSY 13
|
||||
#define EXT4_ERR_ENOTDIR 14
|
||||
#define EXT4_ERR_ENOTEMPTY 15
|
||||
#define EXT4_ERR_ESHUTDOWN 16
|
||||
#define EXT4_ERR_EFAULT 17
|
||||
|
||||
/*
|
||||
* Inode dynamic state flags
|
||||
*/
|
||||
@ -2688,6 +2715,7 @@ extern const char *ext4_decode_error(struct super_block *sb, int errno,
|
||||
extern void ext4_mark_group_bitmap_corrupted(struct super_block *sb,
|
||||
ext4_group_t block_group,
|
||||
unsigned int flags);
|
||||
extern void ext4_set_errno(struct super_block *sb, int err);
|
||||
|
||||
extern __printf(4, 5)
|
||||
void __ext4_error(struct super_block *, const char *, unsigned int,
|
||||
|
@ -58,6 +58,7 @@ static int ext4_journal_check_start(struct super_block *sb)
|
||||
* take the FS itself readonly cleanly.
|
||||
*/
|
||||
if (journal && is_journal_aborted(journal)) {
|
||||
ext4_set_errno(sb, -journal->j_errno);
|
||||
ext4_abort(sb, "Detected aborted journal");
|
||||
return -EROFS;
|
||||
}
|
||||
@ -249,6 +250,7 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
|
||||
if (err) {
|
||||
ext4_journal_abort_handle(where, line, __func__,
|
||||
bh, handle, err);
|
||||
ext4_set_errno(inode->i_sb, -err);
|
||||
__ext4_abort(inode->i_sb, where, line,
|
||||
"error %d when attempting revoke", err);
|
||||
}
|
||||
@ -320,6 +322,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
|
||||
es = EXT4_SB(inode->i_sb)->s_es;
|
||||
es->s_last_error_block =
|
||||
cpu_to_le64(bh->b_blocknr);
|
||||
ext4_set_errno(inode->i_sb, EIO);
|
||||
ext4_error_inode(inode, where, line,
|
||||
bh->b_blocknr,
|
||||
"IO error syncing itable block");
|
||||
|
@ -492,6 +492,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
|
||||
return 0;
|
||||
|
||||
corrupted:
|
||||
ext4_set_errno(inode->i_sb, -err);
|
||||
ext4_error_inode(inode, function, line, 0,
|
||||
"pblk %llu bad header/extent: %s - magic %x, "
|
||||
"entries %u, max %u(%u), depth %u(%u)",
|
||||
|
@ -194,6 +194,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
put_bh(bh);
|
||||
ext4_set_errno(sb, EIO);
|
||||
ext4_error(sb, "Cannot read inode bitmap - "
|
||||
"block_group = %u, inode_bitmap = %llu",
|
||||
block_group, bitmap_blk);
|
||||
@ -1223,6 +1224,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
|
||||
inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
ext4_set_errno(sb, -err);
|
||||
ext4_error(sb, "couldn't read orphan inode %lu (err %d)",
|
||||
ino, err);
|
||||
return inode;
|
||||
|
@ -98,6 +98,7 @@ int ext4_get_max_inline_size(struct inode *inode)
|
||||
|
||||
error = ext4_get_inode_loc(inode, &iloc);
|
||||
if (error) {
|
||||
ext4_set_errno(inode->i_sb, -error);
|
||||
ext4_error_inode(inode, __func__, __LINE__, 0,
|
||||
"can't get inode location %lu",
|
||||
inode->i_ino);
|
||||
@ -1761,6 +1762,7 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
|
||||
|
||||
err = ext4_get_inode_loc(dir, &iloc);
|
||||
if (err) {
|
||||
ext4_set_errno(dir->i_sb, -err);
|
||||
EXT4_ERROR_INODE(dir, "error %d getting inode %lu block",
|
||||
err, dir->i_ino);
|
||||
return true;
|
||||
|
@ -271,6 +271,7 @@ void ext4_evict_inode(struct inode *inode)
|
||||
if (inode->i_blocks) {
|
||||
err = ext4_truncate(inode);
|
||||
if (err) {
|
||||
ext4_set_errno(inode->i_sb, -err);
|
||||
ext4_error(inode->i_sb,
|
||||
"couldn't truncate inode %lu (err %d)",
|
||||
inode->i_ino, err);
|
||||
@ -2478,10 +2479,12 @@ update_disksize:
|
||||
EXT4_I(inode)->i_disksize = disksize;
|
||||
up_write(&EXT4_I(inode)->i_data_sem);
|
||||
err2 = ext4_mark_inode_dirty(handle, inode);
|
||||
if (err2)
|
||||
if (err2) {
|
||||
ext4_set_errno(inode->i_sb, -err2);
|
||||
ext4_error(inode->i_sb,
|
||||
"Failed to mark inode %lu dirty",
|
||||
inode->i_ino);
|
||||
}
|
||||
if (!err)
|
||||
err = err2;
|
||||
}
|
||||
@ -4338,6 +4341,7 @@ make_io:
|
||||
blk_finish_plug(&plug);
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ext4_set_errno(inode->i_sb, EIO);
|
||||
EXT4_ERROR_INODE_BLOCK(inode, block,
|
||||
"unable to read itable block");
|
||||
brelse(bh);
|
||||
@ -4552,6 +4556,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
|
||||
}
|
||||
|
||||
if (!ext4_inode_csum_verify(inode, raw_inode, ei)) {
|
||||
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
||||
ext4_error_inode(inode, function, line, 0,
|
||||
"iget: checksum invalid");
|
||||
ret = -EFSBADCRC;
|
||||
@ -5090,6 +5095,7 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync)
|
||||
sync_dirty_buffer(iloc.bh);
|
||||
if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
|
||||
ext4_set_errno(inode->i_sb, EIO);
|
||||
EXT4_ERROR_INODE_BLOCK(inode, iloc.bh->b_blocknr,
|
||||
"IO error syncing inode");
|
||||
err = -EIO;
|
||||
|
@ -3895,6 +3895,7 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
|
||||
bitmap_bh = ext4_read_block_bitmap(sb, group);
|
||||
if (IS_ERR(bitmap_bh)) {
|
||||
err = PTR_ERR(bitmap_bh);
|
||||
ext4_set_errno(sb, -err);
|
||||
ext4_error(sb, "Error %d reading block bitmap for %u",
|
||||
err, group);
|
||||
return 0;
|
||||
@ -4063,6 +4064,7 @@ repeat:
|
||||
err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
|
||||
GFP_NOFS|__GFP_NOFAIL);
|
||||
if (err) {
|
||||
ext4_set_errno(sb, -err);
|
||||
ext4_error(sb, "Error %d loading buddy information for %u",
|
||||
err, group);
|
||||
continue;
|
||||
@ -4071,6 +4073,7 @@ repeat:
|
||||
bitmap_bh = ext4_read_block_bitmap(sb, group);
|
||||
if (IS_ERR(bitmap_bh)) {
|
||||
err = PTR_ERR(bitmap_bh);
|
||||
ext4_set_errno(sb, -err);
|
||||
ext4_error(sb, "Error %d reading block bitmap for %u",
|
||||
err, group);
|
||||
ext4_mb_unload_buddy(&e4b);
|
||||
@ -4325,6 +4328,7 @@ ext4_mb_discard_lg_preallocations(struct super_block *sb,
|
||||
err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
|
||||
GFP_NOFS|__GFP_NOFAIL);
|
||||
if (err) {
|
||||
ext4_set_errno(sb, -err);
|
||||
ext4_error(sb, "Error %d loading buddy information for %u",
|
||||
err, group);
|
||||
continue;
|
||||
|
@ -173,8 +173,10 @@ static int kmmpd(void *data)
|
||||
* (s_mmp_update_interval * 60) seconds.
|
||||
*/
|
||||
if (retval) {
|
||||
if ((failed_writes % 60) == 0)
|
||||
if ((failed_writes % 60) == 0) {
|
||||
ext4_set_errno(sb, -retval);
|
||||
ext4_error(sb, "Error writing to MMP block");
|
||||
}
|
||||
failed_writes++;
|
||||
}
|
||||
|
||||
@ -205,6 +207,7 @@ static int kmmpd(void *data)
|
||||
|
||||
retval = read_mmp_block(sb, &bh_check, mmp_block);
|
||||
if (retval) {
|
||||
ext4_set_errno(sb, -retval);
|
||||
ext4_error(sb, "error reading MMP data: %d",
|
||||
retval);
|
||||
goto exit_thread;
|
||||
@ -218,6 +221,7 @@ static int kmmpd(void *data)
|
||||
"Error while updating MMP info. "
|
||||
"The filesystem seems to have been"
|
||||
" multiply mounted.");
|
||||
ext4_set_errno(sb, EBUSY);
|
||||
ext4_error(sb, "abort");
|
||||
put_bh(bh_check);
|
||||
retval = -EBUSY;
|
||||
|
@ -156,6 +156,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
|
||||
if (ext4_dx_csum_verify(inode, dirent))
|
||||
set_buffer_verified(bh);
|
||||
else {
|
||||
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
||||
ext4_error_inode(inode, func, line, block,
|
||||
"Directory index failed checksum");
|
||||
brelse(bh);
|
||||
@ -166,6 +167,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
|
||||
if (ext4_dirblock_csum_verify(inode, bh))
|
||||
set_buffer_verified(bh);
|
||||
else {
|
||||
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
||||
ext4_error_inode(inode, func, line, block,
|
||||
"Directory block failed checksum");
|
||||
brelse(bh);
|
||||
@ -1527,6 +1529,7 @@ restart:
|
||||
goto next;
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh)) {
|
||||
ext4_set_errno(sb, EIO);
|
||||
EXT4_ERROR_INODE(dir, "reading directory lblock %lu",
|
||||
(unsigned long) block);
|
||||
brelse(bh);
|
||||
@ -1537,6 +1540,7 @@ restart:
|
||||
!is_dx_internal_node(dir, block,
|
||||
(struct ext4_dir_entry *)bh->b_data) &&
|
||||
!ext4_dirblock_csum_verify(dir, bh)) {
|
||||
ext4_set_errno(sb, EFSBADCRC);
|
||||
EXT4_ERROR_INODE(dir, "checksumming directory "
|
||||
"block %lu", (unsigned long)block);
|
||||
brelse(bh);
|
||||
|
@ -367,6 +367,8 @@ static void __save_error_info(struct super_block *sb, const char *func,
|
||||
ext4_update_tstamp(es, s_last_error_time);
|
||||
strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func));
|
||||
es->s_last_error_line = cpu_to_le32(line);
|
||||
if (es->s_last_error_errcode == 0)
|
||||
es->s_last_error_errcode = EXT4_ERR_EFSCORRUPTED;
|
||||
if (!es->s_first_error_time) {
|
||||
es->s_first_error_time = es->s_last_error_time;
|
||||
es->s_first_error_time_hi = es->s_last_error_time_hi;
|
||||
@ -375,6 +377,7 @@ static void __save_error_info(struct super_block *sb, const char *func,
|
||||
es->s_first_error_line = cpu_to_le32(line);
|
||||
es->s_first_error_ino = es->s_last_error_ino;
|
||||
es->s_first_error_block = es->s_last_error_block;
|
||||
es->s_first_error_errcode = es->s_last_error_errcode;
|
||||
}
|
||||
/*
|
||||
* Start the daily error reporting function if it hasn't been
|
||||
@ -631,6 +634,66 @@ const char *ext4_decode_error(struct super_block *sb, int errno,
|
||||
return errstr;
|
||||
}
|
||||
|
||||
void ext4_set_errno(struct super_block *sb, int err)
|
||||
{
|
||||
if (err < 0)
|
||||
err = -err;
|
||||
|
||||
switch (err) {
|
||||
case EIO:
|
||||
err = EXT4_ERR_EIO;
|
||||
break;
|
||||
case ENOMEM:
|
||||
err = EXT4_ERR_ENOMEM;
|
||||
break;
|
||||
case EFSBADCRC:
|
||||
err = EXT4_ERR_EFSBADCRC;
|
||||
break;
|
||||
case EFSCORRUPTED:
|
||||
err = EXT4_ERR_EFSCORRUPTED;
|
||||
break;
|
||||
case ENOSPC:
|
||||
err = EXT4_ERR_ENOSPC;
|
||||
break;
|
||||
case ENOKEY:
|
||||
err = EXT4_ERR_ENOKEY;
|
||||
break;
|
||||
case EROFS:
|
||||
err = EXT4_ERR_EROFS;
|
||||
break;
|
||||
case EFBIG:
|
||||
err = EXT4_ERR_EFBIG;
|
||||
break;
|
||||
case EEXIST:
|
||||
err = EXT4_ERR_EEXIST;
|
||||
break;
|
||||
case ERANGE:
|
||||
err = EXT4_ERR_ERANGE;
|
||||
break;
|
||||
case EOVERFLOW:
|
||||
err = EXT4_ERR_EOVERFLOW;
|
||||
break;
|
||||
case EBUSY:
|
||||
err = EXT4_ERR_EBUSY;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
err = EXT4_ERR_ENOTDIR;
|
||||
break;
|
||||
case ENOTEMPTY:
|
||||
err = EXT4_ERR_ENOTEMPTY;
|
||||
break;
|
||||
case ESHUTDOWN:
|
||||
err = EXT4_ERR_ESHUTDOWN;
|
||||
break;
|
||||
case EFAULT:
|
||||
err = EXT4_ERR_EFAULT;
|
||||
break;
|
||||
default:
|
||||
err = EXT4_ERR_UNKNOWN;
|
||||
}
|
||||
EXT4_SB(sb)->s_es->s_last_error_errcode = err;
|
||||
}
|
||||
|
||||
/* __ext4_std_error decodes expected errors from journaling functions
|
||||
* automatically and invokes the appropriate error response. */
|
||||
|
||||
@ -655,6 +718,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
|
||||
sb->s_id, function, line, errstr);
|
||||
}
|
||||
|
||||
ext4_set_errno(sb, -errno);
|
||||
save_error_info(sb, function, line);
|
||||
ext4_handle_error(sb);
|
||||
}
|
||||
@ -982,8 +1046,10 @@ static void ext4_put_super(struct super_block *sb)
|
||||
aborted = is_journal_aborted(sbi->s_journal);
|
||||
err = jbd2_journal_destroy(sbi->s_journal);
|
||||
sbi->s_journal = NULL;
|
||||
if ((err < 0) && !aborted)
|
||||
if ((err < 0) && !aborted) {
|
||||
ext4_set_errno(sb, -err);
|
||||
ext4_abort(sb, "Couldn't clean up the journal");
|
||||
}
|
||||
}
|
||||
|
||||
ext4_unregister_sysfs(sb);
|
||||
|
@ -2879,9 +2879,11 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
|
||||
bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO);
|
||||
if (IS_ERR(bh)) {
|
||||
error = PTR_ERR(bh);
|
||||
if (error == -EIO)
|
||||
if (error == -EIO) {
|
||||
ext4_set_errno(inode->i_sb, EIO);
|
||||
EXT4_ERROR_INODE(inode, "block %llu read error",
|
||||
EXT4_I(inode)->i_file_acl);
|
||||
}
|
||||
bh = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user