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:
5ef3208e3be5 ("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>
static int erofs_fill_inode(struct inode *inode)
{
+ const struct address_space_operations *aops;
int err;
trace_erofs_fill_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;
}
/*
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);
{
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);
}
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",