Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/erofs/ishare.c
121797 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (C) 2024, Alibaba Cloud
4
*/
5
#include <linux/xxhash.h>
6
#include <linux/mount.h>
7
#include "internal.h"
8
#include "xattr.h"
9
10
#include "../internal.h"
11
12
static struct vfsmount *erofs_ishare_mnt;
13
14
static inline bool erofs_is_ishare_inode(struct inode *inode)
15
{
16
/* assumed FS_ONDEMAND is excluded with FS_PAGE_CACHE_SHARE feature */
17
return inode->i_sb->s_type == &erofs_anon_fs_type;
18
}
19
20
static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
21
{
22
struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint;
23
struct erofs_inode_fingerprint *fp2 = data;
24
25
return fp1->size == fp2->size &&
26
!memcmp(fp1->opaque, fp2->opaque, fp2->size);
27
}
28
29
static int erofs_ishare_iget5_set(struct inode *inode, void *data)
30
{
31
struct erofs_inode *vi = EROFS_I(inode);
32
33
vi->fingerprint = *(struct erofs_inode_fingerprint *)data;
34
INIT_LIST_HEAD(&vi->ishare_list);
35
spin_lock_init(&vi->ishare_lock);
36
return 0;
37
}
38
39
bool erofs_ishare_fill_inode(struct inode *inode)
40
{
41
struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
42
struct erofs_inode *vi = EROFS_I(inode);
43
const struct address_space_operations *aops;
44
struct erofs_inode_fingerprint fp;
45
struct inode *sharedinode;
46
unsigned long hash;
47
48
aops = erofs_get_aops(inode, true);
49
if (IS_ERR(aops))
50
return false;
51
if (erofs_xattr_fill_inode_fingerprint(&fp, inode, sbi->domain_id))
52
return false;
53
hash = xxh32(fp.opaque, fp.size, 0);
54
sharedinode = iget5_locked(erofs_ishare_mnt->mnt_sb, hash,
55
erofs_ishare_iget5_eq, erofs_ishare_iget5_set,
56
&fp);
57
if (!sharedinode) {
58
kfree(fp.opaque);
59
return false;
60
}
61
62
if (inode_state_read_once(sharedinode) & I_NEW) {
63
sharedinode->i_mapping->a_ops = aops;
64
sharedinode->i_size = vi->vfs_inode.i_size;
65
unlock_new_inode(sharedinode);
66
} else {
67
kfree(fp.opaque);
68
if (aops != sharedinode->i_mapping->a_ops) {
69
iput(sharedinode);
70
return false;
71
}
72
if (sharedinode->i_size != vi->vfs_inode.i_size) {
73
_erofs_printk(inode->i_sb, KERN_WARNING
74
"size(%lld:%lld) not matches for the same fingerprint\n",
75
vi->vfs_inode.i_size, sharedinode->i_size);
76
iput(sharedinode);
77
return false;
78
}
79
}
80
vi->sharedinode = sharedinode;
81
INIT_LIST_HEAD(&vi->ishare_list);
82
spin_lock(&EROFS_I(sharedinode)->ishare_lock);
83
list_add(&vi->ishare_list, &EROFS_I(sharedinode)->ishare_list);
84
spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
85
return true;
86
}
87
88
void erofs_ishare_free_inode(struct inode *inode)
89
{
90
struct erofs_inode *vi = EROFS_I(inode);
91
struct inode *sharedinode = vi->sharedinode;
92
93
if (!sharedinode)
94
return;
95
spin_lock(&EROFS_I(sharedinode)->ishare_lock);
96
list_del(&vi->ishare_list);
97
spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
98
iput(sharedinode);
99
vi->sharedinode = NULL;
100
}
101
102
static int erofs_ishare_file_open(struct inode *inode, struct file *file)
103
{
104
struct inode *sharedinode = EROFS_I(inode)->sharedinode;
105
struct file *realfile;
106
107
if (file->f_flags & O_DIRECT)
108
return -EINVAL;
109
realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred());
110
if (IS_ERR(realfile))
111
return PTR_ERR(realfile);
112
ihold(sharedinode);
113
realfile->f_op = &erofs_file_fops;
114
realfile->f_inode = sharedinode;
115
realfile->f_mapping = sharedinode->i_mapping;
116
path_get(&file->f_path);
117
backing_file_set_user_path(realfile, &file->f_path);
118
119
file_ra_state_init(&realfile->f_ra, file->f_mapping);
120
realfile->private_data = EROFS_I(inode);
121
file->private_data = realfile;
122
return 0;
123
}
124
125
static int erofs_ishare_file_release(struct inode *inode, struct file *file)
126
{
127
struct file *realfile = file->private_data;
128
129
iput(realfile->f_inode);
130
fput(realfile);
131
file->private_data = NULL;
132
return 0;
133
}
134
135
static ssize_t erofs_ishare_file_read_iter(struct kiocb *iocb,
136
struct iov_iter *to)
137
{
138
struct file *realfile = iocb->ki_filp->private_data;
139
struct kiocb dedup_iocb;
140
ssize_t nread;
141
142
if (!iov_iter_count(to))
143
return 0;
144
kiocb_clone(&dedup_iocb, iocb, realfile);
145
nread = filemap_read(&dedup_iocb, to, 0);
146
iocb->ki_pos = dedup_iocb.ki_pos;
147
return nread;
148
}
149
150
static int erofs_ishare_mmap(struct file *file, struct vm_area_struct *vma)
151
{
152
struct file *realfile = file->private_data;
153
154
vma_set_file(vma, realfile);
155
return generic_file_readonly_mmap(file, vma);
156
}
157
158
static int erofs_ishare_fadvise(struct file *file, loff_t offset,
159
loff_t len, int advice)
160
{
161
return vfs_fadvise(file->private_data, offset, len, advice);
162
}
163
164
const struct file_operations erofs_ishare_fops = {
165
.open = erofs_ishare_file_open,
166
.llseek = generic_file_llseek,
167
.read_iter = erofs_ishare_file_read_iter,
168
.mmap = erofs_ishare_mmap,
169
.release = erofs_ishare_file_release,
170
.get_unmapped_area = thp_get_unmapped_area,
171
.splice_read = filemap_splice_read,
172
.fadvise = erofs_ishare_fadvise,
173
};
174
175
struct inode *erofs_real_inode(struct inode *inode, bool *need_iput)
176
{
177
struct erofs_inode *vi, *vi_share;
178
struct inode *realinode;
179
180
*need_iput = false;
181
if (!erofs_is_ishare_inode(inode))
182
return inode;
183
184
vi_share = EROFS_I(inode);
185
spin_lock(&vi_share->ishare_lock);
186
/* fetch any one as real inode */
187
DBG_BUGON(list_empty(&vi_share->ishare_list));
188
list_for_each_entry(vi, &vi_share->ishare_list, ishare_list) {
189
realinode = igrab(&vi->vfs_inode);
190
if (realinode) {
191
*need_iput = true;
192
break;
193
}
194
}
195
spin_unlock(&vi_share->ishare_lock);
196
197
DBG_BUGON(!realinode);
198
return realinode;
199
}
200
201
int __init erofs_init_ishare(void)
202
{
203
struct vfsmount *mnt;
204
int ret;
205
206
mnt = kern_mount(&erofs_anon_fs_type);
207
if (IS_ERR(mnt))
208
return PTR_ERR(mnt);
209
/* generic_fadvise() doesn't work if s_bdi == &noop_backing_dev_info */
210
ret = super_setup_bdi(mnt->mnt_sb);
211
if (ret)
212
kern_unmount(mnt);
213
else
214
erofs_ishare_mnt = mnt;
215
return ret;
216
}
217
218
void erofs_exit_ishare(void)
219
{
220
kern_unmount(erofs_ishare_mnt);
221
}
222
223