All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] erofs: disable LZ4 rolling decompression for now
@ 2026-09-03 14:28 Gao Xiang
  2026-09-03 14:57 ` Gao Xiang
  0 siblings, 1 reply; 3+ messages in thread
From: Gao Xiang @ 2026-09-03 14:28 UTC (permalink / raw)
  To: linux-erofs; +Cc: LKML, Gao Xiang, Walther, Jens-Uwe, Yann Collet

LZ4 rolling decompression [1] was introduced to reduce the memory
footprint of temporary pages:

 For many cases, it is needed for users to read small data within
 a compressed extent (pcluster), either due to random small read, or
 since uptodate folios (typically order-0) cannot be reused for
 decompression again since decompression algorithm refills
 already-uptodate folios.

Rolling decompression works because LZ4 is LZ77-based and only refers
to the most recent 64 KiB of decompressed data, so in theory only a
bounded rolling window of temporary pages is needed when decompressing.

It can save a lot of temporary memory, e.g.
 601,960-byte data can be compressed into a 256k LZ4 compressed extent,
 which means it needs 146 extra pages per request in the worst case if
 rolling decompression is disabled.

However, the upstream LZ4 implementation is not under EROFS' control:
For example, the literal copy memmove() may still **copy long literals
backward** on x86 based on the address comparison even when the source
and destination ranges do not overlap (IOWs, inline decompression
doesn't need to be considered here). That breaks the rolling assumption
and makes the optimization broken.

Disable it for now to make sure the data correctness first since EROFS
is used everywhere now: The rolling window approach can be revived once
we either ensure that the official LZ4 code always copies forward for
non-overlapping ranges or maintain our own LZ4 implementation in EROFS.

The main impact is a higher runtime memory footprint; However, recent
commit 0f6273ab4637 ("erofs: add a reserved buffer pool for lz4
decompression") helps mitigate this when enabled but it's still not
perfect.

[1] https://www.usenix.org/conference/atc19/presentation/gao
    § 3.3 Decompression

Reported-by: "Walther, Jens-Uwe" <waltju@amazon.de>
Closes: https://lore.kernel.org/r/BEZP281MB2102E57CD31862B8D958B33DD2AC2@BEZP281MB2102.DEUP281.PROD.OUTLOOK.COM
Fixes: 8e6c8fa9f2e9 ("erofs: enable big pcluster feature")
Cc: Yann Collet <yann.collet.73@gmail.com>
Signed-off-by: Gao Xiang <xiang@kernel.org>
---
 fs/erofs/decompressor.c | 55 +++++++++--------------------------------
 fs/erofs/internal.h     |  6 +----
 fs/erofs/zdata.c        | 18 +++-----------
 3 files changed, 16 insertions(+), 63 deletions(-)

diff --git a/fs/erofs/decompressor.c b/fs/erofs/decompressor.c
index 27caf4bebddc..d387b27c4ee2 100644
--- a/fs/erofs/decompressor.c
+++ b/fs/erofs/decompressor.c
@@ -7,8 +7,6 @@
 #include "compress.h"
 #include <linux/lz4.h>
 
-#define LZ4_MAX_DISTANCE_PAGES	(DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1)
-
 static int z_erofs_load_lz4_config(struct super_block *sb,
 			    struct erofs_super_block *dsb, void *data, int size)
 {
@@ -21,8 +19,6 @@ static int z_erofs_load_lz4_config(struct super_block *sb,
 			erofs_err(sb, "invalid lz4 cfgs, size=%u", size);
 			return -EINVAL;
 		}
-		distance = le16_to_cpu(lz4->max_distance);
-
 		sbi->lz4.max_pclusterblks = le16_to_cpu(lz4->max_pclusterblks);
 		if (!sbi->lz4.max_pclusterblks) {
 			sbi->lz4.max_pclusterblks = 1;	/* reserved case */
@@ -39,45 +35,25 @@ static int z_erofs_load_lz4_config(struct super_block *sb,
 		sbi->lz4.max_pclusterblks = 1;
 		sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4;
 	}
-
-	sbi->lz4.max_distance_pages = distance ?
-					DIV_ROUND_UP(distance, PAGE_SIZE) + 1 :
-					LZ4_MAX_DISTANCE_PAGES;
 	return z_erofs_gbuf_growsize(sbi->lz4.max_pclusterblks);
 }
 
 /*
- * Fill all gaps with bounce pages if it's a sparse page list. Also check if
- * all physical pages are consecutive, which can be seen for moderate CR.
+ * Fill all gaps with bounce pages if it's a sparse page list (for example some
+ * folios are already uptodate and thus can be mapped into userspace). Also
+ * check if pages are physically consecutive, which can be seen for moderate CR.
  */
-static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq,
-					struct page **pagepool)
+static int z_erofs_oneshot_prepare_dstpages(struct z_erofs_decompress_req *rq,
+					    struct page **pagepool)
 {
-	struct page *availables[LZ4_MAX_DISTANCE_PAGES] = { NULL };
-	unsigned long bounced[DIV_ROUND_UP(LZ4_MAX_DISTANCE_PAGES,
-					   BITS_PER_LONG)] = { 0 };
-	unsigned int lz4_max_distance_pages =
-				EROFS_SB(rq->sb)->lz4.max_distance_pages;
 	void *kaddr = NULL;
-	unsigned int i, j, top;
+	unsigned int i;
 
-	top = 0;
-	for (i = j = 0; i < rq->outpages; ++i, ++j) {
-		struct page *const page = rq->out[i];
-		struct page *victim;
-
-		if (j >= lz4_max_distance_pages)
-			j = 0;
-
-		/* 'valid' bounced can only be tested after a complete round */
-		if (!rq->fillgaps && test_bit(j, bounced)) {
-			DBG_BUGON(i < lz4_max_distance_pages);
-			DBG_BUGON(top >= lz4_max_distance_pages);
-			availables[top++] = rq->out[i - lz4_max_distance_pages];
-		}
+	for (i = 0; i < rq->outpages; ++i) {
+		struct page *page, *victim;
 
+		page = rq->out[i];
 		if (page) {
-			__clear_bit(j, bounced);
 			if (!PageHighMem(page)) {
 				if (!i) {
 					kaddr = page_address(page);
@@ -89,21 +65,14 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq,
 					continue;
 				}
 			}
-			kaddr = NULL;
-			continue;
-		}
-		kaddr = NULL;
-		__set_bit(j, bounced);
-
-		if (top) {
-			victim = availables[--top];
 		} else {
 			victim = __erofs_allocpage(pagepool, rq->gfp, true);
 			if (!victim)
 				return -ENOMEM;
 			set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE);
+			rq->out[i] = victim;
 		}
-		rq->out[i] = victim;
+		kaddr = NULL;
 	}
 	return kaddr ? 1 : 0;
 }
@@ -266,7 +235,7 @@ static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
 		dst_maptype = 0;
 	} else {
 		/* general decoding path which can be used for all cases */
-		ret = z_erofs_lz4_prepare_dstpages(rq, pagepool);
+		ret = z_erofs_oneshot_prepare_dstpages(rq, pagepool);
 		if (ret < 0)
 			return ERR_PTR(ret);
 		if (ret > 0) {
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 65974e57aebf..12e3a5b80a5a 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -71,12 +71,8 @@ struct erofs_dev_context {
 	bool flatdev;
 };
 
-/* all filesystem-wide lz4 configurations */
 struct erofs_sb_lz4_info {
-	/* # of pages needed for EROFS lz4 rolling decompression */
-	u16 max_distance_pages;
-	/* maximum possible blocks for pclusters in the filesystem */
-	u16 max_pclusterblks;
+	u16 max_pclusterblks;	/* maximum physical blocks for LZ4 pclusters */
 };
 
 struct erofs_xattr_prefix_item {
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index e1e25ca0d190..6b07e73ee2aa 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1259,7 +1259,7 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)
 	const struct z_erofs_decompressor *alg =
 				z_erofs_decomp[pcl->algorithmformat];
 	bool try_free = true;
-	int i, j, jtop, err2, err = eio ? -EIO : 0;
+	int i, err2, err = eio ? -EIO : 0;
 	struct page *page;
 	bool overlapped;
 	const char *reason;
@@ -1348,7 +1348,6 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)
 	    be->compressed_pages >= be->onstack_pages + Z_EROFS_ONSTACK_PAGES)
 		kvfree(be->compressed_pages);
 
-	jtop = 0;
 	z_erofs_fill_other_copies(be, err);
 	for (i = 0; i < be->nr_pages; ++i) {
 		page = be->decompressed_pages[i];
@@ -1356,22 +1355,11 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)
 			continue;
 
 		DBG_BUGON(z_erofs_page_is_invalidated(page));
-		if (!z_erofs_is_shortlived_page(page)) {
+		if (!z_erofs_is_shortlived_page(page))
 			erofs_onlinefolio_end(page_folio(page), err, true);
-			continue;
-		}
-		if (pcl->algorithmformat != Z_EROFS_COMPRESSION_LZ4) {
+		else
 			erofs_pagepool_add(be->pagepool, page);
-			continue;
-		}
-		for (j = 0; j < jtop && be->decompressed_pages[j] != page; ++j)
-			;
-		if (j >= jtop)	/* this bounce page is newly detected */
-			be->decompressed_pages[jtop++] = page;
 	}
-	while (jtop)
-		erofs_pagepool_add(be->pagepool,
-				   be->decompressed_pages[--jtop]);
 	if (be->decompressed_pages != be->onstack_pages)
 		kvfree(be->decompressed_pages);
 
-- 
2.47.3



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] erofs: disable LZ4 rolling decompression for now
  2026-09-03 14:28 [PATCH] erofs: disable LZ4 rolling decompression for now Gao Xiang
@ 2026-09-03 14:57 ` Gao Xiang
  2026-09-04  6:36   ` Walther, Jens-Uwe
  0 siblings, 1 reply; 3+ messages in thread
From: Gao Xiang @ 2026-09-03 14:57 UTC (permalink / raw)
  To: linux-erofs; +Cc: Walther, Jens-Uwe, Yann Collet, Gao Xiang

[-- Attachment #1: Type: text/plain, Size: 420 bytes --]

BTW, this issue can be reproducible with the image in the attachment
and the following steps (at least on my local VM):

 echo 4 > /proc/sys/vm/drop_caches
 mkdir mnt mnt2
 mount foo.erofs mnt
 cp mnt/seq mnt2/
 while true; do echo 1 > /proc/sys/vm/drop_caches; sleep 1; done &
 while true; do echo 2 > /proc/sys/vm/drop_caches; sleep 1; done &
 stress -p16 -l0 mnt mnt2 -d $(pwd)/baddump   # erofs-utils/contrib/stress

[-- Attachment #2: foo.erofs.xz --]
[-- Type: application/octet-stream, Size: 193292 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] erofs: disable LZ4 rolling decompression for now
  2026-09-03 14:57 ` Gao Xiang
