erofs: allow sharing page cache with the same aops only

Inode with identical data but different @aops cannot be mixed
because the page cache is managed by different subsystems (e.g.,
@aops for compressed on-disk inodes cannot handle plain on-disk
inodes).

In this patch, we never allow inodes to share the page cache
among plain, compressed, and fileio cases. When a shared inode
is created, we initialize @aops that is the same as the initial
real inode, and subsequent inodes cannot share the page cache
if the inferred @aops differ from the corresponding shared inode.

This is reasonable as a first step because, in typical use cases,
if an inode is compressible, it will fall into compressed
inodes across different filesystem images unless users use plain
filesystems. However, in that cases, users will use plain
filesystems all the time.

Fixes: 5ef3208e3b ("erofs: introduce the page cache share feature")
Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
This commit is contained in:
Hongbo Li
2026-02-14 03:02:48 +00:00
committed by Gao Xiang
parent 6de23f81a5
commit 03c0d030f5
3 changed files with 22 additions and 15 deletions

View File

@@ -222,6 +222,7 @@ err_out:
static int erofs_fill_inode(struct inode *inode)
{
const struct address_space_operations *aops;
int err;
trace_erofs_fill_inode(inode);
@@ -254,7 +255,11 @@ static int erofs_fill_inode(struct inode *inode)
}
mapping_set_large_folios(inode->i_mapping);
return erofs_inode_set_aops(inode, inode, false);
aops = erofs_get_aops(inode, false);
if (IS_ERR(aops))
return PTR_ERR(aops);
inode->i_mapping->a_ops = aops;
return 0;
}
/*

View File

@@ -471,26 +471,24 @@ static inline void *erofs_vm_map_ram(struct page **pages, unsigned int count)
return NULL;
}
static inline int erofs_inode_set_aops(struct inode *inode,
struct inode *realinode, bool no_fscache)
static inline const struct address_space_operations *
erofs_get_aops(struct inode *realinode, bool no_fscache)
{
if (erofs_inode_is_data_compressed(EROFS_I(realinode)->datalayout)) {
if (!IS_ENABLED(CONFIG_EROFS_FS_ZIP))
return -EOPNOTSUPP;
return ERR_PTR(-EOPNOTSUPP);
DO_ONCE_LITE_IF(realinode->i_blkbits != PAGE_SHIFT,
erofs_info, realinode->i_sb,
"EXPERIMENTAL EROFS subpage compressed block support in use. Use at your own risk!");
inode->i_mapping->a_ops = &z_erofs_aops;
return 0;
return &z_erofs_aops;
}
inode->i_mapping->a_ops = &erofs_aops;
if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && !no_fscache &&
erofs_is_fscache_mode(realinode->i_sb))
inode->i_mapping->a_ops = &erofs_fscache_access_aops;
return &erofs_fscache_access_aops;
if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) &&
erofs_is_fileio_mode(EROFS_SB(realinode->i_sb)))
inode->i_mapping->a_ops = &erofs_fileio_aops;
return 0;
return &erofs_fileio_aops;
return &erofs_aops;
}
int erofs_register_sysfs(struct super_block *sb);

View File

@@ -40,10 +40,14 @@ bool erofs_ishare_fill_inode(struct inode *inode)
{
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
struct erofs_inode *vi = EROFS_I(inode);
const struct address_space_operations *aops;
struct erofs_inode_fingerprint fp;
struct inode *sharedinode;
unsigned long hash;
aops = erofs_get_aops(inode, true);
if (IS_ERR(aops))
return false;
if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
return false;
hash = xxh32(fp.opaque, fp.size, 0);
@@ -56,15 +60,15 @@ bool erofs_ishare_fill_inode(struct inode *inode)
}
if (inode_state_read_once(sharedinode) & I_NEW) {
if (erofs_inode_set_aops(sharedinode, inode, true)) {
iget_failed(sharedinode);
kfree(fp.opaque);
return false;
}
sharedinode->i_mapping->a_ops = aops;
sharedinode->i_size = vi->vfs_inode.i_size;
unlock_new_inode(sharedinode);
} else {
kfree(fp.opaque);
if (aops != sharedinode->i_mapping->a_ops) {
iput(sharedinode);
return false;
}
if (sharedinode->i_size != vi->vfs_inode.i_size) {
_erofs_printk(inode->i_sb, KERN_WARNING
"size(%lld:%lld) not matches for the same fingerprint\n",