dcsub.c 2.98 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2005-2019 Junjiro R. Okajima
 */

/*
 * sub-routines for dentry cache
 */

#include "aufs.h"

static void au_dpage_free(struct au_dpage *dpage)
{
	int i;
	struct dentry **p;

	p = dpage->dentries;
	for (i = 0; i < dpage->ndentry; i++)
		dput(*p++);
	free_page((unsigned long)dpage->dentries);
}

int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
{
	int err;
	void *p;

	err = -ENOMEM;
	dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
	if (unlikely(!dpages->dpages))
		goto out;

	p = (void *)__get_free_page(gfp);
	if (unlikely(!p))
		goto out_dpages;

	dpages->dpages[0].ndentry = 0;
	dpages->dpages[0].dentries = p;
	dpages->ndpage = 1;
	return 0; /* success */

out_dpages:
	au_kfree_try_rcu(dpages->dpages);
out:
	return err;
}

void au_dpages_free(struct au_dcsub_pages *dpages)
{
	int i;
	struct au_dpage *p;

	p = dpages->dpages;
	for (i = 0; i < dpages->ndpage; i++)
		au_dpage_free(p++);
	au_kfree_try_rcu(dpages->dpages);
}

static int au_dpages_append(struct au_dcsub_pages *dpages,
			    struct dentry *dentry, gfp_t gfp)
{
	int err, sz;
	struct au_dpage *dpage;
	void *p;

	dpage = dpages->dpages + dpages->ndpage - 1;
	sz = PAGE_SIZE / sizeof(dentry);
	if (unlikely(dpage->ndentry >= sz)) {
		AuLabel(new dpage);
		err = -ENOMEM;
		sz = dpages->ndpage * sizeof(*dpages->dpages);
		p = au_kzrealloc(dpages->dpages, sz,
				 sz + sizeof(*dpages->dpages), gfp,
				 /*may_shrink*/0);
		if (unlikely(!p))
			goto out;

		dpages->dpages = p;
		dpage = dpages->dpages + dpages->ndpage;
		p = (void *)__get_free_page(gfp);
		if (unlikely(!p))
			goto out;

		dpage->ndentry = 0;
		dpage->dentries = p;
		dpages->ndpage++;
	}

	AuDebugOn(au_dcount(dentry) <= 0);
	dpage->dentries[dpage->ndentry++] = dget_dlock(dentry);
	return 0; /* success */

out:
	return err;
}

int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
		       int do_include, au_dpages_test test, void *arg)
{
	int err;

	err = 0;
	write_seqlock(&rename_lock);
	spin_lock(&dentry->d_lock);
	if (do_include
	    && au_dcount(dentry) > 0
	    && (!test || test(dentry, arg)))
		err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
	spin_unlock(&dentry->d_lock);
	if (unlikely(err))
		goto out;

	/*
	 * RCU for vfsmount is unnecessary since this is a traverse in a single
	 * mount
	 */
	while (!IS_ROOT(dentry)) {
		dentry = dentry->d_parent; /* rename_lock is locked */
		spin_lock(&dentry->d_lock);
		if (au_dcount(dentry) > 0
		    && (!test || test(dentry, arg)))
			err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
		spin_unlock(&dentry->d_lock);
		if (unlikely(err))
			break;
	}

out:
	write_sequnlock(&rename_lock);
	return err;
}

static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg)
{
	return au_di(dentry) && dentry->d_sb == arg;
}

int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages,
			    struct dentry *dentry, int do_include)
{
	return au_dcsub_pages_rev(dpages, dentry, do_include,
				  au_dcsub_dpages_aufs, dentry->d_sb);
}