15ef3208eSHongzhen Luo // SPDX-License-Identifier: GPL-2.0-or-later
25ef3208eSHongzhen Luo /*
35ef3208eSHongzhen Luo * Copyright (C) 2024, Alibaba Cloud
45ef3208eSHongzhen Luo */
55ef3208eSHongzhen Luo #include <linux/xxhash.h>
65ef3208eSHongzhen Luo #include <linux/mount.h>
75ef3208eSHongzhen Luo #include "internal.h"
85ef3208eSHongzhen Luo #include "xattr.h"
95ef3208eSHongzhen Luo
105ef3208eSHongzhen Luo #include "../internal.h"
115ef3208eSHongzhen Luo
125ef3208eSHongzhen Luo static struct vfsmount *erofs_ishare_mnt;
135ef3208eSHongzhen Luo
erofs_is_ishare_inode(struct inode * inode)1434096ba9SHongbo Li static inline bool erofs_is_ishare_inode(struct inode *inode)
1534096ba9SHongbo Li {
1634096ba9SHongbo Li /* assumed FS_ONDEMAND is excluded with FS_PAGE_CACHE_SHARE feature */
1734096ba9SHongbo Li return inode->i_sb->s_type == &erofs_anon_fs_type;
1834096ba9SHongbo Li }
1934096ba9SHongbo Li
erofs_ishare_iget5_eq(struct inode * inode,void * data)205ef3208eSHongzhen Luo static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
215ef3208eSHongzhen Luo {
225ef3208eSHongzhen Luo struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint;
235ef3208eSHongzhen Luo struct erofs_inode_fingerprint *fp2 = data;
245ef3208eSHongzhen Luo
255ef3208eSHongzhen Luo return fp1->size == fp2->size &&
265ef3208eSHongzhen Luo !memcmp(fp1->opaque, fp2->opaque, fp2->size);
275ef3208eSHongzhen Luo }
285ef3208eSHongzhen Luo
erofs_ishare_iget5_set(struct inode * inode,void * data)295ef3208eSHongzhen Luo static int erofs_ishare_iget5_set(struct inode *inode, void *data)
305ef3208eSHongzhen Luo {
315ef3208eSHongzhen Luo struct erofs_inode *vi = EROFS_I(inode);
325ef3208eSHongzhen Luo
335ef3208eSHongzhen Luo vi->fingerprint = *(struct erofs_inode_fingerprint *)data;
345ef3208eSHongzhen Luo INIT_LIST_HEAD(&vi->ishare_list);
355ef3208eSHongzhen Luo spin_lock_init(&vi->ishare_lock);
365ef3208eSHongzhen Luo return 0;
375ef3208eSHongzhen Luo }
385ef3208eSHongzhen Luo
erofs_ishare_fill_inode(struct inode * inode)395ef3208eSHongzhen Luo bool erofs_ishare_fill_inode(struct inode *inode)
405ef3208eSHongzhen Luo {
415ef3208eSHongzhen Luo struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
425ef3208eSHongzhen Luo struct erofs_inode *vi = EROFS_I(inode);
43*03c0d030SHongbo Li const struct address_space_operations *aops;
445ef3208eSHongzhen Luo struct erofs_inode_fingerprint fp;
455ef3208eSHongzhen Luo struct inode *sharedinode;
465ef3208eSHongzhen Luo unsigned long hash;
475ef3208eSHongzhen Luo
48*03c0d030SHongbo Li aops = erofs_get_aops(inode, true);
49*03c0d030SHongbo Li if (IS_ERR(aops))
50*03c0d030SHongbo Li return false;
515ef3208eSHongzhen Luo if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
525ef3208eSHongzhen Luo return false;
535ef3208eSHongzhen Luo hash = xxh32(fp.opaque, fp.size, 0);
545ef3208eSHongzhen Luo sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash,
555ef3208eSHongzhen Luo erofs_ishare_iget5_eq, erofs_ishare_iget5_set,
565ef3208eSHongzhen Luo &fp);
575ef3208eSHongzhen Luo if (!sharedinode) {
585ef3208eSHongzhen Luo kfree(fp.opaque);
595ef3208eSHongzhen Luo return false;
605ef3208eSHongzhen Luo }
615ef3208eSHongzhen Luo
625ef3208eSHongzhen Luo if (inode_state_read_once(sharedinode) & I_NEW) {
63*03c0d030SHongbo Li sharedinode->i_mapping->a_ops = aops;
645ef3208eSHongzhen Luo sharedinode->i_size = vi->vfs_inode.i_size;
655ef3208eSHongzhen Luo unlock_new_inode(sharedinode);
665ef3208eSHongzhen Luo } else {
675ef3208eSHongzhen Luo kfree(fp.opaque);
68*03c0d030SHongbo Li if (aops != sharedinode->i_mapping->a_ops) {
69*03c0d030SHongbo Li iput(sharedinode);
70*03c0d030SHongbo Li return false;
71*03c0d030SHongbo Li }
725ef3208eSHongzhen Luo if (sharedinode->i_size != vi->vfs_inode.i_size) {
735ef3208eSHongzhen Luo _erofs_printk(inode->i_sb, KERN_WARNING
745ef3208eSHongzhen Luo "size(%lld:%lld) not matches for the same fingerprint\n",
755ef3208eSHongzhen Luo vi->vfs_inode.i_size, sharedinode->i_size);
765ef3208eSHongzhen Luo iput(sharedinode);
775ef3208eSHongzhen Luo return false;
785ef3208eSHongzhen Luo }
795ef3208eSHongzhen Luo }
805ef3208eSHongzhen Luo vi->sharedinode = sharedinode;
815ef3208eSHongzhen Luo INIT_LIST_HEAD(&vi->ishare_list);
825ef3208eSHongzhen Luo spin_lock(&EROFS_I(sharedinode)->ishare_lock);
835ef3208eSHongzhen Luo list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list);
845ef3208eSHongzhen Luo spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
855ef3208eSHongzhen Luo return true;
865ef3208eSHongzhen Luo }
875ef3208eSHongzhen Luo
erofs_ishare_free_inode(struct inode * inode)885ef3208eSHongzhen Luo void erofs_ishare_free_inode(struct inode *inode)
895ef3208eSHongzhen Luo {
905ef3208eSHongzhen Luo struct erofs_inode *vi = EROFS_I(inode);
915ef3208eSHongzhen Luo struct inode *sharedinode = vi->sharedinode;
925ef3208eSHongzhen Luo
935ef3208eSHongzhen Luo if (!sharedinode)
945ef3208eSHongzhen Luo return;
955ef3208eSHongzhen Luo spin_lock(&EROFS_I(sharedinode)->ishare_lock);
965ef3208eSHongzhen Luo list_del(&vi->ishare_list);
975ef3208eSHongzhen Luo spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
985ef3208eSHongzhen Luo iput(sharedinode);
995ef3208eSHongzhen Luo vi->sharedinode = NULL;
1005ef3208eSHongzhen Luo }
1015ef3208eSHongzhen Luo
erofs_ishare_file_open(struct inode * inode,struct file * file)1025ef3208eSHongzhen Luo static int erofs_ishare_file_open(struct inode *inode, struct file *file)
1035ef3208eSHongzhen Luo {
1045ef3208eSHongzhen Luo struct inode *sharedinode = EROFS_I(inode)->sharedinode;
1055ef3208eSHongzhen Luo struct file *realfile;
1065ef3208eSHongzhen Luo
1075ef3208eSHongzhen Luo if (file->f_flags & O_DIRECT)
1085ef3208eSHongzhen Luo return -EINVAL;
1095ef3208eSHongzhen Luo realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred());
1105ef3208eSHongzhen Luo if (IS_ERR(realfile))
1115ef3208eSHongzhen Luo return PTR_ERR(realfile);
1125ef3208eSHongzhen Luo ihold(sharedinode);
1135ef3208eSHongzhen Luo realfile->f_op = &erofs_file_fops;
1145ef3208eSHongzhen Luo realfile->f_inode = sharedinode;
1155ef3208eSHongzhen Luo realfile->f_mapping = sharedinode->i_mapping;
1165ef3208eSHongzhen Luo path_get(&file->f_path);
1175ef3208eSHongzhen Luo backing_file_set_user_path(realfile, &file->f_path);
1185ef3208eSHongzhen Luo
1195ef3208eSHongzhen Luo file_ra_state_init(&realfile->f_ra, file->f_mapping);
1205ef3208eSHongzhen Luo realfile->private_data = EROFS_I(inode);
1215ef3208eSHongzhen Luo file->private_data = realfile;
1225ef3208eSHongzhen Luo return 0;
1235ef3208eSHongzhen Luo }
1245ef3208eSHongzhen Luo
erofs_ishare_file_release(struct inode * inode,struct file * file)1255ef3208eSHongzhen Luo static int erofs_ishare_file_release(struct inode *inode, struct file *file)
1265ef3208eSHongzhen Luo {
1275ef3208eSHongzhen Luo struct file *realfile = file->private_data;
1285ef3208eSHongzhen Luo
1295ef3208eSHongzhen Luo iput(realfile->f_inode);
1305ef3208eSHongzhen Luo fput(realfile);
1315ef3208eSHongzhen Luo file->private_data = NULL;
1325ef3208eSHongzhen Luo return 0;
1335ef3208eSHongzhen Luo }
1345ef3208eSHongzhen Luo
erofs_ishare_file_read_iter(struct kiocb * iocb,struct iov_iter * to)1355ef3208eSHongzhen Luo static ssize_t erofs_ishare_file_read_iter(struct kiocb *iocb,
1365ef3208eSHongzhen Luo struct iov_iter *to)
1375ef3208eSHongzhen Luo {
1385ef3208eSHongzhen Luo struct file *realfile = iocb->ki_filp->private_data;
1395ef3208eSHongzhen Luo struct kiocb dedup_iocb;
1405ef3208eSHongzhen Luo ssize_t nread;
1415ef3208eSHongzhen Luo
1425ef3208eSHongzhen Luo if (!iov_iter_count(to))
1435ef3208eSHongzhen Luo return 0;
1445ef3208eSHongzhen Luo kiocb_clone(&dedup_iocb, iocb, realfile);
1455ef3208eSHongzhen Luo nread = filemap_read(&dedup_iocb, to, 0);
1465ef3208eSHongzhen Luo iocb->ki_pos = dedup_iocb.ki_pos;
1475ef3208eSHongzhen Luo return nread;
1485ef3208eSHongzhen Luo }
1495ef3208eSHongzhen Luo
erofs_ishare_mmap(struct file * file,struct vm_area_struct * vma)1505ef3208eSHongzhen Luo static int erofs_ishare_mmap(struct file *file, struct vm_area_struct *vma)
1515ef3208eSHongzhen Luo {
1525ef3208eSHongzhen Luo struct file *realfile = file->private_data;
1535ef3208eSHongzhen Luo
1545ef3208eSHongzhen Luo vma_set_file(vma, realfile);
1555ef3208eSHongzhen Luo return generic_file_readonly_mmap(file, vma);
1565ef3208eSHongzhen Luo }
1575ef3208eSHongzhen Luo
erofs_ishare_fadvise(struct file * file,loff_t offset,loff_t len,int advice)158d86d7817SHongzhen Luo static int erofs_ishare_fadvise(struct file *file, loff_t offset,
159d86d7817SHongzhen Luo loff_t len, int advice)
160d86d7817SHongzhen Luo {
161d86d7817SHongzhen Luo return vfs_fadvise(file->private_data, offset, len, advice);
162d86d7817SHongzhen Luo }
163d86d7817SHongzhen Luo
1645ef3208eSHongzhen Luo const struct file_operations erofs_ishare_fops = {
1655ef3208eSHongzhen Luo .open = erofs_ishare_file_open,
1665ef3208eSHongzhen Luo .llseek = generic_file_llseek,
1675ef3208eSHongzhen Luo .read_iter = erofs_ishare_file_read_iter,
1685ef3208eSHongzhen Luo .mmap = erofs_ishare_mmap,
1695ef3208eSHongzhen Luo .release = erofs_ishare_file_release,
1705ef3208eSHongzhen Luo .get_unmapped_area = thp_get_unmapped_area,
1715ef3208eSHongzhen Luo .splice_read = filemap_splice_read,
172d86d7817SHongzhen Luo .fadvise = erofs_ishare_fadvise,
1735ef3208eSHongzhen Luo };
1745ef3208eSHongzhen Luo
erofs_real_inode(struct inode * inode,bool * need_iput)17534096ba9SHongbo Li struct inode *erofs_real_inode(struct inode *inode, bool *need_iput)
17634096ba9SHongbo Li {
17734096ba9SHongbo Li struct erofs_inode *vi, *vi_share;
17834096ba9SHongbo Li struct inode *realinode;
17934096ba9SHongbo Li
18034096ba9SHongbo Li *need_iput = false;
18134096ba9SHongbo Li if (!erofs_is_ishare_inode(inode))
18234096ba9SHongbo Li return inode;
18334096ba9SHongbo Li
18434096ba9SHongbo Li vi_share = EROFS_I(inode);
18534096ba9SHongbo Li spin_lock(&vi_share->ishare_lock);
18634096ba9SHongbo Li /* fetch any one as real inode */
18734096ba9SHongbo Li DBG_BUGON(list_empty(&vi_share->ishare_list));
18834096ba9SHongbo Li list_for_each_entry(vi, &vi_share->ishare_list, ishare_list) {
18934096ba9SHongbo Li realinode = igrab(&vi->vfs_inode);
19034096ba9SHongbo Li if (realinode) {
19134096ba9SHongbo Li *need_iput = true;
19234096ba9SHongbo Li break;
19334096ba9SHongbo Li }
19434096ba9SHongbo Li }
19534096ba9SHongbo Li spin_unlock(&vi_share->ishare_lock);
19634096ba9SHongbo Li
19734096ba9SHongbo Li DBG_BUGON(!realinode);
19834096ba9SHongbo Li return realinode;
19934096ba9SHongbo Li }
20034096ba9SHongbo Li
erofs_init_ishare(void)2015ef3208eSHongzhen Luo int __init erofs_init_ishare(void)
2025ef3208eSHongzhen Luo {
2035ef3208eSHongzhen Luo erofs_ishare_mnt = kern_mount(&erofs_anon_fs_type);
2045ef3208eSHongzhen Luo return PTR_ERR_OR_ZERO(erofs_ishare_mnt);
2055ef3208eSHongzhen Luo }
2065ef3208eSHongzhen Luo
erofs_exit_ishare(void)2075ef3208eSHongzhen Luo void erofs_exit_ishare(void)
2085ef3208eSHongzhen Luo {
2095ef3208eSHongzhen Luo kern_unmount(erofs_ishare_mnt);
2105ef3208eSHongzhen Luo }
211