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_MERGE_VIEW,
36 DOMAIN_INVALID,
37 };
38
39 union hmdfs_ino {
40 const uint64_t ino_output;
41 struct {
42 uint64_t ino_raw : BIT_WIDE_INO_RAW;
43 uint64_t dev_id : BIT_WIDE_DEVID;
44 uint8_t domain : BIT_WIDE_DOMAIN;
45 };
46 };
47
read_ino_domain(uint64_t ino)48 static uint8_t read_ino_domain(uint64_t ino)
49 {
50 union hmdfs_ino _ino = {
51 .ino_output = ino,
52 };
53
54 return _ino.domain;
55 }
56
57 struct iget_args {
58 /* The lower inode of local/merge/root(part) inode */
59 struct inode *lo_i;
60 /* The peer of remote inode */
61 struct hmdfs_peer *peer;
62 /* The ino of remote inode */
63 uint64_t remote_ino;
64
65 /* Returned inode's ino */
66 union hmdfs_ino ino;
67 };
68
69 /**
70 * iget_test - whether or not the inode with matched hashval is the one we are
71 * looking for
72 *
73 * @inode: the local inode we found in inode cache with matched hashval
74 * @data: struct iget_args
75 */
iget_test(struct inode * inode,void * data)76 static int iget_test(struct inode *inode, void *data)
77 {
78 struct hmdfs_inode_info *hii = hmdfs_i(inode);
79 struct iget_args *ia = data;
80 int res = 0;
81
82 WARN_ON(ia->ino.domain < DOMAIN_ROOT ||
83 ia->ino.domain >= DOMAIN_INVALID);
84
85 if (read_ino_domain(inode->i_ino) == DOMAIN_ROOT)
86 return 0;
87
88 switch (ia->ino.domain) {
89 case DOMAIN_MERGE_VIEW:
90 res = (ia->lo_i == hii->lower_inode);
91 break;
92 case DOMAIN_DEVICE_LOCAL:
93 res = (ia->lo_i == hii->lower_inode);
94 break;
95 case DOMAIN_DEVICE_REMOTE:
96 res = (ia->peer == hii->conn &&
97 ia->remote_ino == hii->remote_ino);
98 break;
99 }
100
101 return res;
102 }
103
104 /**
105 * iget_set - initialize a inode with iget_args
106 *
107 * @sb: the superblock of current hmdfs instance
108 * @data: struct iget_args
109 */
iget_set(struct inode * inode,void * data)110 static int iget_set(struct inode *inode, void *data)
111 {
112 struct hmdfs_inode_info *hii = hmdfs_i(inode);
113 struct iget_args *ia = (struct iget_args *)data;
114
115 inode->i_ino = ia->ino.ino_output;
116 inode_inc_iversion(inode);
117
118 hii->conn = ia->peer;
119 hii->remote_ino = ia->remote_ino;
120 hii->lower_inode = ia->lo_i;
121
122 return 0;
123 }
124
make_ino_raw_dev_local(uint64_t lo_ino)125 static uint64_t make_ino_raw_dev_local(uint64_t lo_ino)
126 {
127 if (!(lo_ino >> BIT_WIDE_INO_RAW))
128 return lo_ino;
129
130 return lo_ino * GOLDEN_RATIO_64 >> BIT_WIDE_INO_RAW;
131 }
132
make_ino_raw_dev_remote(uint64_t remote_ino)133 static uint64_t make_ino_raw_dev_remote(uint64_t remote_ino)
134 {
135 return hash_long(remote_ino, BIT_WIDE_INO_RAW);
136 }
137
138 /**
139 * hmdfs_iget5_locked_merge - obtain an inode for the merge-view
140 *
141 * @sb: superblock of current instance
142 * @fst_lo_i: the lower inode of it's first comrade
143 *
144 * Simply replace the lower's domain for a new ino.
145 */
hmdfs_iget5_locked_merge(struct super_block * sb,struct dentry * fst_lo_d)146 struct inode *hmdfs_iget5_locked_merge(struct super_block *sb,
147 struct dentry *fst_lo_d)
148 {
149 struct iget_args ia = {
150 .lo_i = d_inode(fst_lo_d),
151 .peer = NULL,
152 .remote_ino = 0,
153 .ino.ino_output = 0,
154 };
155
156 if (unlikely(!d_inode(fst_lo_d))) {
157 hmdfs_err("Received a invalid lower inode");
158 return NULL;
159 }
160
161 ia.ino.ino_raw = d_inode(fst_lo_d)->i_ino;
162 ia.ino.dev_id = hmdfs_d(fst_lo_d)->device_id;
163 ia.ino.domain = DOMAIN_MERGE_VIEW;
164 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
165 }
166
167 /**
168 * hmdfs_iget5_locked_local - obtain an inode for the local-dev-view
169 *
170 * @sb: superblock of current instance
171 * @lo_i: the lower inode from local filesystem
172 *
173 * Hashing local inode's ino to generate our ino. We continue to compare the
174 * address of the lower_inode for uniqueness when collisions occurred.
175 */
hmdfs_iget5_locked_local(struct super_block * sb,struct inode * lo_i)176 struct inode *hmdfs_iget5_locked_local(struct super_block *sb,
177 struct inode *lo_i)
178 {
179 struct iget_args ia = {
180 .lo_i = lo_i,
181 .peer = NULL,
182 .remote_ino = 0,
183 .ino.ino_output = 0,
184 };
185
186 if (unlikely(!lo_i)) {
187 hmdfs_err("Received a invalid lower inode");
188 return NULL;
189 }
190 ia.ino.ino_raw = make_ino_raw_dev_local(lo_i->i_ino);
191 ia.ino.dev_id = 0;
192 ia.ino.domain = DOMAIN_DEVICE_LOCAL;
193 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
194 }
195
196 /**
197 * hmdfs_iget5_locked_remote - obtain an inode for the remote-dev-view
198 *
199 * @sb: superblock of current instance
200 * @peer: corresponding device node
201 * @remote_ino: remote inode's ino
202 *
203 * Hash remote ino for ino's 32bit~1bit.
204 *
205 * Note that currenly implementation assume the each remote inode has unique
206 * ino. Thus the combination of the peer's unique dev_id and the remote_ino
207 * is enough to determine a unique remote inode.
208 */
hmdfs_iget5_locked_remote(struct super_block * sb,struct hmdfs_peer * peer,uint64_t remote_ino)209 struct inode *hmdfs_iget5_locked_remote(struct super_block *sb,
210 struct hmdfs_peer *peer,
211 uint64_t remote_ino)
212 {
213 struct iget_args ia = {
214 .lo_i = NULL,
215 .peer = peer,
216 .remote_ino = remote_ino,
217 .ino.ino_output = 0,
218 };
219
220 if (unlikely(!peer)) {
221 hmdfs_err("Received a invalid peer");
222 return NULL;
223 }
224
225 ia.ino.ino_raw = make_ino_raw_dev_remote(remote_ino);
226 ia.ino.dev_id = peer->device_id;
227 ia.ino.domain = DOMAIN_DEVICE_REMOTE;
228 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
229 }
230
hmdfs_iget_locked_root(struct super_block * sb,uint64_t root_ino,struct inode * lo_i,struct hmdfs_peer * peer)231 struct inode *hmdfs_iget_locked_root(struct super_block *sb, uint64_t root_ino,
232 struct inode *lo_i,
233 struct hmdfs_peer *peer)
234 {
235 struct iget_args ia = {
236 .lo_i = lo_i,
237 .peer = peer,
238 .remote_ino = 0,
239 .ino.ino_raw = root_ino,
240 .ino.dev_id = peer ? peer->device_id : 0,
241 .ino.domain = DOMAIN_ROOT,
242 };
243
244 if (unlikely(root_ino < 0 || root_ino >= HMDFS_ROOT_INVALID)) {
245 hmdfs_err("Root %llu is invalid", root_ino);
246 return NULL;
247 }
248 if (unlikely(root_ino == HMDFS_ROOT_DEV_REMOTE && !peer)) {
249 hmdfs_err("Root %llu received a invalid peer", root_ino);
250 return NULL;
251 }
252
253 return iget5_locked(sb, ia.ino.ino_output, iget_test, iget_set, &ia);
254 }
255