1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * fs/hmdfs/inode.c
4 *
5 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
6 */
7
8 #include "hmdfs_device_view.h"
9 #include "inode.h"
10 #include "comm/connection.h"
11
12 /**
13 * Rules to generate inode numbers:
14 *
15 * "/", "/device_view", "/merge_view", "/device_view/local", "/device_view/cid"
16 * = DOMAIN {3} : dev_id {29} : HMDFS_ROOT {32}
17 *
18 * "/device_view/cid/xxx"
19 * = DOMAIN {3} : dev_id {29} : hash(remote_ino){32}
20 *
21 * "/merge_view/xxx"
22 * = DOMAIN {3} : lower's dev_id {29} : lower's ino_raw {32}
23 */
24
25 #define BIT_WIDE_TOTAL 64
26
27 #define BIT_WIDE_DOMAIN 3
28 #define BIT_WIDE_DEVID 29
29 #define BIT_WIDE_INO_RAW 32
30
31 enum DOMAIN {
32 DOMAIN_ROOT,
33 DOMAIN_DEVICE_LOCAL,
34 DOMAIN_DEVICE_REMOTE,
35 DOMAIN_DEVICE_CLOUD,
36 DOMAIN_MERGE_VIEW,
37 DOMAIN_CLOUD_MERGE_VIEW,
38 DOMAIN_INVALID,
39 };
40
41 union hmdfs_ino {
42 const uint64_t ino_output;
43 struct {
44 uint64_t ino_raw : BIT_WIDE_INO_RAW;
45 uint64_t dev_id : BIT_WIDE_DEVID;
46 uint8_t domain : BIT_WIDE_DOMAIN;
47 };
48 };
49
read_ino_domain(uint64_t ino)50 static uint8_t read_ino_domain(uint64_t ino)
51 {
52 union hmdfs_ino _ino = {
53 .ino_output = ino,
54 };
55
56 return _ino.domain;
57 }
58
59 struct iget_args {
60 /* The lower inode of local/merge/root(part) inode */
61 struct inode *lo_i;
62 /* The peer of remote inode */
63 struct hmdfs_peer *peer;
64 /* The ino of remote inode */
65 uint64_t remote_ino;
66
67 /* The recordId of cloud inode */
68 uint8_t *cloud_record_id;
69 uint8_t *reserved;
70
71 /* Returned inode's ino */
72 union hmdfs_ino ino;
73 };
74
75 /**
76 * iget_test - whether or not the inode with matched hashval is the one we are
77 * looking for
78 *
79 * @inode: the local inode we found in inode cache with matched hashval
80 * @data: struct iget_args
81 */
iget_test(struct inode * inode,void * data)82 static int iget_test(struct inode *inode, void *data)
83 {
84 struct hmdfs_inode_info *hii = hmdfs_i(inode);
85 struct iget_args *ia = data;
86 int res = 0;
87
88 WARN_ON(ia->ino.domain < DOMAIN_ROOT ||
89 ia->ino.domain >= DOMAIN_INVALID);
90
91 if ((read_ino_domain(inode->i_ino) == DOMAIN_ROOT) ||
92 (read_ino_domain(inode->i_ino) != ia->ino.domain))
93 return 0;
94
95 switch (ia->ino.domain) {
96 case DOMAIN_MERGE_VIEW:
97 case DOMAIN_CLOUD_MERGE_VIEW:
98 res = (ia->lo_i == hii->lower_inode);
99 break;
100 case DOMAIN_DEVICE_LOCAL:
101 res = (ia->lo_i == hii->lower_inode);
102 break;
103 case DOMAIN_DEVICE_REMOTE:
104 res = (ia->peer == hii->conn &&
105 ia->remote_ino == hii->remote_ino);
106 break;
107 case DOMAIN_DEVICE_CLOUD:
108 res = (ia->cloud_record_id &&
109 (memcmp(ia->cloud_record_id, hii->cloud_record_id,
110 CLOUD_RECORD_ID_LEN) == 0) &&
111 (ia->reserved[0] == hii->reserved[0]));
112 break;
113 }
114
115 return res;
116 }
117
118 /**
119 * iget_set - initialize a inode with iget_args
120 *
121 * @sb: the superblock of current hmdfs instance
122 * @data: struct iget_args
123 */
iget_set(struct inode * inode,void * data)124 static int iget_set(struct inode *inode, void *data)
125 {
126 struct hmdfs_inode_info *hii = hmdfs_i(inode);
127 struct iget_args *ia = (struct iget_args *)data;
128
129 inode->i_ino = ia->ino.ino_output;
130 inode_inc_iversion(inode);
131
132 hii->conn = ia->peer;
133 hii->remote_ino = ia->remote_ino;
134 hii->lower_inode = ia->lo_i;
135
136 if (ia->cloud_record_id) {
137 memcpy(hii->cloud_record_id, ia->cloud_record_id, CLOUD_RECORD_ID_LEN);
138 memcpy(hii->reserved, ia->reserved, CLOUD_DENTRY_RESERVED_LENGTH);
139 }
140
141 return 0;
142 }
143
make_ino_raw_dev_local(uint64_t lo_ino)144 static uint64_t make_ino_raw_dev_local(uint64_t lo_ino)
145 {
146 if (!(lo_ino >> BIT_WIDE_INO_RAW))
147 return lo_ino;
148
149 return lo_ino * GOLDEN_RATIO_64 >> BIT_WIDE_INO_RAW;
150 }
151
make_ino_raw_dev_remote(uint64_t remote_ino)152 static uint64_t make_ino_raw_dev_remote(uint64_t remote_ino)
153 {
154 return hash_long(remote_ino, BIT_WIDE_INO_RAW);
155 }
156
157 /**
158 * hmdfs_iget5_locked_merge - obtain an inode for the merge-view
159 *
160 * @sb: superblock of current instance
161 * @fst_lo_i: the lower inode of it's first comrade
162 *
163 * Simply replace the lower's domain for a new ino.
164 */
hmdfs_iget5_locked_merge(struct super_block * sb,struct dentry * fst_lo_d)165 struct inode *hmdfs_iget5_locked_merge(struct super_block *sb,
166 struct dentry *fst_lo_d)
167 {
168 struct iget_args ia = {
169 .lo_i = d_inode(fst_lo_d),
170 .peer = NULL,
171 .remote_ino = 0,
172 .cloud_record_id = NULL,
173 .ino.ino_output = 0,
174 };
175
176 if (unlikely(!d_inode(fst_lo_d))) {
177 hmdfs_err("Received a invalid lower inode");
178 return NULL;
179 }
180 if (unlikely(!hmdfs_d(fst_lo_d))) {
181 hmdfs_err("Received a invalid fsdata");
182 return NULL;
183 }
184
185 ia.ino.ino_raw = d_inode(fst_lo_d)->i_ino;
186 ia.ino.dev_id = hmdfs_d(fst_lo_d)->device_id;
187 ia.ino.domain = DOMAIN_MERGE_VIEW;
188 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
189 }
190
hmdfs_iget5_locked_cloud_merge(struct super_block * sb,struct dentry * fst_lo_d)191 struct inode *hmdfs_iget5_locked_cloud_merge(struct super_block *sb,
192 struct dentry *fst_lo_d)
193 {
194 struct iget_args ia = {
195 .lo_i = d_inode(fst_lo_d),
196 .peer = NULL,
197 .remote_ino = 0,
198 .cloud_record_id = NULL,
199 .ino.ino_output = 0,
200 };
201
202 if (unlikely(!d_inode(fst_lo_d))) {
203 hmdfs_err("Received a invalid lower inode");
204 return NULL;
205 }
206 if (unlikely(!hmdfs_d(fst_lo_d))) {
207 hmdfs_err("Received a invalid fsdata");
208 return NULL;
209 }
210
211 ia.ino.ino_raw = d_inode(fst_lo_d)->i_ino;
212 ia.ino.dev_id = hmdfs_d(fst_lo_d)->device_id;
213 ia.ino.domain = DOMAIN_CLOUD_MERGE_VIEW;
214 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
215 }
216
217 /**
218 * hmdfs_iget5_locked_local - obtain an inode for the local-dev-view
219 *
220 * @sb: superblock of current instance
221 * @lo_i: the lower inode from local filesystem
222 *
223 * Hashing local inode's ino to generate our ino. We continue to compare the
224 * address of the lower_inode for uniqueness when collisions occurred.
225 */
hmdfs_iget5_locked_local(struct super_block * sb,struct inode * lo_i)226 struct inode *hmdfs_iget5_locked_local(struct super_block *sb,
227 struct inode *lo_i)
228 {
229 struct iget_args ia = {
230 .lo_i = lo_i,
231 .peer = NULL,
232 .remote_ino = 0,
233 .cloud_record_id = NULL,
234 .ino.ino_output = 0,
235 };
236
237 if (unlikely(!lo_i)) {
238 hmdfs_err("Received a invalid lower inode");
239 return NULL;
240 }
241 ia.ino.ino_raw = make_ino_raw_dev_local(lo_i->i_ino);
242 ia.ino.dev_id = 0;
243 ia.ino.domain = DOMAIN_DEVICE_LOCAL;
244 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
245 }
246
247 /**
248 * hmdfs_iget5_locked_remote - obtain an inode for the remote-dev-view
249 *
250 * @sb: superblock of current instance
251 * @peer: corresponding device node
252 * @remote_ino: remote inode's ino
253 *
254 * Hash remote ino for ino's 32bit~1bit.
255 *
256 * Note that currenly implementation assume the each remote inode has unique
257 * ino. Thus the combination of the peer's unique dev_id and the remote_ino
258 * is enough to determine a unique remote inode.
259 */
hmdfs_iget5_locked_remote(struct super_block * sb,struct hmdfs_peer * peer,uint64_t remote_ino)260 struct inode *hmdfs_iget5_locked_remote(struct super_block *sb,
261 struct hmdfs_peer *peer,
262 uint64_t remote_ino)
263 {
264 struct iget_args ia = {
265 .lo_i = NULL,
266 .peer = peer,
267 .remote_ino = remote_ino,
268 .cloud_record_id = NULL,
269 .ino.ino_output = 0,
270 };
271
272 if (unlikely(!peer)) {
273 hmdfs_err("Received a invalid peer");
274 return NULL;
275 }
276
277 ia.ino.ino_raw = make_ino_raw_dev_remote(remote_ino);
278 ia.ino.dev_id = peer->device_id;
279 ia.ino.domain = DOMAIN_DEVICE_REMOTE;
280 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
281 }
282
283 /**
284 * hmdfs_iget5_locked_cloud - obtain an inode for the cloud-dev-view
285 *
286 * @sb: superblock of current instance
287 * @peer: corresponding device node
288 * @cloud_id: cloud file record id
289 *
290 * Hash remote ino for ino's 32bit~1bit.
291 *
292 * Note that currenly implementation assume the each remote inode has unique
293 * ino. Thus the combination of the peer's unique dev_id and the remote_ino
294 * is enough to determine a unique remote inode.
295 */
hmdfs_iget5_locked_cloud(struct super_block * sb,struct hmdfs_peer * peer,struct hmdfs_lookup_cloud_ret * res)296 struct inode *hmdfs_iget5_locked_cloud(struct super_block *sb,
297 struct hmdfs_peer *peer,
298 struct hmdfs_lookup_cloud_ret *res)
299 {
300 struct iget_args ia = {
301 .lo_i = NULL,
302 .peer = peer,
303 .remote_ino = 0,
304 .cloud_record_id = res->record_id,
305 .reserved = res->reserved,
306 .ino.ino_output = 0,
307 };
308
309 if (unlikely(!peer)) {
310 hmdfs_err("Received a invalid peer");
311 return NULL;
312 }
313
314 ia.ino.ino_raw = make_ino_raw_cloud(res->record_id) + res->reserved[0];
315 ia.ino.dev_id = peer->device_id;
316 ia.ino.domain = DOMAIN_DEVICE_CLOUD;
317 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
318 }
319
hmdfs_iget_locked_root(struct super_block * sb,uint64_t root_ino,struct inode * lo_i,struct hmdfs_peer * peer)320 struct inode *hmdfs_iget_locked_root(struct super_block *sb, uint64_t root_ino,
321 struct inode *lo_i,
322 struct hmdfs_peer *peer)
323 {
324 struct iget_args ia = {
325 .lo_i = lo_i,
326 .peer = peer,
327 .remote_ino = 0,
328 .cloud_record_id = NULL,
329 .ino.ino_raw = root_ino,
330 .ino.dev_id = peer ? peer->device_id : 0,
331 .ino.domain = DOMAIN_ROOT,
332 };
333
334 if (unlikely(root_ino < 0 || root_ino >= HMDFS_ROOT_INVALID)) {
335 hmdfs_err("Root %llu is invalid", root_ino);
336 return NULL;
337 }
338 if (unlikely(root_ino == HMDFS_ROOT_DEV_REMOTE && !peer)) {
339 hmdfs_err("Root %llu received a invalid peer", root_ino);
340 return NULL;
341 }
342
343 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
344 }
345