Merge tag 'erofs-for-7.3-rc3-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs

Pull erofs updates from Gao Xiang:
 "The most impactful fix here is to disable LZ4 rolling decompression
  for now.

  AWS folks recently found their systems could get corrupted data with
  some rare, specific LZ4 datasets, and after a deeper analysis, I found
  the root cause is that there could be uncontrolled backward memory
  copies in the current LZ4 implementation and it breaks the assumption
  of the rolling decompression optimization, since the kernel LZ4
  codebase is out of our control and it needs more time to plan how to
  do next, so disable LZ4 rolling decompression for now to ensure data
  correctness for real production on these rare cases first. The
  technical details also see the corresponding commit.

  Other changes are random minor fixes.

  Summary:

   - Disable LZ4 rolling decompression for now due to the uncontrolled
     LZ4 implementation

   - Fix missing sysfs feature entry for xattr prefixes

   - Fix invalid LZMA decoders on resize failure

   - Rearrange the inode_share cache key to avoid potential collisions

   - Fix erofs_bread() when fsoffset is used on sub-page-block EROFS
     filesystems"

* tag 'erofs-for-7.3-rc3-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs:
  erofs: add missing buf->off in erofs_bread()
  erofs: delimit inode_share cache key components
  erofs: disable LZ4 rolling decompression for now
  erofs: preserve LZMA decoders on resize failure
  erofs: add sysfs feature entry for xattr prefixes
This commit is contained in:
Linus Torvalds
2026-09-12 08:18:50 -07:00
8 changed files with 39 additions and 73 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ Description: Shows all enabled kernel features.
Supported features:
compr_cfgs, big_pcluster, chunked_file, device_table,
compr_head2, sb_chksum, ztailpacking, dedupe, fragments,
48bit, metabox.
xattr_prefixes, 48bit, metabox.
What: /sys/fs/erofs/<disk>/sync_decompress
Date: November 2021
+1 -1
View File
@@ -48,7 +48,7 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, bool need_kmap)
return NULL;
if (!buf->base)
buf->base = kmap_local_page(buf->page);
return buf->base + (offset & ~PAGE_MASK);
return buf->base + ((buf->off + offset) & ~PAGE_MASK);
}
int erofs_init_metabuf(struct erofs_buf *buf, struct super_block *sb,
+12 -43
View File
@@ -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) {
+14 -4
View File
@@ -5,6 +5,7 @@
struct z_erofs_lzma {
struct z_erofs_lzma *next;
struct xz_dec_microlzma *state;
unsigned int dict_size;
u8 bounce[PAGE_SIZE];
};
@@ -128,11 +129,19 @@ again:
err = 0;
/* 2. walk each isolated stream and grow max dict_size if needed */
for (strm = head; strm; strm = strm->next) {
struct xz_dec_microlzma *state;
if (strm->dict_size >= dict_size)
continue;
state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size);
if (!state) {
err = -ENOMEM;
break;
}
if (strm->state)
xz_dec_microlzma_end(strm->state);
strm->state = xz_dec_microlzma_alloc(XZ_PREALLOC, dict_size);
if (!strm->state)
err = -ENOMEM;
strm->state = state;
strm->dict_size = dict_size;
}
/* 3. push back all to the global list and update max dict_size */
@@ -142,7 +151,8 @@ again:
spin_unlock(&z_erofs_lzma_lock);
wake_up_all(&z_erofs_lzma_wq);
z_erofs_lzma_max_dictsize = dict_size;
if (!err)
z_erofs_lzma_max_dictsize = dict_size;
mutex_unlock(&lzma_resize_mutex);
return err;
}
+1 -5
View File
@@ -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 {
+2
View File
@@ -95,6 +95,7 @@ EROFS_ATTR_FEATURE(sb_chksum);
EROFS_ATTR_FEATURE(ztailpacking);
EROFS_ATTR_FEATURE(fragments);
EROFS_ATTR_FEATURE(dedupe);
EROFS_ATTR_FEATURE(xattr_prefixes);
EROFS_ATTR_FEATURE(48bit);
EROFS_ATTR_FEATURE(metabox);
@@ -108,6 +109,7 @@ static struct attribute *erofs_feat_attrs[] = {
ATTR_LIST(ztailpacking),
ATTR_LIST(fragments),
ATTR_LIST(dedupe),
ATTR_LIST(xattr_prefixes),
ATTR_LIST(48bit),
ATTR_LIST(metabox),
NULL,
+5 -4
View File
@@ -620,8 +620,8 @@ int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp,
{
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
struct erofs_xattr_prefix_item *prefix;
int domainlen, valuelen, base_index;
const char *infix;
int valuelen, base_index;
if (!test_opt(&sbi->opt, INODE_SHARE))
return -EOPNOTSUPP;
@@ -633,17 +633,18 @@ int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp,
valuelen = erofs_getxattr(inode, base_index, infix, NULL, 0);
if (valuelen <= 0 || valuelen > (1 << sbi->blkszbits))
return -EFSCORRUPTED;
fp->size = valuelen + (domain_id ? strlen(domain_id) : 0);
domainlen = strlen(domain_id);
fp->size = domainlen + 1 + valuelen;
fp->opaque = kmalloc(fp->size, GFP_KERNEL);
if (!fp->opaque)
return -ENOMEM;
memcpy(fp->opaque, domain_id, domainlen + 1);
if (valuelen != erofs_getxattr(inode, base_index, infix,
fp->opaque, valuelen)) {
fp->opaque + domainlen + 1, valuelen)) {
kfree(fp->opaque);
fp->opaque = NULL;
return -EFSCORRUPTED;
}
memcpy(fp->opaque + valuelen, domain_id, fp->size - valuelen);
return 0;
}
#endif
+3 -15
View File
@@ -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);