summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-07-27 09:31:44 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-07-27 09:31:44 -0700
commite895a6fc204ff662bafe6b857f571424f1d17888 (patch)
treec91f40e3147fcd096d99ad201729e36f89708ea2
parente63c75f72faa04af24e4b11d4047e7ac1e80ba5b (diff)
parentc9b47e6b23114e939b17f818471c7a46e59006e7 (diff)
Merge tag 'erofs-for-7.2-rc6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs
Pull erofs fixes from Gao Xiang: "Fix a regression in page cache sharing which can cause a NULL pointer dereference, and limit LZMA stream memory usage on systems with many CPUs. - Keep a valid f_path for page cache sharing to fix a recent mincore() NULL pointer dereference - Limit LZMA stream pool size when too many processors are available - Sync up with Hongbo Li's latest email address" * tag 'erofs-for-7.2-rc6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs: erofs: cap LZMA stream pool size erofs: ensure valid f_path for page cache sharing MAINTAINERS: update Hongbo Li's email address
-rw-r--r--MAINTAINERS2
-rw-r--r--fs/erofs/Kconfig15
-rw-r--r--fs/erofs/decompressor_lzma.c3
-rw-r--r--fs/erofs/internal.h4
-rw-r--r--fs/erofs/ishare.c58
5 files changed, 48 insertions, 34 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 716acfc3d7c1..2f9472c1a090 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9617,7 +9617,7 @@ M: Chao Yu <chao@kernel.org>
R: Yue Hu <zbestahu@gmail.com>
R: Jeffle Xu <jefflexu@linux.alibaba.com>
R: Sandeep Dhavale <dhavale@google.com>
-R: Hongbo Li <lihongbo22@huawei.com>
+R: Hongbo Li <hongbohbli@tencent.com>
R: Chunhai Guo <guochunhai@vivo.com>
L: linux-erofs@lists.ozlabs.org
S: Maintained
diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig
index 4789b1077d8c..8ca1767dafb6 100644
--- a/fs/erofs/Kconfig
+++ b/fs/erofs/Kconfig
@@ -131,6 +131,20 @@ config EROFS_FS_ZIP_LZMA
Say N if you want to disable LZMA compression support.
+config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS
+ int "EROFS LZMA default maximum decompression streams"
+ depends on EROFS_FS_ZIP_LZMA
+ range 1 NR_CPUS
+ default 16
+ help
+ By default EROFS allocates one LZMA decompression stream per CPU.
+ Each stream can hold a dictionary of up to 8 MiB taken from the
+ mounted image, so on systems with many CPUs this can reserve a lot
+ of memory. This caps the default; the lzma_streams module parameter
+ still overrides it.
+
+ If unsure, keep the default of 16.
+
config EROFS_FS_ZIP_DEFLATE
bool "EROFS DEFLATE compressed data support"
depends on EROFS_FS_ZIP
@@ -189,6 +203,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI
config EROFS_FS_PAGE_CACHE_SHARE
bool "EROFS page cache share support (experimental)"
depends on EROFS_FS && EROFS_FS_XATTR
+ select FS_STACK
help
This enables page cache sharing among inodes with identical
content fingerprints on the same machine.
diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c
index f6692d0f2f04..6b0cdb446c6a 100644
--- a/fs/erofs/decompressor_lzma.c
+++ b/fs/erofs/decompressor_lzma.c
@@ -51,7 +51,8 @@ static int __init z_erofs_lzma_init(void)
/* by default, use # of possible CPUs instead */
if (!z_erofs_lzma_nstrms)
- z_erofs_lzma_nstrms = num_possible_cpus();
+ z_erofs_lzma_nstrms = min_t(unsigned int, num_possible_cpus(),
+ CONFIG_EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS);
for (i = 0; i < z_erofs_lzma_nstrms; ++i) {
struct z_erofs_lzma *strm = kzalloc_obj(*strm);
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 580f8d9f14e7..57bd21859c65 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -288,8 +288,8 @@ struct erofs_inode {
struct erofs_inode_fingerprint fingerprint;
spinlock_t ishare_lock;
};
- /* for each real inode */
- struct inode *sharedinode;
+ /* for each real filesystem inode */
+ struct dentry *sharedentry;
};
#endif
/* the corresponding vfs inode */
diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c
index a1a20a5ba548..fa7d4112dec5 100644
--- a/fs/erofs/ishare.c
+++ b/fs/erofs/ishare.c
@@ -2,14 +2,13 @@
/*
* Copyright (C) 2024, Alibaba Cloud
*/
+#include <linux/backing-file.h>
#include <linux/xxhash.h>
#include <linux/mount.h>
#include <linux/security.h>
#include "internal.h"
#include "xattr.h"
-#include "../internal.h"
-
static struct vfsmount *erofs_ishare_mnt;
static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
@@ -33,10 +32,12 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data)
bool erofs_ishare_fill_inode(struct inode *inode)
{
+ static const struct file_operations empty_fops = {};
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
const struct address_space_operations *aops;
struct erofs_inode *vi = EROFS_I(inode);
struct erofs_inode_fingerprint fp;
+ struct dentry *sd;
struct inode *si;
aops = erofs_get_aops(inode);
@@ -49,7 +50,9 @@ bool erofs_ishare_fill_inode(struct inode *inode)
xxh32(fp.opaque, fp.size, 0),
erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp);
if (si && (inode_state_read_once(si) & I_NEW)) {
+ si->i_fop = &empty_fops;
si->i_mapping->a_ops = aops;
+ si->i_mode = 0444 | S_IFREG;
si->i_size = inode->i_size;
unlock_new_inode(si);
} else {
@@ -65,7 +68,10 @@ bool erofs_ishare_fill_inode(struct inode *inode)
return false;
}
}
- vi->sharedinode = si;
+ sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */
+ if (IS_ERR(sd))
+ return false;
+ vi->sharedentry = sd;
INIT_LIST_HEAD(&vi->ishare_list);
spin_lock(&EROFS_I(si)->ishare_lock);
list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
@@ -75,48 +81,40 @@ bool erofs_ishare_fill_inode(struct inode *inode)
void erofs_ishare_free_inode(struct inode *inode)
{
- struct erofs_inode *vi = EROFS_I(inode);
- struct inode *sharedinode = vi->sharedinode;
+ struct erofs_inode *vi = EROFS_I(inode), *svi;
- if (!sharedinode)
+ if (!vi->sharedentry)
return;
- spin_lock(&EROFS_I(sharedinode)->ishare_lock);
+ svi = EROFS_I(d_inode(vi->sharedentry));
+ spin_lock(&svi->ishare_lock);
list_del(&vi->ishare_list);
- spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
- iput(sharedinode);
- vi->sharedinode = NULL;
+ spin_unlock(&svi->ishare_lock);
+ dput(vi->sharedentry);
+ vi->sharedentry = NULL;
}
static int erofs_ishare_file_open(struct inode *inode, struct file *file)
{
- struct inode *sharedinode = EROFS_I(inode)->sharedinode;
- struct file *realfile;
+ struct path sharedpath = {
+ .mnt = erofs_ishare_mnt,
+ .dentry = EROFS_I(inode)->sharedentry,
+ };
+ struct file *rf;
if (file->f_flags & O_DIRECT)
return -EINVAL;
- realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred(),
- file);
- if (IS_ERR(realfile))
- return PTR_ERR(realfile);
- ihold(sharedinode);
- realfile->f_op = &erofs_file_fops;
- realfile->f_inode = sharedinode;
- realfile->f_mapping = sharedinode->i_mapping;
- path_get(&file->f_path);
- backing_file_set_user_path(realfile, &file->f_path);
-
- file_ra_state_init(&realfile->f_ra, file->f_mapping);
- realfile->private_data = EROFS_I(inode);
- file->private_data = realfile;
+
+ rf = backing_file_open(file, file->f_flags | O_NOATIME,
+ &sharedpath, current_cred());
+ if (IS_ERR(rf))
+ return PTR_ERR(rf);
+ file->private_data = rf;
return 0;
}
static int erofs_ishare_file_release(struct inode *inode, struct file *file)
{
- struct file *realfile = file->private_data;
-
- iput(realfile->f_inode);
- fput(realfile);
+ fput(file->private_data);
file->private_data = NULL;
return 0;
}