Skip to content

Commit 4835f74

Browse files
surenbaghdasaryanakpm00
authored andcommitted
alloc_tag: support for page allocation tag compression
Implement support for storing page allocation tag references directly in the page flags instead of page extensions. sysctl.vm.mem_profiling boot parameter it extended to provide a way for a user to request this mode. Enabling compression eliminates memory overhead caused by page_ext and results in better performance for page allocations. However this mode will not work if the number of available page flag bits is insufficient to address all kernel allocations. Such condition can happen during boot or when loading a module. If this condition is detected, memory allocation profiling gets disabled with an appropriate warning. By default compression mode is disabled. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Suren Baghdasaryan <[email protected]> Reviewed-by: Pasha Tatashin <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Borislav Petkov (AMD) <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Daniel Gomez <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Davidlohr Bueso <[email protected]> Cc: David Rientjes <[email protected]> Cc: Dennis Zhou <[email protected]> Cc: Johannes Weiner <[email protected]> Cc: John Hubbard <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Joonsoo Kim <[email protected]> Cc: Kalesh Singh <[email protected]> Cc: Kees Cook <[email protected]> Cc: Kent Overstreet <[email protected]> Cc: Liam R. Howlett <[email protected]> Cc: Luis Chamberlain <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Mike Rapoport (Microsoft) <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Paul E. McKenney <[email protected]> Cc: Petr Pavlu <[email protected]> Cc: Roman Gushchin <[email protected]> Cc: Sami Tolvanen <[email protected]> Cc: Sourav Panda <[email protected]> Cc: Steven Rostedt (Google) <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Thomas Huth <[email protected]> Cc: Uladzislau Rezki (Sony) <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: Xiongwei Song <[email protected]> Cc: Yu Zhao <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 42895a8 commit 4835f74

File tree

8 files changed

+290
-33
lines changed

8 files changed

+290
-33
lines changed

Documentation/mm/allocation-profiling.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ kconfig options:
1818
missing annotation
1919

2020
Boot parameter:
21-
sysctl.vm.mem_profiling=0|1|never
21+
sysctl.vm.mem_profiling={0|1|never}[,compressed]
2222

2323
When set to "never", memory allocation profiling overhead is minimized and it
2424
cannot be enabled at runtime (sysctl becomes read-only).
2525
When CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=y, default value is "1".
2626
When CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT=n, default value is "never".
27+
"compressed" optional parameter will try to store page tag references in a
28+
compact format, avoiding page extensions. This results in improved performance
29+
and memory consumption, however it might fail depending on system configuration.
30+
If compression fails, a warning is issued and memory allocation profiling gets
31+
disabled.
2732

2833
sysctl:
2934
/proc/sys/vm/mem_profiling

include/linux/alloc_tag.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,16 @@ struct alloc_tag {
3030
struct alloc_tag_counters __percpu *counters;
3131
} __aligned(8);
3232

