Test EROFS to inject faults such as ENOMEM, EIO, and so on.
          If unsure, say N.
 
+config EROFS_FS_IO_MAX_RETRIES
+       int "EROFS IO Maximum Retries"
+       depends on EROFS_FS
+       default "5"
+       help
+         Maximum retry count of IO Errors.
+
+         If unsure, leave the default value (5 retries, 6 IOs at most).
+
 config EROFS_FS_ZIP
        bool "EROFS Data Compresssion Support"
        depends on EROFS_FS
 
 }
 
 /* prio -- true is used for dir */
-struct page *erofs_get_meta_page(struct super_block *sb,
-       erofs_blk_t blkaddr, bool prio)
+struct page *__erofs_get_meta_page(struct super_block *sb,
+       erofs_blk_t blkaddr, bool prio, bool nofail)
 {
-       struct inode *bd_inode = sb->s_bdev->bd_inode;
-       struct address_space *mapping = bd_inode->i_mapping;
+       struct inode *const bd_inode = sb->s_bdev->bd_inode;
+       struct address_space *const mapping = bd_inode->i_mapping;
+       /* prefer retrying in the allocator to blindly looping below */
+       const gfp_t gfp = mapping_gfp_constraint(mapping, ~__GFP_FS) |
+               (nofail ? __GFP_NOFAIL : 0);
+       unsigned int io_retries = nofail ? EROFS_IO_MAX_RETRIES_NOFAIL : 0;
        struct page *page;
+       int err;
 
 repeat:
-       page = find_or_create_page(mapping, blkaddr,
-       /*
-        * Prefer looping in the allocator rather than here,
-        * at least that code knows what it's doing.
-        */
-               mapping_gfp_constraint(mapping, ~__GFP_FS) | __GFP_NOFAIL);
-
-       BUG_ON(!page || !PageLocked(page));
+       page = find_or_create_page(mapping, blkaddr, gfp);
+       if (unlikely(page == NULL)) {
+               DBG_BUGON(nofail);
+               return ERR_PTR(-ENOMEM);
+       }
+       DBG_BUGON(!PageLocked(page));
 
        if (!PageUptodate(page)) {
                struct bio *bio;
-               int err;
 
-               bio = erofs_grab_bio(sb, blkaddr, 1, read_endio, true);
+               bio = erofs_grab_bio(sb, blkaddr, 1, read_endio, nofail);
+               if (IS_ERR(bio)) {
+                       DBG_BUGON(nofail);
+                       err = PTR_ERR(bio);
+                       goto err_out;
+               }
 
                err = bio_add_page(bio, page, PAGE_SIZE, 0);
-               BUG_ON(err != PAGE_SIZE);
+               if (unlikely(err != PAGE_SIZE)) {
+                       err = -EFAULT;
+                       goto err_out;
+               }
 
                __submit_bio(bio, REQ_OP_READ,
                        REQ_META | (prio ? REQ_PRIO : 0));
 
                lock_page(page);
 
-               /* the page has been truncated by others? */
+               /* this page has been truncated by others */
                if (unlikely(page->mapping != mapping)) {
+unlock_repeat:
                        unlock_page(page);
                        put_page(page);
                        goto repeat;
 
                /* more likely a read error */
                if (unlikely(!PageUptodate(page))) {
-                       unlock_page(page);
-                       put_page(page);
-
-                       page = ERR_PTR(-EIO);
+                       if (io_retries) {
+                               --io_retries;
+                               goto unlock_repeat;
+                       }
+                       err = -EIO;
+                       goto err_out;
                }
        }
        return page;
+
+err_out:
+       unlock_page(page);
+       put_page(page);
+       return ERR_PTR(err);
 }
 
 static int erofs_map_blocks_flatmode(struct inode *inode,
 
        submit_bio(bio);
 }
 
-extern struct page *erofs_get_meta_page(struct super_block *sb,
-       erofs_blk_t blkaddr, bool prio);
+#ifndef CONFIG_EROFS_FS_IO_MAX_RETRIES
+#define EROFS_IO_MAX_RETRIES_NOFAIL    0
+#else
+#define EROFS_IO_MAX_RETRIES_NOFAIL    CONFIG_EROFS_FS_IO_MAX_RETRIES
+#endif
+
+extern struct page *__erofs_get_meta_page(struct super_block *sb,
+       erofs_blk_t blkaddr, bool prio, bool nofail);
+
+static inline struct page *erofs_get_meta_page(struct super_block *sb,
+       erofs_blk_t blkaddr, bool prio)
+{
+       return __erofs_get_meta_page(sb, blkaddr, prio, false);
+}
+
+static inline struct page *erofs_get_meta_page_nofail(struct super_block *sb,
+       erofs_blk_t blkaddr, bool prio)
+{
+       return __erofs_get_meta_page(sb, blkaddr, prio, true);
+}
+
 extern int erofs_map_blocks(struct inode *, struct erofs_map_blocks *, int);
 extern int erofs_map_blocks_iter(struct inode *, struct erofs_map_blocks *,
        struct page **, int);
 };
 
 
