xref: /kernel/linux/linux-5.10/fs/hmdfs/dentry.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * fs/hmdfs/dentry.c
4 *
5 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
6 */
7
8#include <linux/ctype.h>
9#include <linux/slab.h>
10
11#include "comm/connection.h"
12#include "hmdfs_dentryfile.h"
13#include "hmdfs_device_view.h"
14#include "hmdfs_merge_view.h"
15
16extern struct kmem_cache *hmdfs_dentry_cachep;
17
18void hmdfs_set_time(struct dentry *dentry, unsigned long time)
19{
20	struct hmdfs_dentry_info *d_info = dentry->d_fsdata;
21
22	if (d_info)
23		d_info->time = time;
24}
25
26unsigned long hmdfs_get_time(struct dentry *dentry)
27{
28	struct hmdfs_dentry_info *d_info = dentry->d_fsdata;
29
30	if (d_info)
31		return (unsigned long)d_info->time;
32	return 0;
33}
34
35static int hmdfs_d_remote_revalidate(struct hmdfs_peer *conn,
36				     struct dentry *target,
37				     struct dentry *parent)
38{
39	unsigned int timeout = hmdfs_sb(target->d_sb)->dcache_timeout;
40	unsigned long dentry_time = hmdfs_get_time(target);
41	struct clearcache_item *item;
42
43	item = hmdfs_find_cache_item(conn->device_id, parent);
44	if (!item)
45		return 0;
46	kref_put(&item->ref, release_cache_item);
47
48	if (cache_item_revalidate(READ_ONCE(conn->conn_time),
49				  dentry_time, timeout))
50		return 1;
51
52	return 0;
53}
54
55static inline void lock_for_dname_cmp(struct dentry *dentry,
56				      struct dentry *lower_dentry)
57{
58	if (dentry < lower_dentry) {
59		spin_lock(&dentry->d_lock);
60		spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
61	} else {
62		spin_lock(&lower_dentry->d_lock);
63		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
64	}
65}
66
67static inline void unlock_for_dname_cmp(struct dentry *dentry,
68					struct dentry *lower_dentry)
69{
70	spin_unlock(&dentry->d_lock);
71	spin_unlock(&lower_dentry->d_lock);
72}
73
74static int hmdfs_dev_d_revalidate(struct dentry *direntry, unsigned int flags)
75{
76	struct inode *dinode = NULL;
77	struct hmdfs_inode_info *info = NULL;
78
79	spin_lock(&direntry->d_lock);
80	if (IS_ROOT(direntry)) {
81		spin_unlock(&direntry->d_lock);
82		return 1;
83	}
84	spin_unlock(&direntry->d_lock);
85
86	dinode = d_inode(direntry);
87	if (!dinode)
88		return 0;
89
90	info = hmdfs_i(dinode);
91	if (info->inode_type == HMDFS_LAYER_SECOND_LOCAL ||
92	    info->inode_type == HMDFS_LAYER_FIRST_DEVICE) {
93		return 1;
94	}
95	if (info->conn && info->conn->status == NODE_STAT_ONLINE)
96		return 1;
97
98	return 0;
99}
100
101static int hmdfs_d_revalidate(struct dentry *direntry, unsigned int flags)
102{
103	struct inode *dinode = NULL;
104	struct hmdfs_inode_info *info = NULL;
105	struct path lower_path, parent_lower_path;
106	struct dentry *parent_dentry = NULL;
107	struct dentry *parent_lower_dentry = NULL;
108	struct dentry *lower_cur_parent_dentry = NULL;
109	struct dentry *lower_dentry = NULL;
110	int ret;
111
112	if (flags & LOOKUP_RCU)
113		return -ECHILD;
114
115	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET | LOOKUP_REVAL))
116		return 0;
117
118	dinode = d_inode(direntry);
119	if (!dinode)
120		return 0;
121
122	/* remote dentry timeout */
123	info = hmdfs_i(dinode);
124	parent_dentry = dget_parent(direntry);
125	if (info->conn) {
126		ret = hmdfs_d_remote_revalidate(info->conn, direntry,
127						parent_dentry);
128		dput(parent_dentry);
129		return ret;
130	}
131
132	hmdfs_get_lower_path(direntry, &lower_path);
133	lower_dentry = lower_path.dentry;
134	lower_cur_parent_dentry = dget_parent(lower_dentry);
135	hmdfs_get_lower_path(parent_dentry, &parent_lower_path);
136	parent_lower_dentry = parent_lower_path.dentry;
137	if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
138		ret = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
139		if (ret == 0)
140			goto out;
141	}
142
143	spin_lock(&lower_dentry->d_lock);
144	if (d_unhashed(lower_dentry)) {
145		spin_unlock(&lower_dentry->d_lock);
146		ret = 0;
147		goto out;
148	}
149	spin_unlock(&lower_dentry->d_lock);
150
151	if (parent_lower_dentry != lower_cur_parent_dentry) {
152		ret = 0;
153		goto out;
154	}
155
156	ret = 1;
157	lock_for_dname_cmp(direntry, lower_dentry);
158	if (!qstr_case_eq(&direntry->d_name, &lower_dentry->d_name))
159		ret = 0;
160	unlock_for_dname_cmp(direntry, lower_dentry);
161
162out:
163	hmdfs_put_lower_path(&parent_lower_path);
164	dput(lower_cur_parent_dentry);
165	hmdfs_put_lower_path(&lower_path);
166	dput(parent_dentry);
167	return ret;
168}
169
170static void hmdfs_dev_d_release(struct dentry *dentry)
171{
172	struct clearcache_item *item;
173	if (!dentry || !dentry->d_fsdata)
174		return;
175
176	switch (hmdfs_d(dentry)->dentry_type) {
177	case HMDFS_LAYER_SECOND_LOCAL:
178		hmdfs_clear_cache_dents(dentry, false);
179		hmdfs_drop_remote_cache_dents(dentry);
180		path_put(&(hmdfs_d(dentry)->lower_path));
181		break;
182	case HMDFS_LAYER_ZERO:
183		hmdfs_put_reset_lower_path(dentry);
184		break;
185	case HMDFS_LAYER_FIRST_DEVICE:
186		break;
187	case HMDFS_LAYER_SECOND_REMOTE:
188		hmdfs_clear_cache_dents(dentry, false);
189		break;
190	case HMDFS_LAYER_SECOND_CLOUD:
191		item = hmdfs_find_cache_item(CLOUD_DEVICE, dentry);
192		if (item) {
193			/* cloud dentryfile didn't link to
194			   'struct cache_file_node', so close file here.
195			 */
196			filp_close(item->filp, NULL);
197			kref_put(&item->ref, release_cache_item);
198		}
199		hmdfs_clear_cache_dents(dentry, false);
200		break;
201	default:
202		hmdfs_err("Unexpected dentry type %d",
203			  hmdfs_d(dentry)->dentry_type);
204		return;
205	}
206
207	kmem_cache_free(hmdfs_dentry_cachep, dentry->d_fsdata);
208	dentry->d_fsdata = NULL;
209}
210
211static void hmdfs_d_release(struct dentry *dentry)
212{
213	if (!dentry || !dentry->d_fsdata)
214		return;
215
216	hmdfs_clear_cache_dents(dentry, false);
217	hmdfs_drop_remote_cache_dents(dentry);
218	hmdfs_put_reset_lower_path(dentry);
219	kmem_cache_free(hmdfs_dentry_cachep, dentry->d_fsdata);
220	dentry->d_fsdata = NULL;
221}
222
223static int hmdfs_cmp_ci(const struct dentry *dentry, unsigned int len,
224			const char *str, const struct qstr *name)
225{
226	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
227
228	if (name->len != len)
229		return 1;
230
231	if (!sbi->s_case_sensitive) {
232		if (str_n_case_eq(name->name, str, len))
233			return 0;
234	} else {
235		if (!strncmp(name->name, str, len))
236			return 0;
237	}
238	return 1;
239}
240
241static int hmdfs_hash_ci(const struct dentry *dentry, struct qstr *qstr)
242{
243	const unsigned char *name = qstr->name;
244	unsigned int len = qstr->len;
245	unsigned long hash;
246	struct hmdfs_sb_info *sbi = hmdfs_sb(dentry->d_sb);
247
248	if (sbi->s_case_sensitive)
249		return 0;
250
251	hash = init_name_hash(dentry);
252	while (len--)
253		hash = partial_name_hash(tolower(*name++), hash);
254	qstr->hash = end_name_hash(hash);
255	return 0;
256}
257
258void clear_comrades_locked(struct list_head *comrade_list)
259{
260	struct hmdfs_dentry_comrade *cc, *nc;
261
262	WARN_ON(!comrade_list);
263	list_for_each_entry_safe(cc, nc, comrade_list, list) {
264		dput(cc->lo_d);
265		kfree(cc);
266	}
267	INIT_LIST_HEAD(comrade_list);
268}
269
270void clear_comrades(struct dentry *dentry)
271{
272	struct hmdfs_dentry_info_merge *cdi = hmdfs_dm(dentry);
273
274	wait_event(cdi->wait_queue, !has_merge_lookup_work(cdi));
275	mutex_lock(&cdi->comrade_list_lock);
276	clear_comrades_locked(&cdi->comrade_list);
277	mutex_unlock(&cdi->comrade_list_lock);
278}
279
280/**
281 * d_revalidate_merge - revalidate a merge dentry
282 *
283 * Always return 0 to invalidate a dentry for fault-tolerance.
284 * The cost is acceptable for a overlay filesystem.
285 */
286static int d_revalidate_merge(struct dentry *direntry, unsigned int flags)
287{
288	struct hmdfs_dentry_info_merge *dim = hmdfs_dm(direntry);
289	struct hmdfs_dentry_comrade *comrade = NULL;
290	struct dentry *parent_dentry = NULL;
291	struct dentry *lower_cur_parent_dentry = NULL;
292	struct inode *dinode = NULL;
293	struct hmdfs_inode_info *info = NULL;
294	int ret = 1;
295
296	if (flags & LOOKUP_RCU) {
297		return -ECHILD;
298	}
299
300	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET | LOOKUP_REVAL)) {
301		return 0;
302	}
303
304	dinode = d_inode(direntry);
305	if (!dinode)
306		return 0;
307
308	info = hmdfs_i(dinode);
309	if (info->inode_type == HMDFS_LAYER_FIRST_MERGE_CLOUD)
310		return 1;
311
312	parent_dentry = dget_parent(direntry);
313        mutex_lock(&dim->comrade_list_lock);
314	list_for_each_entry(comrade, &(dim->comrade_list), list) {
315		lower_cur_parent_dentry = dget_parent(comrade->lo_d);
316		if ((comrade->lo_d->d_flags & DCACHE_OP_REVALIDATE)) {
317			ret = comrade->lo_d->d_op->d_revalidate(
318				comrade->lo_d, flags);
319			if (ret == 0) {
320				dput(lower_cur_parent_dentry);
321				goto out;
322			}
323		}
324		dput(lower_cur_parent_dentry);
325	}
326out:
327        mutex_unlock(&dim->comrade_list_lock);
328	dput(parent_dentry);
329	return ret;
330}
331
332static void d_release_merge(struct dentry *dentry)
333{
334	if (!dentry || !dentry->d_fsdata)
335		return;
336
337	clear_comrades(dentry);
338	kmem_cache_free(hmdfs_dentry_merge_cachep, dentry->d_fsdata);
339	dentry->d_fsdata = NULL;
340}
341
342const struct dentry_operations hmdfs_dops_merge = {
343	.d_revalidate = d_revalidate_merge,
344	.d_release = d_release_merge,
345};
346
347const struct dentry_operations hmdfs_dev_dops = {
348	.d_revalidate = hmdfs_dev_d_revalidate,
349	.d_release = hmdfs_dev_d_release,
350};
351
352const struct dentry_operations hmdfs_dops = {
353	.d_revalidate = hmdfs_d_revalidate,
354	.d_release = hmdfs_d_release,
355	.d_compare = hmdfs_cmp_ci,
356	.d_hash = hmdfs_hash_ci,
357};
358