mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
					
				
					 6 changed files with 79 additions and 6 deletions
				
			
		| 
						 | 
					@ -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 @@ static int __ext4_get_inode_loc(struct inode *inode,
 | 
				
			||||||
		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 a new issue