Commit 46f870d6 authored by Theodore Ts'o's avatar Theodore Ts'o
Browse files

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: default avatarTheodore Ts'o <tytso@mit.edu>
parent 878520ac
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -371,7 +371,8 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
	if (buffer_verified(bh))
		goto verified;
	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_error(sb, "bg %u: bad block bitmap checksum", 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)
		return -EFSCORRUPTED;
	wait_on_buffer(bh);
	ext4_simulate_fail_bh(sb, bh, EXT4_SIM_BBITMAP_EIO);
	if (!buffer_uptodate(bh)) {
		ext4_set_errno(sb, EIO);
		ext4_error(sb, "Cannot read block bitmap - "
+37 −0
Original line number Diff line number Diff line
@@ -1557,6 +1557,9 @@ struct ext4_sb_info {
	/* Barrier between changing inodes' journal flags and writepages ops. */
	struct percpu_rw_semaphore s_journal_flag_rwsem;
	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)
@@ -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));
}

/*
 * 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
 *
+3 −1
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,
		goto verified;
	blk = ext4_inode_bitmap(sb, desc);
	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_error(sb, "Corrupt inode bitmap - block_group = %u, "
			   "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);
	submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh);
	wait_on_buffer(bh);
	ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO);
	if (!buffer_uptodate(bh)) {
		put_bh(bh);
		ext4_set_errno(sb, EIO);
+5 −1
Original line number Diff line number Diff line
@@ -4243,6 +4243,8 @@ static int __ext4_get_inode_loc(struct inode *inode,
	bh = sb_getblk(sb, block);
	if (unlikely(!bh))
		return -ENOMEM;
	if (ext4_simulate_fail(sb, EXT4_SIM_INODE_EIO))
		goto simulate_eio;
	if (!buffer_uptodate(bh)) {
		lock_buffer(bh);

@@ -4341,6 +4343,7 @@ static int __ext4_get_inode_loc(struct inode *inode,
		blk_finish_plug(&plug);
		wait_on_buffer(bh);
		if (!buffer_uptodate(bh)) {
		simulate_eio:
			ext4_set_errno(inode->i_sb, EIO);
			EXT4_ERROR_INODE_BLOCK(inode, block,
					       "unable to read itable block");
@@ -4555,7 +4558,8 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
					      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_error_inode(inode, function, line, 0,
				 "iget: checksum invalid");
+8 −3
Original line number Diff line number Diff line
@@ -109,6 +109,9 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
	struct ext4_dir_entry *dirent;
	int is_dx_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)) {
		__ext4_warning(inode->i_sb, func, line,
@@ -153,7 +156,8 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
	 * caller is sure it should be an index block.
	 */
	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);
		else {
			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 (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);
		else {
			ext4_set_errno(inode->i_sb, EFSBADCRC);
Loading