-static inline struct page *erofs_get_inline_page(struct inode *inode,
-       erofs_blk_t blkaddr)
+static inline struct page *
+erofs_get_inline_page_nofail(struct inode *inode,
+                            erofs_blk_t blkaddr)
 {
-       return erofs_get_meta_page(inode->i_sb,
+       return erofs_get_meta_page_nofail(inode->i_sb,
                blkaddr, S_ISDIR(inode->i_mode));
 }
 
 
        erofs_blk_t blkaddr = vle_extent_blkaddr(inode, lcn);
        struct z_erofs_vle_decompressed_index *di;
        unsigned long long ofs;
-       const unsigned int clusterbits = EROFS_SB(inode->i_sb)->clusterbits;
+       struct super_block *const sb = inode->i_sb;
+       const unsigned int clusterbits = EROFS_SB(sb)->clusterbits;
        const unsigned int clustersize = 1 << clusterbits;
 
        if (page->index != blkaddr) {
                unlock_page(page);
                put_page(page);
 
-               *page_iter = page = erofs_get_meta_page(inode->i_sb,
-                       blkaddr, false);
+               page = erofs_get_meta_page_nofail(sb, blkaddr, false);
+               *page_iter = page;
                *kaddr_iter = kmap_atomic(page);
        }
 
        struct page *mpage = *mpage_ret;
        void *kaddr;
        bool initial;
-       const unsigned int clusterbits = EROFS_SB(inode->i_sb)->clusterbits;
+       struct super_block *const sb = inode->i_sb;
+       const unsigned int clusterbits = EROFS_SB(sb)->clusterbits;
        const unsigned int clustersize = 1 << clusterbits;
        int err = 0;
 
                if (mpage != NULL)
                        put_page(mpage);
 
-               mpage = erofs_get_meta_page(inode->i_sb, e_blkaddr, false);
+               mpage = erofs_get_meta_page_nofail(sb, e_blkaddr, false);
                *mpage_ret = mpage;
        } else {
                lock_page(mpage);
 
        struct xattr_iter it;
        unsigned i;
        struct erofs_xattr_ibody_header *ih;
+       struct super_block *sb;
        struct erofs_sb_info *sbi;
        struct erofs_vnode *vi;
        bool atomic_map;
        vi = EROFS_V(inode);
        BUG_ON(!vi->xattr_isize);
 
-       sbi = EROFS_I_SB(inode);
+       sb = inode->i_sb;
+       sbi = EROFS_SB(sb);
        it.blkaddr = erofs_blknr(iloc(sbi, vi->nid) + vi->inode_isize);
        it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
 
-       it.page = erofs_get_inline_page(inode, it.blkaddr);
+       it.page = erofs_get_inline_page_nofail(inode, it.blkaddr);
        BUG_ON(IS_ERR(it.page));
 
        /* read in shared xattr array (non-atomic, see kmalloc below) */
                        BUG_ON(it.ofs != EROFS_BLKSIZ);
                        xattr_iter_end(&it, atomic_map);
 
-                       it.page = erofs_get_meta_page(inode->i_sb,
+                       it.page = erofs_get_meta_page_nofail(sb,
                                ++it.blkaddr, S_ISDIR(inode->i_mode));
                        BUG_ON(IS_ERR(it.page));
 
                xattr_iter_end(it, true);
 
                it->blkaddr += erofs_blknr(it->ofs);
-               it->page = erofs_get_meta_page(it->sb, it->blkaddr, false);
+               it->page = erofs_get_meta_page_nofail(it->sb,
+                       it->blkaddr, false);
                BUG_ON(IS_ERR(it->page));
 
                it->kaddr = kmap_atomic(it->page);
        it->blkaddr = erofs_blknr(iloc(sbi, vi->nid) + inline_xattr_ofs);
        it->ofs = erofs_blkoff(iloc(sbi, vi->nid) + inline_xattr_ofs);
 
-       it->page = erofs_get_inline_page(inode, it->blkaddr);
+       it->page = erofs_get_inline_page_nofail(inode, it->blkaddr);
        BUG_ON(IS_ERR(it->page));
        it->kaddr = kmap_atomic(it->page);
 
 static int shared_getxattr(struct inode *inode, struct getxattr_iter *it)
 {
        struct erofs_vnode *const vi = EROFS_V(inode);
-       struct erofs_sb_info *const sbi = EROFS_SB(inode->i_sb);
+       struct super_block *const sb = inode->i_sb;
+       struct erofs_sb_info *const sbi = EROFS_SB(sb);
        unsigned i;
        int ret = -ENOATTR;
 
                        if (i)
                                xattr_iter_end(&it->it, true);
 
-                       it->it.page = erofs_get_meta_page(inode->i_sb,
+                       it->it.page = erofs_get_meta_page_nofail(sb,
                                blkaddr, false);
                        BUG_ON(IS_ERR(it->it.page));
                        it->it.kaddr = kmap_atomic(it->it.page);
 {
        struct inode *const inode = d_inode(it->dentry);
        struct erofs_vnode *const vi = EROFS_V(inode);
-       struct erofs_sb_info *const sbi = EROFS_I_SB(inode);
+       struct super_block *const sb = inode->i_sb;
+       struct erofs_sb_info *const sbi = EROFS_SB(sb);
        unsigned i;
        int ret = 0;
 
                        if (i)
                                xattr_iter_end(&it->it, true);
 
-                       it->it.page = erofs_get_meta_page(inode->i_sb,
+                       it->it.page = erofs_get_meta_page_nofail(sb,
                                blkaddr, false);
                        BUG_ON(IS_ERR(it->it.page));
                        it->it.kaddr = kmap_atomic(it->it.page);