xref: /kernel/linux/linux-5.10/fs/hmdfs/inode.c (revision 8c2ecf20)
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
31enum 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
41union 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
50static 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
59struct 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 */
82static 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		return 1;
93	if (read_ino_domain(inode->i_ino) != ia->ino.domain)
94		return 0;
95
96	switch (ia->ino.domain) {
97	case DOMAIN_MERGE_VIEW:
98	case DOMAIN_CLOUD_MERGE_VIEW:
99		res = (ia->lo_i == hii->lower_inode);
100		break;
101	case DOMAIN_DEVICE_LOCAL:
102		res = (ia->lo_i == hii->lower_inode);
103		break;
104	case DOMAIN_DEVICE_REMOTE:
105		res = (ia->peer == hii->conn &&
106		       ia->remote_ino == hii->remote_ino);
107		break;
108	case DOMAIN_DEVICE_CLOUD:
109		res = (ia->cloud_record_id &&
110		       (memcmp(ia->cloud_record_id, hii->cloud_record_id,
111			       CLOUD_RECORD_ID_LEN) == 0) &&
112		       (ia->reserved[0] == hii->reserved[0]));
113		break;
114	}
115
116	return res;
117}
118
119/**
120 * iget_set - initialize a inode with iget_args
121 *
122 * @sb: the superblock of current hmdfs instance
123 * @data: struct iget_args
124 */
125static int iget_set(struct inode *inode, void *data)
126{
127	struct hmdfs_inode_info *hii = hmdfs_i(inode);
128	struct iget_args *ia = (struct iget_args *)data;
129
130	inode->i_ino = ia->ino.ino_output;
131	inode_inc_iversion(inode);
132
133	hii->conn = ia->peer;
134	hii->remote_ino = ia->remote_ino;
135	hii->lower_inode = ia->lo_i;
136
137	if (ia->cloud_record_id) {
138		memcpy(hii->cloud_record_id, ia->cloud_record_id, CLOUD_RECORD_ID_LEN);
139		memcpy(hii->reserved, ia->reserved, CLOUD_DENTRY_RESERVED_LENGTH);
140	}
141
142	return 0;
143}
144
145static uint64_t make_ino_raw_dev_local(uint64_t lo_ino)
146{
147	if (!(lo_ino >> BIT_WIDE_INO_RAW))
148		return lo_ino;
149
150	return lo_ino * GOLDEN_RATIO_64 >> BIT_WIDE_INO_RAW;
151}
152
153static uint64_t make_ino_raw_dev_remote(uint64_t remote_ino)
154{
155	return hash_long(remote_ino, BIT_WIDE_INO_RAW);
156}
157
158/**
159 * hmdfs_iget5_locked_merge - obtain an inode for the merge-view
160 *
161 * @sb: superblock of current instance
162 * @fst_lo_i: the lower inode of it's first comrade
163 *
164 * Simply replace the lower's domain for a new ino.
165 */
166struct inode *hmdfs_iget5_locked_merge(struct super_block *sb,
167				       struct dentry *fst_lo_d)
168{
169	struct iget_args ia = {
170		.lo_i = d_inode(fst_lo_d),
171		.peer = NULL,
172		.remote_ino = 0,
173		.cloud_record_id = NULL,
174		.ino.ino_output = 0,
175	};
176
177	if (unlikely(!d_inode(fst_lo_d))) {
178		hmdfs_err("Received a invalid lower inode");
179		return NULL;
180	}
181	if (unlikely(!hmdfs_d(fst_lo_d))) {
182		hmdfs_err("Received a invalid fsdata");
183		return NULL;
184	}
185
186	ia.ino.ino_raw = d_inode(fst_lo_d)->i_ino;
187	ia.ino.dev_id = hmdfs_d(fst_lo_d)->device_id;
188	ia.ino.domain = DOMAIN_MERGE_VIEW;
189	return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
190}
191
192struct inode *hmdfs_iget5_locked_cloud_merge(struct super_block *sb,
193					     struct dentry *fst_lo_d)
194{
195	struct iget_args ia = {
196		.lo_i = d_inode(fst_lo_d),
197		.peer = NULL,
198		.remote_ino = 0,
199		.cloud_record_id = NULL,
200		.ino.ino_output = 0,
201	};
202
203	if (unlikely(!d_inode(fst_lo_d))) {
204		hmdfs_err("Received a invalid lower inode");
205		return NULL;
206	}
207	if (unlikely(!hmdfs_d(fst_lo_d))) {
208		hmdfs_err("Received a invalid fsdata");
209		return NULL;
210	}
211
212	ia.ino.ino_raw = d_inode(fst_lo_d)->i_ino;
213	ia.ino.dev_id = hmdfs_d(fst_lo_d)->device_id;
214	ia.ino.domain = DOMAIN_CLOUD_MERGE_VIEW;
215	return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
216}
217
218/**
219 * hmdfs_iget5_locked_local - obtain an inode for the local-dev-view
220 *
221 * @sb: superblock of current instance
222 * @lo_i: the lower inode from local filesystem
223 *
224 * Hashing local inode's ino to generate our ino. We continue to compare the
225 * address of the lower_inode for uniqueness when collisions occurred.
226 */
227struct inode *hmdfs_iget5_locked_local(struct super_block *sb,
228				       struct inode *lo_i)
229{
230	struct iget_args ia = {
231		.lo_i = lo_i,
232		.peer = NULL,
233		.remote_ino = 0,
234		.cloud_record_id = NULL,
235		.ino.ino_output = 0,
236	};
237
238	if (unlikely(!lo_i)) {
239		hmdfs_err("Received a invalid lower inode");
240		return NULL;
241	}
242	ia.ino.ino_raw = make_ino_raw_dev_local(lo_i->i_ino);
243	ia.ino.dev_id = 0;
244	ia.ino.domain = DOMAIN_DEVICE_LOCAL;
245	return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
246}
247
248/**
249 * hmdfs_iget5_locked_remote - obtain an inode for the remote-dev-view
250 *
251 * @sb: superblock of current instance
252 * @peer: corresponding device node
253 * @remote_ino: remote inode's ino
254 *
255 * Hash remote ino for ino's 32bit~1bit.
256 *
257 * Note that currenly implementation assume the each remote inode has unique
258 * ino. Thus the combination of the peer's unique dev_id and the remote_ino
259 * is enough to determine a unique remote inode.
260 */
261struct inode *hmdfs_iget5_locked_remote(struct super_block *sb,
262					struct hmdfs_peer *peer,
263					uint64_t remote_ino)
264{
265	struct iget_args ia = {
266		.lo_i = NULL,
267		.peer = peer,
268		.remote_ino = remote_ino,
269		.cloud_record_id = NULL,
270		.ino.ino_output = 0,
271	};
272
273	if (unlikely(!peer)) {
274		hmdfs_err("Received a invalid peer");
275		return NULL;
276	}
277
278	ia.ino.ino_raw = make_ino_raw_dev_remote(remote_ino);
279	ia.ino.dev_id = peer->device_id;
280	ia.ino.domain = DOMAIN_DEVICE_REMOTE;
281	return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
282}
283
284/**
285 * hmdfs_iget5_locked_cloud - obtain an inode for the cloud-dev-view
286 *
287 * @sb: superblock of current instance
288 * @peer: corresponding device node
289 * @cloud_id: cloud file record id
290 *
291 * Hash remote ino for ino's 32bit~1bit.
292 *
293 * Note that currenly implementation assume the each remote inode has unique
294 * ino. Thus the combination of the peer's unique dev_id and the remote_ino
295 * is enough to determine a unique remote inode.
296 */
297struct inode *hmdfs_iget5_locked_cloud(struct super_block *sb,
298				       struct hmdfs_peer *peer,
299				       struct hmdfs_lookup_cloud_ret *res)
300{
301	struct iget_args ia = {
302		.lo_i = NULL,
303		.peer = peer,
304		.remote_ino = 0,
305		.cloud_record_id = res->record_id,
306		.reserved = res->reserved,
307		.ino.ino_output = 0,
308	};
309
310	if (unlikely(!peer)) {
311		hmdfs_err("Received a invalid peer");
312		return NULL;
313	}
314
315	ia.ino.ino_raw = make_ino_raw_cloud(res->record_id) + res->reserved[0];
316	ia.ino.dev_id = peer->device_id;
317	ia.ino.domain = DOMAIN_DEVICE_CLOUD;
318	return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
319}
320
321struct inode *hmdfs_iget_locked_root(struct super_block *sb, uint64_t root_ino,
322				     struct inode *lo_i,
323				     struct hmdfs_peer *peer)
324{
325	struct iget_args ia = {
326		.lo_i = lo_i,
327		.peer = peer,
328		.remote_ino = 0,
329		.cloud_record_id = NULL,
330		.ino.ino_raw = root_ino,
331		.ino.dev_id = peer ? peer->device_id : 0,
332		.ino.domain = DOMAIN_ROOT,
333	};
334
335	if (unlikely(root_ino < 0 || root_ino >= HMDFS_ROOT_INVALID)) {
336		hmdfs_err("Root %llu is invalid", root_ino);
337		return NULL;
338	}
339	if (unlikely(root_ino == HMDFS_ROOT_DEV_REMOTE && !peer)) {
340		hmdfs_err("Root %llu received a invalid peer", root_ino);
341		return NULL;
342	}
343
344	return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
345}
346
347
348void hmdfs_update_upper_file(struct file *upper_file, struct file *lower_file)
349{
350	loff_t upper_size = i_size_read(upper_file->f_inode);
351	loff_t lower_size = i_size_read(lower_file->f_inode);
352
353	if (upper_file->f_inode->i_mapping && upper_size != lower_size) {
354		i_size_write(upper_file->f_inode, lower_size);
355		truncate_inode_pages(upper_file->f_inode->i_mapping, 0);
356	}
357}