mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
ext4: simulate various I/O and checksum errors when reading metadata
This allows us to test various error handling code paths Link: https://lore.kernel.org/r/20191209012317.59398-1-tytso@mit.edu Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
878520ac45
commit
46f870d690
@ -371,7 +371,8 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
|
|||||||
if (buffer_verified(bh))
|
if (buffer_verified(bh))
|
||||||
goto verified;
|
goto verified;
|
||||||
if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
|
if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
|
||||||
desc, bh))) {
|
desc, bh) ||
|
||||||
|
ext4_simulate_fail(sb, EXT4_SIM_BBITMAP_CRC))) {
|
||||||
ext4_unlock_group(sb, block_group);
|
ext4_unlock_group(sb, block_group);
|
||||||
ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
|
ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
|
||||||
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
ext4_mark_group_bitmap_corrupted(sb, block_group,
|
||||||
@ -505,6 +506,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
|
|||||||
if (!desc)
|
if (!desc)
|
||||||
return -EFSCORRUPTED;
|
return -EFSCORRUPTED;
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
|
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_BBITMAP_EIO);
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
ext4_set_errno(sb, EIO);
|
ext4_set_errno(sb, EIO);
|
||||||
ext4_error(sb, "Cannot read block bitmap - "
|
ext4_error(sb, "Cannot read block bitmap - "
|
||||||
|
@ -1557,6 +1557,9 @@ struct ext4_sb_info {
|
|||||||
/* Barrier between changing inodes' journal flags and writepages ops. */
|
/* Barrier between changing inodes' journal flags and writepages ops. */
|
||||||
struct percpu_rw_semaphore s_journal_flag_rwsem;
|
struct percpu_rw_semaphore s_journal_flag_rwsem;
|
||||||
struct dax_device *s_daxdev;
|
struct dax_device *s_daxdev;
|
||||||
|
#ifdef CONFIG_EXT4_DEBUG
|
||||||
|
unsigned long s_simulate_fail;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
|
||||||
@ -1575,6 +1578,40 @@ 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));
|
ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simulate_fail codes
|
||||||
|
*/
|
||||||
|
#define EXT4_SIM_BBITMAP_EIO 1
|
||||||
|
#define EXT4_SIM_BBITMAP_CRC 2
|
||||||
|
#define EXT4_SIM_IBITMAP_EIO 3
|
||||||
|
#define EXT4_SIM_IBITMAP_CRC 4
|
||||||
|
#define EXT4_SIM_INODE_EIO 5
|
||||||
|
#define EXT4_SIM_INODE_CRC 6
|
||||||
|
#define EXT4_SIM_DIRBLOCK_EIO 7
|
||||||
|
#define EXT4_SIM_DIRBLOCK_CRC 8
|
||||||
|
|
||||||
|
static inline bool ext4_simulate_fail(struct super_block *sb,
|
||||||
|
unsigned long code)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_EXT4_DEBUG
|
||||||
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
||||||
|
|
||||||
|
if (unlikely(sbi->s_simulate_fail == code)) {
|
||||||
|
sbi->s_simulate_fail = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ext4_simulate_fail_bh(struct super_block *sb,
|
||||||
|
struct buffer_head *bh,
|
||||||
|
unsigned long code)
|
||||||
|
{
|
||||||
|
if (!IS_ERR(bh) && ext4_simulate_fail(sb, code))
|
||||||
|
clear_buffer_uptodate(bh);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error number codes for s_{first,last}_error_errno
|
* Error number codes for s_{first,last}_error_errno
|
||||||
*
|
*
|
||||||
|
@ -94,7 +94,8 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
|
|||||||
goto verified;
|
goto verified;
|
||||||
blk = ext4_inode_bitmap(sb, desc);
|
blk = ext4_inode_bitmap(sb, desc);
|
||||||
if (!ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh,
|
if (!ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh,
|
||||||
EXT4_INODES_PER_GROUP(sb) / 8)) {
|
EXT4_INODES_PER_GROUP(sb) / 8) ||
|
||||||
|
ext4_simulate_fail(sb, EXT4_SIM_IBITMAP_CRC)) {
|
||||||
ext4_unlock_group(sb, block_group);
|
ext4_unlock_group(sb, block_group);
|
||||||
ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
|
ext4_error(sb, "Corrupt inode bitmap - block_group = %u, "
|
||||||
"inode_bitmap = %llu", block_group, blk);
|
"inode_bitmap = %llu", block_group, blk);
|
||||||
@ -192,6 +193,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
|
|||||||
get_bh(bh);
|
get_bh(bh);
|
||||||
submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh);
|
submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh);
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
|
ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO);
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
put_bh(bh);
|
put_bh(bh);
|
||||||
ext4_set_errno(sb, EIO);
|
ext4_set_errno(sb, EIO);
|
||||||
|
@ -4243,6 +4243,8 @@ static int __ext4_get_inode_loc(struct inode *inode,
|
|||||||
bh = sb_getblk(sb, block);
|
bh = sb_getblk(sb, block);
|
||||||
if (unlikely(!bh))
|
if (unlikely(!bh))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
if (ext4_simulate_fail(sb, EXT4_SIM_INODE_EIO))
|
||||||
|
goto simulate_eio;
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
lock_buffer(bh);
|
lock_buffer(bh);
|
||||||
|
|
||||||
@ -4341,6 +4343,7 @@ make_io:
|
|||||||
blk_finish_plug(&plug);
|
blk_finish_plug(&plug);
|
||||||
wait_on_buffer(bh);
|
wait_on_buffer(bh);
|
||||||
if (!buffer_uptodate(bh)) {
|
if (!buffer_uptodate(bh)) {
|
||||||
|
simulate_eio:
|
||||||
ext4_set_errno(inode->i_sb, EIO);
|
ext4_set_errno(inode->i_sb, EIO);
|
||||||
EXT4_ERROR_INODE_BLOCK(inode, block,
|
EXT4_ERROR_INODE_BLOCK(inode, block,
|
||||||
"unable to read itable block");
|
"unable to read itable block");
|
||||||
@ -4555,7 +4558,8 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
|
|||||||
sizeof(gen));
|
sizeof(gen));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ext4_inode_csum_verify(inode, raw_inode, ei)) {
|
if (!ext4_inode_csum_verify(inode, raw_inode, ei) ||
|
||||||
|
ext4_simulate_fail(sb, EXT4_SIM_INODE_CRC)) {
|
||||||
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
||||||
ext4_error_inode(inode, function, line, 0,
|
ext4_error_inode(inode, function, line, 0,
|
||||||
"iget: checksum invalid");
|
"iget: checksum invalid");
|
||||||
|
@ -109,7 +109,10 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
|
|||||||
struct ext4_dir_entry *dirent;
|
struct ext4_dir_entry *dirent;
|
||||||
int is_dx_block = 0;
|
int is_dx_block = 0;
|
||||||
|
|
||||||
bh = ext4_bread(NULL, inode, block, 0);
|
if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO))
|
||||||
|
bh = ERR_PTR(-EIO);
|
||||||
|
else
|
||||||
|
bh = ext4_bread(NULL, inode, block, 0);
|
||||||
if (IS_ERR(bh)) {
|
if (IS_ERR(bh)) {
|
||||||
__ext4_warning(inode->i_sb, func, line,
|
__ext4_warning(inode->i_sb, func, line,
|
||||||
"inode #%lu: lblock %lu: comm %s: "
|
"inode #%lu: lblock %lu: comm %s: "
|
||||||
@ -153,7 +156,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
|
|||||||
* caller is sure it should be an index block.
|
* caller is sure it should be an index block.
|
||||||
*/
|
*/
|
||||||
if (is_dx_block && type == INDEX) {
|
if (is_dx_block && type == INDEX) {
|
||||||
if (ext4_dx_csum_verify(inode, dirent))
|
if (ext4_dx_csum_verify(inode, dirent) &&
|
||||||
|
!ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
|
||||||
set_buffer_verified(bh);
|
set_buffer_verified(bh);
|
||||||
else {
|
else {
|
||||||
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
||||||
@ -164,7 +168,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!is_dx_block) {
|
if (!is_dx_block) {
|
||||||
if (ext4_dirblock_csum_verify(inode, bh))
|
if (ext4_dirblock_csum_verify(inode, bh) &&
|
||||||
|
!ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
|
||||||
set_buffer_verified(bh);
|
set_buffer_verified(bh);
|
||||||
else {
|
else {
|
||||||
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
ext4_set_errno(inode->i_sb, EFSBADCRC);
|
||||||
|
@ -29,6 +29,7 @@ typedef enum {
|
|||||||
attr_last_error_time,
|
attr_last_error_time,
|
||||||
attr_feature,
|
attr_feature,
|
||||||
attr_pointer_ui,
|
attr_pointer_ui,
|
||||||
|
attr_pointer_ul,
|
||||||
attr_pointer_atomic,
|
attr_pointer_atomic,
|
||||||
attr_journal_task,
|
attr_journal_task,
|
||||||
} attr_id_t;
|
} attr_id_t;
|
||||||
@ -160,6 +161,9 @@ static struct ext4_attr ext4_attr_##_name = { \
|
|||||||
#define EXT4_RW_ATTR_SBI_UI(_name,_elname) \
|
#define EXT4_RW_ATTR_SBI_UI(_name,_elname) \
|
||||||
EXT4_ATTR_OFFSET(_name, 0644, pointer_ui, ext4_sb_info, _elname)
|
EXT4_ATTR_OFFSET(_name, 0644, pointer_ui, ext4_sb_info, _elname)
|
||||||
|
|
||||||
|
#define EXT4_RW_ATTR_SBI_UL(_name,_elname) \
|
||||||
|
EXT4_ATTR_OFFSET(_name, 0644, pointer_ul, ext4_sb_info, _elname)
|
||||||
|
|
||||||
#define EXT4_ATTR_PTR(_name,_mode,_id,_ptr) \
|
#define EXT4_ATTR_PTR(_name,_mode,_id,_ptr) \
|
||||||
static struct ext4_attr ext4_attr_##_name = { \
|
static struct ext4_attr ext4_attr_##_name = { \
|
||||||
.attr = {.name = __stringify(_name), .mode = _mode }, \
|
.attr = {.name = __stringify(_name), .mode = _mode }, \
|
||||||
@ -194,6 +198,9 @@ EXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.int
|
|||||||
EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
|
EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst);
|
||||||
EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
|
EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval);
|
||||||
EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
|
EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst);
|
||||||
|
#ifdef CONFIG_EXT4_DEBUG
|
||||||
|
EXT4_RW_ATTR_SBI_UL(simulate_fail, s_simulate_fail);
|
||||||
|
#endif
|
||||||
EXT4_RO_ATTR_ES_UI(errors_count, s_error_count);
|
EXT4_RO_ATTR_ES_UI(errors_count, s_error_count);
|
||||||
EXT4_ATTR(first_error_time, 0444, first_error_time);
|
EXT4_ATTR(first_error_time, 0444, first_error_time);
|
||||||
EXT4_ATTR(last_error_time, 0444, last_error_time);
|
EXT4_ATTR(last_error_time, 0444, last_error_time);
|
||||||
@ -228,6 +235,9 @@ static struct attribute *ext4_attrs[] = {
|
|||||||
ATTR_LIST(first_error_time),
|
ATTR_LIST(first_error_time),
|
||||||
ATTR_LIST(last_error_time),
|
ATTR_LIST(last_error_time),
|
||||||
ATTR_LIST(journal_task),
|
ATTR_LIST(journal_task),
|
||||||
|
#ifdef CONFIG_EXT4_DEBUG
|
||||||
|
ATTR_LIST(simulate_fail),
|
||||||
|
#endif
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
ATTRIBUTE_GROUPS(ext4);
|
ATTRIBUTE_GROUPS(ext4);
|
||||||
@ -318,6 +328,11 @@ static ssize_t ext4_attr_show(struct kobject *kobj,
|
|||||||
else
|
else
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
*((unsigned int *) ptr));
|
*((unsigned int *) ptr));
|
||||||
|
case attr_pointer_ul:
|
||||||
|
if (!ptr)
|
||||||
|
return 0;
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%lu\n",
|
||||||
|
*((unsigned long *) ptr));
|
||||||
case attr_pointer_atomic:
|
case attr_pointer_atomic:
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return 0;
|
return 0;
|
||||||
@ -361,6 +376,14 @@ static ssize_t ext4_attr_store(struct kobject *kobj,
|
|||||||
else
|
else
|
||||||
*((unsigned int *) ptr) = t;
|
*((unsigned int *) ptr) = t;
|
||||||
return len;
|
return len;
|
||||||
|
case attr_pointer_ul:
|
||||||
|
if (!ptr)
|
||||||
|
return 0;
|
||||||
|
ret = kstrtoul(skip_spaces(buf), 0, &t);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
*((unsigned long *) ptr) = t;
|
||||||
|
return len;
|
||||||
case attr_inode_readahead:
|
case attr_inode_readahead:
|
||||||
return inode_readahead_blks_store(sbi, buf, len);
|
return inode_readahead_blks_store(sbi, buf, len);
|
||||||
case attr_trigger_test_error:
|
case attr_trigger_test_error:
|
||||||
|
Loading…
Reference in New Issue
Block a user