@ 2026-09-04  6:36   ` Walther, Jens-Uwe
  0 siblings, 0 replies; 3+ messages in thread
From: Walther, Jens-Uwe @ 2026-09-04  6:36 UTC (permalink / raw)
  To: Gao Xiang, linux-erofs@lists.ozlabs.org
  Cc: Yann Collet, Gao Xiang, Berning, Sam, Garcia, Arnaldo


[-- Attachment #1.1: Type: text/plain, Size: 2023 bytes --]

Thank you very much!
I will involve to our Bottlerocket team to take care during my annual leave which starts today in the afternoon.

Best regards
Jens-Uwe Walther
Senior Specialist TAM Containers
AWS Enterprise Support
[CKA badge]<https://www.credly.com/badges/b4f55949-4357-4062-93a9-6d0f0f5f3833/linked_in>[CKAD badge]<https://www.credly.com/badges/85b75f03-91d4-4887-8150-cfb9ea39d272> [CKS: Certified Kubernetes Security Specialist] <https://www.credly.com/badges/c26e3b3c-cfc7-46d7-8b7b-7a7a59045c60/>
e: waltju@amazon.com<mailto:waltju@amazon.com> m: +49 152 38870547
[cid:image004.png@01D9D4D4.4DF69060]<http://www.aws.amazon.com/>
From: Gao Xiang <xiang@kernel.org>
Date: Thursday, 3. September 2026 at 16:58
To: linux-erofs@lists.ozlabs.org <linux-erofs@lists.ozlabs.org>
Cc: Walther, Jens-Uwe <waltju@amazon.de>; Yann Collet <yann.collet.73@gmail.com>; Gao Xiang <xiang@kernel.org>
Subject: RE: [EXTERNAL] [PATCH] erofs: disable LZ4 rolling decompression for now

CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you can confirm the sender and know the content is safe.



BTW, this issue can be reproducible with the image in the attachment
and the following steps (at least on my local VM):

 echo 4 > /proc/sys/vm/drop_caches
 mkdir mnt mnt2
 mount foo.erofs mnt
 cp mnt/seq mnt2/
 while true; do echo 1 > /proc/sys/vm/drop_caches; sleep 1; done &
 while true; do echo 2 > /proc/sys/vm/drop_caches; sleep 1; done &
 stress -p16 -l0 mnt mnt2 -d $(pwd)/baddump   # erofs-utils/contrib/stress



Amazon Web Services EMEA SARL
38 avenue John F. Kennedy, L-1855 Luxembourg
Sitz der Gesellschaft: L-1855 Luxemburg
eingetragen im Luxemburgischen Handelsregister unter R.C.S. B186284

Amazon Web Services EMEA SARL, Niederlassung Deutschland
Anni-Albers-Str. 21, D-80807 Muenchen
Sitz der Zweigniederlassung: Muenchen
eingetragen im Handelsregister des Amtsgerichts Muenchen unter HRB 242240
USt-ID DE317013094

[-- Attachment #1.2: Type: text/html, Size: 5468 bytes --]

[-- Attachment #2: image001.png --]
[-- Type: image/png, Size: 5216 bytes --]

[-- Attachment #3: image002.png --]
[-- Type: image/png, Size: 5563 bytes --]

[-- Attachment #4: image003.png --]
[-- Type: image/png, Size: 7022 bytes --]

[-- Attachment #5: image004.png --]
[-- Type: image/png, Size: 3073 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-04  6:37 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 14:28 [PATCH] erofs: disable LZ4 rolling decompression for now Gao Xiang
2026-09-03 14:57 ` Gao Xiang
2026-09-04  6:36   ` Walther, Jens-Uwe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.