33+
struct alloc_tag_kernel_section {
34+
struct alloc_tag *first_tag;
35+
unsigned long count;
36+
};
37+
3338
struct alloc_tag_module_section {
34-
unsigned long start_addr;
39+
union {
40+
unsigned long start_addr;
41+
struct alloc_tag *first_tag;
42+
};
3543
unsigned long end_addr;
3644
/* used size */
3745
unsigned long size;

include/linux/codetag.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ struct codetag_module;
1313
struct seq_buf;
1414
struct module;
1515

16+
#define CODETAG_SECTION_START_PREFIX "__start_"
17+
#define CODETAG_SECTION_STOP_PREFIX "__stop_"
18+
1619
/*
1720
* An instance of this structure is created in a special ELF section at every
1821
* code location being tagged. At runtime, the special section is treated as

include/linux/page-flags-layout.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,12 @@
111111
ZONES_WIDTH - LRU_GEN_WIDTH - SECTIONS_WIDTH - \
112112
NODES_WIDTH - KASAN_TAG_WIDTH - LAST_CPUPID_WIDTH)
113113

114+
#define NR_NON_PAGEFLAG_BITS (SECTIONS_WIDTH + NODES_WIDTH + ZONES_WIDTH + \
115+
LAST_CPUPID_SHIFT + KASAN_TAG_WIDTH + \
116+
LRU_GEN_WIDTH + LRU_REFS_WIDTH)
117+
118+
#define NR_UNUSED_PAGEFLAG_BITS (BITS_PER_LONG - \
119+
(NR_NON_PAGEFLAG_BITS + NR_PAGEFLAGS))
120+
114121
#endif
115122
#endif /* _LINUX_PAGE_FLAGS_LAYOUT */

include/linux/pgalloc_tag.h

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,118 @@
1111

1212
#include <linux/page_ext.h>
1313

14+
extern struct page_ext_operations page_alloc_tagging_ops;
15+
extern unsigned long alloc_tag_ref_mask;
16+
extern int alloc_tag_ref_offs;
17+
extern struct alloc_tag_kernel_section kernel_tags;
18+
19+
DECLARE_STATIC_KEY_FALSE(mem_profiling_compressed);
20+
21+
typedef u16 pgalloc_tag_idx;
22+
1423
union pgtag_ref_handle {
1524
union codetag_ref *ref; /* reference in page extension */
25+
struct page *page; /* reference in page flags */
1626
};
1727

18-
extern struct page_ext_operations page_alloc_tagging_ops;
28+
/* Reserved indexes */
29+
#define CODETAG_ID_NULL 0
30+
#define CODETAG_ID_EMPTY 1
31+
#define CODETAG_ID_FIRST 2
32+
33+
#ifdef CONFIG_MODULES
34+
35+
extern struct alloc_tag_module_section module_tags;
36+
37+
static inline struct alloc_tag *module_idx_to_tag(pgalloc_tag_idx idx)
38+
{
39+
return &module_tags.first_tag[idx - kernel_tags.count];
40+
}
41+
42+
static inline pgalloc_tag_idx module_tag_to_idx(struct alloc_tag *tag)
43+
{
44+
return CODETAG_ID_FIRST + kernel_tags.count + (tag - module_tags.first_tag);
45+
}
46+
47+
#else /* CONFIG_MODULES */
48+
49+
static inline struct alloc_tag *module_idx_to_tag(pgalloc_tag_idx idx)
50+
{
51+
pr_warn("invalid page tag reference %lu\n", (unsigned long)idx);
52+
return NULL;
53+
}
54+
55+
static inline pgalloc_tag_idx module_tag_to_idx(struct alloc_tag *tag)
56+
{
57+
pr_warn("invalid page tag 0x%lx\n", (unsigned long)tag);
58+
return CODETAG_ID_NULL;
59+
}
60+
61+
#endif /* CONFIG_MODULES */
62+
63+
static inline void idx_to_ref(pgalloc_tag_idx idx, union codetag_ref *ref)
64+
{
65+
switch (idx) {
66+
case (CODETAG_ID_NULL):
67+
ref->ct = NULL;
68+
break;
69+
case (CODETAG_ID_EMPTY):
70+
set_codetag_empty(ref);
71+
break;
72+
default:
73+
idx -= CODETAG_ID_FIRST;
74+
ref->ct = idx < kernel_tags.count ?
75+
&kernel_tags.first_tag[idx].ct :
76+
&module_idx_to_tag(idx)->ct;
77+
break;
78+
}
79+
}
80+
81+
static inline pgalloc_tag_idx ref_to_idx(union codetag_ref *ref)
82+
{
83+
struct alloc_tag *tag;
84+
85+
if (!ref->ct)
86+
return CODETAG_ID_NULL;
87+
88+
if (is_codetag_empty(ref))
89+
return CODETAG_ID_EMPTY;
90+
91+
tag = ct_to_alloc_tag(ref->ct);
92+
if (tag >= kernel_tags.first_tag && tag < kernel_tags.first_tag + kernel_tags.count)
93+
return CODETAG_ID_FIRST + (tag - kernel_tags.first_tag);
94+
95+
return module_tag_to_idx(tag);
96+
}
97+
98+
1999

20100
/* Should be called only if mem_alloc_profiling_enabled() */
21101
static inline bool get_page_tag_ref(struct page *page, union codetag_ref *ref,
22102
union pgtag_ref_handle *handle)
23103
{
24-
struct page_ext *page_ext;
25-
union codetag_ref *tmp;
26-
27104
if (!page)
28105
return false;
29106

30-
page_ext = page_ext_get(page);
31-
if (!page_ext)
32-
return false;
107+
if (static_key_enabled(&mem_profiling_compressed)) {
108+
pgalloc_tag_idx idx;
109+
110+
idx = (page->flags >> alloc_tag_ref_offs) & alloc_tag_ref_mask;
111+
idx_to_ref(idx, ref);
112+
handle->page = page;
113+
} else {
114+
struct page_ext *page_ext;
115+
union codetag_ref *tmp;
116+
117+
page_ext = page_ext_get(page);
118+
if (!page_ext)
119+
return false;
120+
121+
tmp = (union codetag_ref *)page_ext_data(page_ext, &page_alloc_tagging_ops);
122+
ref->ct = tmp->ct;
123+
handle->ref = tmp;
124+
}
33125

34-
tmp = (union codetag_ref *)page_ext_data(page_ext, &page_alloc_tagging_ops);
35-
ref->ct = tmp->ct;
36-
handle->ref = tmp;
37126
return true;
38127
}
39128

@@ -42,16 +131,35 @@ static inline void put_page_tag_ref(union pgtag_ref_handle handle)
42131
if (WARN_ON(!handle.ref))
43132
return;
44133

45-
page_ext_put((void *)handle.ref - page_alloc_tagging_ops.offset);
134+
if (!static_key_enabled(&mem_profiling_compressed))
135+
page_ext_put((void *)handle.ref - page_alloc_tagging_ops.offset);
46136
}
47137

