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,
        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 - "
 
        /* 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)
                 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
  *
 
                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);
        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);
 
        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);
 
                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");
                                              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");
 
        struct ext4_dir_entry *dirent;
        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)) {
                __ext4_warning(inode->i_sb, func, line,
                               "inode #%lu: lblock %lu: comm %s: "
         * 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);
                }
        }
        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);
 
        attr_last_error_time,
        attr_feature,
        attr_pointer_ui,
+       attr_pointer_ul,
        attr_pointer_atomic,
        attr_journal_task,
 } attr_id_t;
 #define EXT4_RW_ATTR_SBI_UI(_name,_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) \
 static struct ext4_attr ext4_attr_##_name = {                  \
        .attr = {.name = __stringify(_name), .mode = _mode },   \
 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_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_ATTR(first_error_time, 0444, first_error_time);
 EXT4_ATTR(last_error_time, 0444, last_error_time);
        ATTR_LIST(first_error_time),
        ATTR_LIST(last_error_time),
        ATTR_LIST(journal_task),
+#ifdef CONFIG_EXT4_DEBUG
+       ATTR_LIST(simulate_fail),
+#endif
        NULL,
 };
 ATTRIBUTE_GROUPS(ext4);
                else
                        return snprintf(buf, PAGE_SIZE, "%u\n",
                                        *((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:
                if (!ptr)
                        return 0;
                else
                        *((unsigned int *) ptr) = t;
                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:
                return inode_readahead_blks_store(sbi, buf, len);
        case attr_trigger_test_error: