--- /dev/null
+From 4012d78562193ef5eb613bad4b0c0fa187637cfe Mon Sep 17 00:00:00 2001
+From: Junbeom Yeom <junbeom.yeom@samsung.com>
+Date: Fri, 19 Dec 2025 21:40:31 +0900
+Subject: erofs: fix unexpected EIO under memory pressure
+
+From: Junbeom Yeom <junbeom.yeom@samsung.com>
+
+commit 4012d78562193ef5eb613bad4b0c0fa187637cfe upstream.
+
+erofs readahead could fail with ENOMEM under the memory pressure because
+it tries to alloc_page with GFP_NOWAIT | GFP_NORETRY, while GFP_KERNEL
+for a regular read. And if readahead fails (with non-uptodate folios),
+the original request will then fall back to synchronous read, and
+`.read_folio()` should return appropriate errnos.
+
+However, in scenarios where readahead and read operations compete,
+read operation could return an unintended EIO because of an incorrect
+error propagation.
+
+To resolve this, this patch modifies the behavior so that, when the
+PCL is for read(which means pcl.besteffort is true), it attempts actual
+decompression instead of propagating the privios error except initial EIO.
+
+- Page size: 4K
+- The original size of FileA: 16K
+- Compress-ratio per PCL: 50% (Uncompressed 8K -> Compressed 4K)
+[page0, page1] [page2, page3]
+[PCL0]---------[PCL1]
+
+- functions declaration:
+ . pread(fd, buf, count, offset)
+ . readahead(fd, offset, count)
+- Thread A tries to read the last 4K
+- Thread B tries to do readahead 8K from 4K
+- RA, besteffort == false
+- R, besteffort == true
+
+ <process A> <process B>
+
+pread(FileA, buf, 4K, 12K)
+ do readahead(page3) // failed with ENOMEM
+ wait_lock(page3)
+ if (!uptodate(page3))
+ goto do_read
+ readahead(FileA, 4K, 8K)
+ // Here create PCL-chain like below:
+ // [null, page1] [page2, null]
+ // [PCL0:RA]-----[PCL1:RA]
+...
+ do read(page3) // found [PCL1:RA] and add page3 into it,
+ // and then, change PCL1 from RA to R
+...
+ // Now, PCL-chain is as below:
+ // [null, page1] [page2, page3]
+ // [PCL0:RA]-----[PCL1:R]
+
+ // try to decompress PCL-chain...
+ z_erofs_decompress_queue
+ err = 0;
+
+ // failed with ENOMEM, so page 1
+ // only for RA will not be uptodated.
+ // it's okay.
+ err = decompress([PCL0:RA], err)
+
+ // However, ENOMEM propagated to next
+ // PCL, even though PCL is not only
+ // for RA but also for R. As a result,
+ // it just failed with ENOMEM without
+ // trying any decompression, so page2
+ // and page3 will not be uptodated.
+ ** BUG HERE ** --> err = decompress([PCL1:R], err)
+
+ return err as ENOMEM
+...
+ wait_lock(page3)
+ if (!uptodate(page3))
+ return EIO <-- Return an unexpected EIO!
+...
+
+Fixes: 2349d2fa02db ("erofs: sunset unneeded NOFAILs")
+Cc: stable@vger.kernel.org
+Reviewed-by: Jaewook Kim <jw5454.kim@samsung.com>
+Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
+Signed-off-by: Junbeom Yeom <junbeom.yeom@samsung.com>
+Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/erofs/zdata.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/fs/erofs/zdata.c
++++ b/fs/erofs/zdata.c
+@@ -1262,17 +1262,17 @@ static int z_erofs_parse_in_bvecs(struct
+ return err;
+ }
+
+-static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
++static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)
+ {
+ struct erofs_sb_info *const sbi = EROFS_SB(be->sb);
+ struct z_erofs_pcluster *pcl = be->pcl;
+ unsigned int pclusterpages = z_erofs_pclusterpages(pcl);
+ const struct z_erofs_decompressor *decomp =
+ z_erofs_decomp[pcl->algorithmformat];
+- int i, j, jtop, err2;
++ bool try_free = true;
++ int i, j, jtop, err2, err = eio ? -EIO : 0;
+ struct page *page;
+ bool overlapped;
+- bool try_free = true;
+
+ mutex_lock(&pcl->lock);
+ be->nr_pages = PAGE_ALIGN(pcl->length + pcl->pageofs_out) >> PAGE_SHIFT;
+@@ -1400,12 +1400,12 @@ static int z_erofs_decompress_queue(cons
+ .pcl = io->head,
+ };
+ struct z_erofs_pcluster *next;
+- int err = io->eio ? -EIO : 0;
++ int err = 0;
+
+ for (; be.pcl != Z_EROFS_PCLUSTER_TAIL; be.pcl = next) {
+ DBG_BUGON(!be.pcl);
+ next = READ_ONCE(be.pcl->next);
+- err = z_erofs_decompress_pcluster(&be, err) ?: err;
++ err = z_erofs_decompress_pcluster(&be, io->eio) ?: err;
+ }
+ return err;
+ }