48-
static inline void update_page_tag_ref(union pgtag_ref_handle handle,
49-
union codetag_ref *ref)
138+
static inline void update_page_tag_ref(union pgtag_ref_handle handle, union codetag_ref *ref)
50139
{
51-
if (WARN_ON(!handle.ref || !ref))
52-
return;
53-
54-
handle.ref->ct = ref->ct;
140+
if (static_key_enabled(&mem_profiling_compressed)) {
141+
struct page *page = handle.page;
142+
unsigned long old_flags;
143+
unsigned long flags;
144+
unsigned long idx;
145+
146+
if (WARN_ON(!page || !ref))
147+
return;
148+
149+
idx = (unsigned long)ref_to_idx(ref);
150+
idx = (idx & alloc_tag_ref_mask) << alloc_tag_ref_offs;
151+
do {
152+
old_flags = READ_ONCE(page->flags);
153+
flags = old_flags;
154+
flags &= ~(alloc_tag_ref_mask << alloc_tag_ref_offs);
155+
flags |= idx;
156+
} while (unlikely(!try_cmpxchg(&page->flags, &old_flags, flags)));
157+
} else {
158+
if (WARN_ON(!handle.ref || !ref))
159+
return;
160+
161+
handle.ref->ct = ref->ct;
162+
}
55163
}
56164

57165
static inline void clear_page_tag_ref(struct page *page)
@@ -122,6 +230,8 @@ static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr)
122230
this_cpu_sub(tag->counters->bytes, PAGE_SIZE * nr);
123231
}
124232

233+
void __init alloc_tag_sec_init(void);
234+
125235
#else /* CONFIG_MEM_ALLOC_PROFILING */
126236

127237
static inline void clear_page_tag_ref(struct page *page) {}
@@ -130,6 +240,7 @@ static inline void pgalloc_tag_add(struct page *page, struct task_struct *task,
130240
static inline void pgalloc_tag_sub(struct page *page, unsigned int nr) {}
131241
static inline struct alloc_tag *pgalloc_tag_get(struct page *page) { return NULL; }
132242
static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) {}
243+
static inline void alloc_tag_sec_init(void) {}
133244

134245
#endif /* CONFIG_MEM_ALLOC_PROFILING */
135246

0 commit comments

Comments
 (0)