]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - fs/aufs/dcsub.c
UBUNTU: SAUCE: aufs: bugfix, for v4.10, copy-up on XFS branch
[mirror_ubuntu-zesty-kernel.git] / fs / aufs / dcsub.c
CommitLineData
e14748e8
SF
1/*
2 * Copyright (C) 2005-2016 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
24static 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 au_delayed_free_page((unsigned long)dpage->dentries);
33}
34
35int 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
54out_dpages:
55 au_delayed_kfree(dpages->dpages);
56out:
57 return err;
58}
59
60void 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 au_delayed_kfree(dpages->dpages);
69}
70
71static 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
105out:
106 return err;
107}
108
109/* todo: BAD approach */
110/* copied from linux/fs/dcache.c */
111enum d_walk_ret {
112 D_WALK_CONTINUE,
113 D_WALK_QUIT,
114 D_WALK_NORETRY,
115 D_WALK_SKIP,
116};
117
118extern void d_walk(struct dentry *parent, void *data,
119 enum d_walk_ret (*enter)(void *, struct dentry *),
120 void (*finish)(void *));
121
122struct 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
130static 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
149int 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
165int 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
196out:
197 write_sequnlock(&rename_lock);
198 return err;
199}
200
201static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg)
202{
203 return au_di(dentry) && dentry->d_sb == arg;
204}
205
206int 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
213int 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}