]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - fs/aufs/dcsub.c
UBUNTU: SAUCE: Import aufs driver
[mirror_ubuntu-artful-kernel.git] / fs / aufs / dcsub.c
1 /*
2 * Copyright (C) 2005-2017 Junjiro R. Okajima
3 *
4 * This program, aufs is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * sub-routines for dentry cache
20 */
21
22 #include "aufs.h"
23
24 static void au_dpage_free(struct au_dpage *dpage)
25 {
26 int i;
27 struct dentry **p;
28
29 p = dpage->dentries;
30 for (i = 0; i < dpage->ndentry; i++)
31 dput(*p++);
32 free_page((unsigned long)dpage->dentries);
33 }
34
35 int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
36 {
37 int err;
38 void *p;
39
40 err = -ENOMEM;
41 dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
42 if (unlikely(!dpages->dpages))
43 goto out;
44
45 p = (void *)__get_free_page(gfp);
46 if (unlikely(!p))
47 goto out_dpages;
48
49 dpages->dpages[0].ndentry = 0;
50 dpages->dpages[0].dentries = p;
51 dpages->ndpage = 1;
52 return 0; /* success */
53
54 out_dpages:
55 kfree(dpages->dpages);
56 out:
57 return err;
58 }
59
60 void au_dpages_free(struct au_dcsub_pages *dpages)
61 {
62 int i;
63 struct au_dpage *p;
64
65 p = dpages->dpages;
66 for (i = 0; i < dpages->ndpage; i++)
67 au_dpage_free(p++);
68 kfree(dpages->dpages);
69 }
70
71 static int au_dpages_append(struct au_dcsub_pages *dpages,
72 struct dentry *dentry, gfp_t gfp)
73 {
74 int err, sz;
75 struct au_dpage *dpage;
76 void *p;
77
78 dpage = dpages->dpages + dpages->ndpage - 1;
79 sz = PAGE_SIZE / sizeof(dentry);
80 if (unlikely(dpage->ndentry >= sz)) {
81 AuLabel(new dpage);
82 err = -ENOMEM;
83 sz = dpages->ndpage * sizeof(*dpages->dpages);
84 p = au_kzrealloc(dpages->dpages, sz,
85 sz + sizeof(*dpages->dpages), gfp,
86 /*may_shrink*/0);
87 if (unlikely(!p))
88 goto out;
89
90 dpages->dpages = p;
91 dpage = dpages->dpages + dpages->ndpage;
92 p = (void *)__get_free_page(gfp);
93 if (unlikely(!p))
94 goto out;
95
96 dpage->ndentry = 0;
97 dpage->dentries = p;
98 dpages->ndpage++;
99 }
100
101 AuDebugOn(au_dcount(dentry) <= 0);
102 dpage->dentries[dpage->ndentry++] = dget_dlock(dentry);
103 return 0; /* success */
104
105 out:
106 return err;
107 }
108
109 /* todo: BAD approach */
110 /* copied from linux/fs/dcache.c */
111 enum d_walk_ret {
112 D_WALK_CONTINUE,
113 D_WALK_QUIT,
114 D_WALK_NORETRY,
115 D_WALK_SKIP,
116 };
117
118 extern void d_walk(struct dentry *parent, void *data,
119 enum d_walk_ret (*enter)(void *, struct dentry *),
120 void (*finish)(void *));
121
122 struct ac_dpages_arg {
123 int err;
124 struct au_dcsub_pages *dpages;
125 struct super_block *sb;
126 au_dpages_test test;
127 void *arg;
128 };
129
130 static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry)
131 {
132 enum d_walk_ret ret;
133 struct ac_dpages_arg *arg = _arg;
134
135 ret = D_WALK_CONTINUE;
136 if (dentry->d_sb == arg->sb
137 && !IS_ROOT(dentry)
138 && au_dcount(dentry) > 0
139 && au_di(dentry)
140 && (!arg->test || arg->test(dentry, arg->arg))) {
141 arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC);
142 if (unlikely(arg->err))
143 ret = D_WALK_QUIT;
144 }
145
146 return ret;
147 }
148
149 int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
150 au_dpages_test test, void *arg)
151 {
152 struct ac_dpages_arg args = {
153 .err = 0,
154 .dpages = dpages,
155 .sb = root->d_sb,
156 .test = test,
157 .arg = arg
158 };
159
160 d_walk(root, &args, au_call_dpages_append, NULL);
161
162 return args.err;
163 }
164
165 int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
166 int do_include, au_dpages_test test, void *arg)
167 {
168 int err;
169
170 err = 0;
171 write_seqlock(&rename_lock);
172 spin_lock(&dentry->d_lock);
173 if (do_include
174 && au_dcount(dentry) > 0
175 && (!test || test(dentry, arg)))
176 err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
177 spin_unlock(&dentry->d_lock);
178 if (unlikely(err))
179 goto out;
180
181 /*
182 * RCU for vfsmount is unnecessary since this is a traverse in a single
183 * mount
184 */
185 while (!IS_ROOT(dentry)) {
186 dentry = dentry->d_parent; /* rename_lock is locked */
187 spin_lock(&dentry->d_lock);
188 if (au_dcount(dentry) > 0
189 && (!test || test(dentry, arg)))
190 err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
191 spin_unlock(&dentry->d_lock);
192 if (unlikely(err))
193 break;
194 }
195
196 out:
197 write_sequnlock(&rename_lock);
198 return err;
199 }
200
201 static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg)
202 {
203 return au_di(dentry) && dentry->d_sb == arg;
204 }
205
206 int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages,
207 struct dentry *dentry, int do_include)
208 {
209 return au_dcsub_pages_rev(dpages, dentry, do_include,
210 au_dcsub_dpages_aufs, dentry->d_sb);
211 }
212
213 int au_test_subdir(struct dentry *d1, struct dentry *d2)
214 {
215 struct path path[2] = {
216 {
217 .dentry = d1
218 },
219 {
220 .dentry = d2
221 }
222 };
223
224 return path_is_under(path + 0, path + 1);
225 }