summaryrefslogtreecommitdiff
path: root/fs/aufs/module.h
blob: 6f968ba41005826a3a344a909daff1c551a112cd (plain)
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
/*
 * Copyright (C) 2005-2016 Junjiro R. Okajima
 */

/*
 * module initialization and module-global
 */

#ifndef __AUFS_MODULE_H__
#define __AUFS_MODULE_H__

#ifdef __KERNEL__

#include <linux/slab.h>
#include "debug.h"

struct path;
struct seq_file;

/* module parameters */
extern int sysaufs_brs;
extern bool au_userns;

/* ---------------------------------------------------------------------- */

extern int au_dir_roflags;

void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink);
void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
		   int may_shrink);

static inline int au_kmidx_sub(size_t sz, size_t new_sz)
{
#ifndef CONFIG_SLOB
	return kmalloc_index(sz) - kmalloc_index(new_sz);
#else
	return -1; /* SLOB is untested */
#endif
}

int au_seq_path(struct seq_file *seq, struct path *path);

#ifdef CONFIG_PROC_FS
/* procfs.c */
int __init au_procfs_init(void);
void au_procfs_fin(void);
#else
AuStubInt0(au_procfs_init, void);
AuStubVoid(au_procfs_fin, void);
#endif

/* ---------------------------------------------------------------------- */

/* kmem cache and delayed free */
enum {
	AuCache_DINFO,
	AuCache_ICNTNR,
	AuCache_FINFO,
	AuCache_VDIR,
	AuCache_DEHSTR,
	AuCache_HNOTIFY, /* must be last */
	AuCache_Last
};

enum {
	AU_DFREE_KFREE,
	AU_DFREE_FREE_PAGE,
	AU_DFREE_Last
};

struct au_cache {
	struct kmem_cache	*cache;
	struct llist_head	llist;	/* delayed free */
};

/*
 * in order to reduce the cost of the internal timer, consolidate all the
 * delayed free works into a single delayed_work.
 */
struct au_dfree {
	struct au_cache		cache[AuCache_Last];
	struct llist_head	llist[AU_DFREE_Last];
	struct delayed_work	dwork;
};

extern struct au_dfree au_dfree;

#define AuCacheFlags		(SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD)
#define AuCache(type)		KMEM_CACHE(type, AuCacheFlags)
#define AuCacheCtor(type, ctor)	\
	kmem_cache_create(#type, sizeof(struct type), \
			  __alignof__(struct type), AuCacheFlags, ctor)

#define AU_DFREE_DELAY		msecs_to_jiffies(10)
#define AU_DFREE_BODY(lnode, llist) do {				\
		if (llist_add(lnode, llist))				\
			schedule_delayed_work(&au_dfree.dwork,		\
					      AU_DFREE_DELAY);		\
	} while (0)
#define AU_CACHE_DFREE_FUNC(name, idx, lnode)				\
	void au_cache_dfree_##name(struct au_##name *p)			\
	{								\
		struct au_cache *cp = au_dfree.cache + AuCache_##idx;	\
		AU_DFREE_BODY(&p->lnode, &cp->llist);			\
	}

#define AuCacheFuncs(name, index) \
static inline struct au_##name *au_cache_alloc_##name(void) \
{ return kmem_cache_alloc(au_dfree.cache[AuCache_##index].cache, GFP_NOFS); } \
static inline void au_cache_free_##name(struct au_##name *p) \
{ kmem_cache_free(au_dfree.cache[AuCache_##index].cache, p); } \
void au_cache_dfree_##name(struct au_##name *p)

AuCacheFuncs(dinfo, DINFO);
AuCacheFuncs(icntnr, ICNTNR);
AuCacheFuncs(finfo, FINFO);
AuCacheFuncs(vdir, VDIR);
AuCacheFuncs(vdir_dehstr, DEHSTR);
#ifdef CONFIG_AUFS_HNOTIFY
AuCacheFuncs(hnotify, HNOTIFY);
#endif

static inline void au_delayed_kfree(const void *p)
{
	AuDebugOn(!p);
	AuDebugOn(ksize(p) < sizeof(struct llist_node));

	AU_DFREE_BODY((void *)p, au_dfree.llist + AU_DFREE_KFREE);
}

/* cast only */
static inline void au_free_page(void *p)
{
	free_page((unsigned long)p);
}

static inline void au_delayed_free_page(unsigned long addr)
{
	AU_DFREE_BODY((void *)addr, au_dfree.llist + AU_DFREE_FREE_PAGE);
}

#endif /* __KERNEL__ */
#endif /* __AUFS_MODULE_H__ */