-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib: add allocation tagging support for memory allocation profiling
Introduce CONFIG_MEM_ALLOC_PROFILING which provides definitions to easily instrument memory allocators. It registers an "alloc_tags" codetag type with /proc/allocinfo interface to output allocation tag information when the feature is enabled. CONFIG_MEM_ALLOC_PROFILING_DEBUG is provided for debugging the memory allocation profiling instrumentation. Memory allocation profiling can be enabled or disabled at runtime using /proc/sys/vm/mem_profiling sysctl when CONFIG_MEM_ALLOC_PROFILING_DEBUG=n. CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT enables memory allocation profiling by default. [surenb@google.com: Documentation/filesystems/proc.rst: fix allocinfo title] Link: https://lkml.kernel.org/r/20240326073813.727090-1-surenb@google.com [surenb@google.com: do limited memory accounting for modules with ARCH_NEEDS_WEAK_PER_CPU] Link: https://lkml.kernel.org/r/20240402180933.1663992-2-surenb@google.com [klarasmodin@gmail.com: explicitly include irqflags.h in alloc_tag.h] Link: https://lkml.kernel.org/r/20240407133252.173636-1-klarasmodin@gmail.com [surenb@google.com: fix alloc_tag_init() to prevent passing NULL to PTR_ERR()] Link: https://lkml.kernel.org/r/20240417003349.2520094-1-surenb@google.com Link: https://lkml.kernel.org/r/20240321163705.3067592-14-surenb@google.com Signed-off-by: Suren Baghdasaryan <surenb@google.com> Co-developed-by: Kent Overstreet <kent.overstreet@linux.dev> Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev> Signed-off-by: Klara Modin <klarasmodin@gmail.com> Tested-by: Kees Cook <keescook@chromium.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Alex Gaynor <alex.gaynor@gmail.com> Cc: Alice Ryhl <aliceryhl@google.com> Cc: Andreas Hindborg <a.hindborg@samsung.com> Cc: Benno Lossin <benno.lossin@proton.me> Cc: "Björn Roy Baron" <bjorn3_gh@protonmail.com> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Christoph Lameter <cl@linux.com> Cc: Dennis Zhou <dennis@kernel.org> Cc: Gary Guo <gary@garyguo.net> Cc: Miguel Ojeda <ojeda@kernel.org> Cc: Pasha Tatashin <pasha.tatashin@soleen.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Tejun Heo <tj@kernel.org> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Wedson Almeida Filho <wedsonaf@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
- Loading branch information
1 parent
47a92df
commit 22d407b
Showing
10 changed files
with
428 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
#ifndef __ASM_GENERIC_CODETAG_LDS_H | ||
#define __ASM_GENERIC_CODETAG_LDS_H | ||
|
||
#define SECTION_WITH_BOUNDARIES(_name) \ | ||
. = ALIGN(8); \ | ||
__start_##_name = .; \ | ||
KEEP(*(_name)) \ | ||
__stop_##_name = .; | ||
|
||
#define CODETAG_SECTIONS() \ | ||
SECTION_WITH_BOUNDARIES(alloc_tags) | ||
|
||
#endif /* __ASM_GENERIC_CODETAG_LDS_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* allocation tagging | ||
*/ | ||
#ifndef _LINUX_ALLOC_TAG_H | ||
#define _LINUX_ALLOC_TAG_H | ||
|
||
#include <linux/bug.h> | ||
#include <linux/codetag.h> | ||
#include <linux/container_of.h> | ||
#include <linux/preempt.h> | ||
#include <asm/percpu.h> | ||
#include <linux/cpumask.h> | ||
#include <linux/static_key.h> | ||
#include <linux/irqflags.h> | ||
|
||
struct alloc_tag_counters { | ||
u64 bytes; | ||
u64 calls; | ||
}; | ||
|
||
/* | ||
* An instance of this structure is created in a special ELF section at every | ||
* allocation callsite. At runtime, the special section is treated as | ||
* an array of these. Embedded codetag utilizes codetag framework. | ||
*/ | ||
struct alloc_tag { | ||
struct codetag ct; | ||
struct alloc_tag_counters __percpu *counters; | ||
} __aligned(8); | ||
|
||
#ifdef CONFIG_MEM_ALLOC_PROFILING | ||
|
||
static inline struct alloc_tag *ct_to_alloc_tag(struct codetag *ct) | ||
{ | ||
return container_of(ct, struct alloc_tag, ct); | ||
} | ||
|
||
#ifdef ARCH_NEEDS_WEAK_PER_CPU | ||
/* | ||
* When percpu variables are required to be defined as weak, static percpu | ||
* variables can't be used inside a function (see comments for DECLARE_PER_CPU_SECTION). | ||
* Instead we will accound all module allocations to a single counter. | ||
*/ | ||
DECLARE_PER_CPU(struct alloc_tag_counters, _shared_alloc_tag); | ||
|
||
#define DEFINE_ALLOC_TAG(_alloc_tag) \ | ||
static struct alloc_tag _alloc_tag __used __aligned(8) \ | ||
__section("alloc_tags") = { \ | ||
.ct = CODE_TAG_INIT, \ | ||
.counters = &_shared_alloc_tag }; | ||
|
||
#else /* ARCH_NEEDS_WEAK_PER_CPU */ | ||
|
||
#define DEFINE_ALLOC_TAG(_alloc_tag) \ | ||
static DEFINE_PER_CPU(struct alloc_tag_counters, _alloc_tag_cntr); \ | ||
static struct alloc_tag _alloc_tag __used __aligned(8) \ | ||
__section("alloc_tags") = { \ | ||
.ct = CODE_TAG_INIT, \ | ||
.counters = &_alloc_tag_cntr }; | ||
|
||
#endif /* ARCH_NEEDS_WEAK_PER_CPU */ | ||
|
||
DECLARE_STATIC_KEY_MAYBE(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT, | ||
mem_alloc_profiling_key); | ||
|
||
static inline bool mem_alloc_profiling_enabled(void) | ||
{ | ||
return static_branch_maybe(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT, | ||
&mem_alloc_profiling_key); | ||
} | ||
|
||
static inline struct alloc_tag_counters alloc_tag_read(struct alloc_tag *tag) | ||
{ | ||
struct alloc_tag_counters v = { 0, 0 }; | ||
struct alloc_tag_counters *counter; | ||
int cpu; | ||
|
||
for_each_possible_cpu(cpu) { | ||
counter = per_cpu_ptr(tag->counters, cpu); | ||
v.bytes += counter->bytes; | ||
v.calls += counter->calls; | ||
} | ||
|
||
return v; | ||
} | ||
|
||
#ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG | ||
static inline void alloc_tag_add_check(union codetag_ref *ref, struct alloc_tag *tag) | ||
{ | ||
WARN_ONCE(ref && ref->ct, | ||
"alloc_tag was not cleared (got tag for %s:%u)\n", | ||
ref->ct->filename, ref->ct->lineno); | ||
|
||
WARN_ONCE(!tag, "current->alloc_tag not set"); | ||
} | ||
|
||
static inline void alloc_tag_sub_check(union codetag_ref *ref) | ||
{ | ||
WARN_ONCE(ref && !ref->ct, "alloc_tag was not set\n"); | ||
} | ||
#else | ||
static inline void alloc_tag_add_check(union codetag_ref *ref, struct alloc_tag *tag) {} | ||
static inline void alloc_tag_sub_check(union codetag_ref *ref) {} | ||
#endif | ||
|
||
/* Caller should verify both ref and tag to be valid */ | ||
static inline void __alloc_tag_ref_set(union codetag_ref *ref, struct alloc_tag *tag) | ||
{ | ||
ref->ct = &tag->ct; | ||
/* | ||
* We need in increment the call counter every time we have a new | ||
* allocation or when we split a large allocation into smaller ones. | ||
* Each new reference for every sub-allocation needs to increment call | ||
* counter because when we free each part the counter will be decremented. | ||
*/ | ||
this_cpu_inc(tag->counters->calls); | ||
} | ||
|
||
static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, size_t bytes) | ||
{ | ||
alloc_tag_add_check(ref, tag); | ||
if (!ref || !tag) | ||
return; | ||
|
||
__alloc_tag_ref_set(ref, tag); | ||
this_cpu_add(tag->counters->bytes, bytes); | ||
} | ||
|
||
static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) | ||
{ | ||
struct alloc_tag *tag; | ||
|
||
alloc_tag_sub_check(ref); | ||
if (!ref || !ref->ct) | ||
return; | ||
|
||
tag = ct_to_alloc_tag(ref->ct); | ||
|
||
this_cpu_sub(tag->counters->bytes, bytes); | ||
this_cpu_dec(tag->counters->calls); | ||
|
||
ref->ct = NULL; | ||
} | ||
|
||
#else /* CONFIG_MEM_ALLOC_PROFILING */ | ||
|
||
#define DEFINE_ALLOC_TAG(_alloc_tag) | ||
static inline bool mem_alloc_profiling_enabled(void) { return false; } | ||
static inline void alloc_tag_add(union codetag_ref *ref, struct alloc_tag *tag, | ||
size_t bytes) {} | ||
static inline void alloc_tag_sub(union codetag_ref *ref, size_t bytes) {} | ||
|
||
#endif /* CONFIG_MEM_ALLOC_PROFILING */ | ||
|
||
#endif /* _LINUX_ALLOC_TAG_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.