]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - fs/aufs/dynop.c
x86/speculation/mds: Add sysfs reporting for MDS
[mirror_ubuntu-bionic-kernel.git] / fs / aufs / dynop.c
1 /*
2 * Copyright (C) 2010-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 * dynamically customizable operations for regular files
20 */
21
22 #include "aufs.h"
23
24 #define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop)
25
26 /*
27 * How large will these lists be?
28 * Usually just a few elements, 20-30 at most for each, I guess.
29 */
30 static struct hlist_bl_head dynop[AuDyLast];
31
32 static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl,
33 const void *h_op)
34 {
35 struct au_dykey *key, *tmp;
36 struct hlist_bl_node *pos;
37
38 key = NULL;
39 hlist_bl_lock(hbl);
40 hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode)
41 if (tmp->dk_op.dy_hop == h_op) {
42 key = tmp;
43 kref_get(&key->dk_kref);
44 break;
45 }
46 hlist_bl_unlock(hbl);
47
48 return key;
49 }
50
51 static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key)
52 {
53 struct au_dykey **k, *found;
54 const void *h_op = key->dk_op.dy_hop;
55 int i;
56
57 found = NULL;
58 k = br->br_dykey;
59 for (i = 0; i < AuBrDynOp; i++)
60 if (k[i]) {
61 if (k[i]->dk_op.dy_hop == h_op) {
62 found = k[i];
63 break;
64 }
65 } else
66 break;
67 if (!found) {
68 spin_lock(&br->br_dykey_lock);
69 for (; i < AuBrDynOp; i++)
70 if (k[i]) {
71 if (k[i]->dk_op.dy_hop == h_op) {
72 found = k[i];
73 break;
74 }
75 } else {
76 k[i] = key;
77 break;
78 }
79 spin_unlock(&br->br_dykey_lock);
80 BUG_ON(i == AuBrDynOp); /* expand the array */
81 }
82
83 return found;
84 }
85
86 /* kref_get() if @key is already added */
87 static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key)
88 {
89 struct au_dykey *tmp, *found;
90 struct hlist_bl_node *pos;
91 const void *h_op = key->dk_op.dy_hop;
92
93 found = NULL;
94 hlist_bl_lock(hbl);
95 hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode)
96 if (tmp->dk_op.dy_hop == h_op) {
97 kref_get(&tmp->dk_kref);
98 found = tmp;
99 break;
100 }
101 if (!found)
102 hlist_bl_add_head(&key->dk_hnode, hbl);
103 hlist_bl_unlock(hbl);
104
105 if (!found)
106 DyPrSym(key);
107 return found;
108 }
109
110 static void dy_free_rcu(struct rcu_head *rcu)
111 {
112 struct au_dykey *key;
113
114 key = container_of(rcu, struct au_dykey, dk_rcu);
115 DyPrSym(key);
116 kfree(key);
117 }
118
119 static void dy_free(struct kref *kref)
120 {
121 struct au_dykey *key;
122 struct hlist_bl_head *hbl;
123
124 key = container_of(kref, struct au_dykey, dk_kref);
125 hbl = dynop + key->dk_op.dy_type;
126 au_hbl_del(&key->dk_hnode, hbl);
127 call_rcu(&key->dk_rcu, dy_free_rcu);
128 }
129
130 void au_dy_put(struct au_dykey *key)
131 {
132 kref_put(&key->dk_kref, dy_free);
133 }
134
135 /* ---------------------------------------------------------------------- */
136
137 #define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *))
138
139 #ifdef CONFIG_AUFS_DEBUG
140 #define DyDbgDeclare(cnt) unsigned int cnt = 0
141 #define DyDbgInc(cnt) do { cnt++; } while (0)
142 #else
143 #define DyDbgDeclare(cnt) do {} while (0)
144 #define DyDbgInc(cnt) do {} while (0)
145 #endif
146
147 #define DySet(func, dst, src, h_op, h_sb) do { \
148 DyDbgInc(cnt); \
149 if (h_op->func) { \
150 if (src.func) \
151 dst.func = src.func; \
152 else \
153 AuDbg("%s %s\n", au_sbtype(h_sb), #func); \
154 } \
155 } while (0)
156
157 #define DySetForce(func, dst, src) do { \
158 AuDebugOn(!src.func); \
159 DyDbgInc(cnt); \
160 dst.func = src.func; \
161 } while (0)
162
163 #define DySetAop(func) \
164 DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb)
165 #define DySetAopForce(func) \
166 DySetForce(func, dyaop->da_op, aufs_aop)
167
168 static void dy_aop(struct au_dykey *key, const void *h_op,
169 struct super_block *h_sb __maybe_unused)
170 {
171 struct au_dyaop *dyaop = (void *)key;
172 const struct address_space_operations *h_aop = h_op;
173 DyDbgDeclare(cnt);
174
175 AuDbg("%s\n", au_sbtype(h_sb));
176
177 DySetAop(writepage);
178 DySetAopForce(readpage); /* force */
179 DySetAop(writepages);
180 DySetAop(set_page_dirty);
181 DySetAop(readpages);
182 DySetAop(write_begin);
183 DySetAop(write_end);
184 DySetAop(bmap);
185 DySetAop(invalidatepage);
186 DySetAop(releasepage);
187 DySetAop(freepage);
188 /* this one will be changed according to an aufs mount option */
189 DySetAop(direct_IO);
190 DySetAop(migratepage);
191 DySetAop(isolate_page);
192 DySetAop(putback_page);
193 DySetAop(launder_page);
194 DySetAop(is_partially_uptodate);
195 DySetAop(is_dirty_writeback);
196 DySetAop(error_remove_page);
197 DySetAop(swap_activate);
198 DySetAop(swap_deactivate);
199
200 DyDbgSize(cnt, *h_aop);
201 }
202
203 /* ---------------------------------------------------------------------- */
204
205 static void dy_bug(struct kref *kref)
206 {
207 BUG();
208 }
209
210 static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
211 {
212 struct au_dykey *key, *old;
213 struct hlist_bl_head *hbl;
214 struct op {
215 unsigned int sz;
216 void (*set)(struct au_dykey *key, const void *h_op,
217 struct super_block *h_sb __maybe_unused);
218 };
219 static const struct op a[] = {
220 [AuDy_AOP] = {
221 .sz = sizeof(struct au_dyaop),
222 .set = dy_aop
223 }
224 };
225 const struct op *p;
226
227 hbl = dynop + op->dy_type;
228 key = dy_gfind_get(hbl, op->dy_hop);
229 if (key)
230 goto out_add; /* success */
231
232 p = a + op->dy_type;
233 key = kzalloc(p->sz, GFP_NOFS);
234 if (unlikely(!key)) {
235 key = ERR_PTR(-ENOMEM);
236 goto out;
237 }
238
239 key->dk_op.dy_hop = op->dy_hop;
240 kref_init(&key->dk_kref);
241 p->set(key, op->dy_hop, au_br_sb(br));
242 old = dy_gadd(hbl, key);
243 if (old) {
244 kfree(key);
245 key = old;
246 }
247
248 out_add:
249 old = dy_bradd(br, key);
250 if (old)
251 /* its ref-count should never be zero here */
252 kref_put(&key->dk_kref, dy_bug);
253 out:
254 return key;
255 }
256
257 /* ---------------------------------------------------------------------- */
258 /*
259 * Aufs prohibits O_DIRECT by defaut even if the branch supports it.
260 * This behaviour is necessary to return an error from open(O_DIRECT) instead
261 * of the succeeding I/O. The dio mount option enables O_DIRECT and makes
262 * open(O_DIRECT) always succeed, but the succeeding I/O may return an error.
263 * See the aufs manual in detail.
264 */
265 static void dy_adx(struct au_dyaop *dyaop, int do_dx)
266 {
267 if (!do_dx)
268 dyaop->da_op.direct_IO = NULL;
269 else
270 dyaop->da_op.direct_IO = aufs_aop.direct_IO;
271 }
272
273 static struct au_dyaop *dy_aget(struct au_branch *br,
274 const struct address_space_operations *h_aop,
275 int do_dx)
276 {
277 struct au_dyaop *dyaop;
278 struct au_dynop op;
279
280 op.dy_type = AuDy_AOP;
281 op.dy_haop = h_aop;
282 dyaop = (void *)dy_get(&op, br);
283 if (IS_ERR(dyaop))
284 goto out;
285 dy_adx(dyaop, do_dx);
286
287 out:
288 return dyaop;
289 }
290
291 int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex,
292 struct inode *h_inode)
293 {
294 int err, do_dx;
295 struct super_block *sb;
296 struct au_branch *br;
297 struct au_dyaop *dyaop;
298
299 AuDebugOn(!S_ISREG(h_inode->i_mode));
300 IiMustWriteLock(inode);
301
302 sb = inode->i_sb;
303 br = au_sbr(sb, bindex);
304 do_dx = !!au_opt_test(au_mntflags(sb), DIO);
305 dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx);
306 err = PTR_ERR(dyaop);
307 if (IS_ERR(dyaop))
308 /* unnecessary to call dy_fput() */
309 goto out;
310
311 err = 0;
312 inode->i_mapping->a_ops = &dyaop->da_op;
313
314 out:
315 return err;
316 }
317
318 /*
319 * Is it safe to replace a_ops during the inode/file is in operation?
320 * Yes, I hope so.
321 */
322 int au_dy_irefresh(struct inode *inode)
323 {
324 int err;
325 aufs_bindex_t btop;
326 struct inode *h_inode;
327
328 err = 0;
329 if (S_ISREG(inode->i_mode)) {
330 btop = au_ibtop(inode);
331 h_inode = au_h_iptr(inode, btop);
332 err = au_dy_iaop(inode, btop, h_inode);
333 }
334 return err;
335 }
336
337 void au_dy_arefresh(int do_dx)
338 {
339 struct hlist_bl_head *hbl;
340 struct hlist_bl_node *pos;
341 struct au_dykey *key;
342
343 hbl = dynop + AuDy_AOP;
344 hlist_bl_lock(hbl);
345 hlist_bl_for_each_entry(key, pos, hbl, dk_hnode)
346 dy_adx((void *)key, do_dx);
347 hlist_bl_unlock(hbl);
348 }
349
350 /* ---------------------------------------------------------------------- */
351
352 void __init au_dy_init(void)
353 {
354 int i;
355
356 /* make sure that 'struct au_dykey *' can be any type */
357 BUILD_BUG_ON(offsetof(struct au_dyaop, da_key));
358
359 for (i = 0; i < AuDyLast; i++)
360 INIT_HLIST_BL_HEAD(dynop + i);
361 }
362
363 void au_dy_fin(void)
364 {
365 int i;
366
367 for (i = 0; i < AuDyLast; i++)
368 WARN_ON(!hlist_bl_empty(dynop + i));
369 }