Skip to content

Instantly share code, notes, and snippets.

@thejh
Last active August 26, 2024 15:43
Show Gist options
  • Save thejh/d61b75722489d23d2ed41593b00f4fb9 to your computer and use it in GitHub Desktop.
Save thejh/d61b75722489d23d2ed41593b00f4fb9 to your computer and use it in GitHub Desktop.
perf addrinfo patch
diff --git a/include/linux/slab.h b/include/linux/slab.h
index b5f5ee8308d0..aad7ffe6c45a 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -778,4 +778,6 @@ size_t kmalloc_size_roundup(size_t size);
void __init kmem_cache_init_late(void);
+const char *folio_cache_name(const struct folio *folio);
+
#endif /* _LINUX_SLAB_H */
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index 3a64499b0f5d..c92b750a9489 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -132,6 +132,27 @@ enum perf_sw_ids {
PERF_COUNT_SW_MAX, /* non-ABI */
};
+enum perf_addrinfo_memtype {
+ MEMTYPE_INVALID,
+ MEMTYPE_USER,
+ MEMTYPE_KERNEL,
+ MEMTYPE_KERNEL_LINEAR_MAPPING,
+ MEMTYPE_KERNEL_TEXT,
+ MEMTYPE_KERNEL_VMALLOC,
+ MEMTYPE_KERNEL_OWN_TASK_STACK,
+ MEMTYPE_KERNEL_MODULE,
+ MEMTYPE_KERNEL_SLAB,
+};
+
+struct perf_addrinfo {
+ enum perf_addrinfo_memtype type;
+ union {
+ struct {
+ char name[32];
+ } slab;
+ };
+} __attribute__((aligned(8)));
+
/*
* Bits that can be set in attr.sample_type to request information
* in the overflow packets.
@@ -162,8 +183,9 @@ enum perf_event_sample_format {
PERF_SAMPLE_DATA_PAGE_SIZE = 1U << 22,
PERF_SAMPLE_CODE_PAGE_SIZE = 1U << 23,
PERF_SAMPLE_WEIGHT_STRUCT = 1U << 24,
+ PERF_SAMPLE_ADDRINFO = 1U << 25,
- PERF_SAMPLE_MAX = 1U << 25, /* non-ABI */
+ PERF_SAMPLE_MAX = 1U << 26, /* non-ABI */
};
#define PERF_SAMPLE_WEIGHT_TYPE (PERF_SAMPLE_WEIGHT | PERF_SAMPLE_WEIGHT_STRUCT)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index f0f0f71213a1..208622d55855 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1855,6 +1855,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type)
if (sample_type & PERF_SAMPLE_ADDR)
size += sizeof(data->addr);
+ if (sample_type & PERF_SAMPLE_ADDRINFO)
+ size += sizeof(struct perf_addrinfo);
+
if (sample_type & PERF_SAMPLE_PERIOD)
size += sizeof(data->period);
@@ -7330,6 +7333,49 @@ void perf_output_sample(struct perf_output_handle *handle,
if (sample_type & PERF_SAMPLE_ADDR)
perf_output_put(handle, data->addr);
+ if (sample_type & PERF_SAMPLE_ADDRINFO) {
+ struct perf_addrinfo addrinfo;
+ unsigned long addr = untagged_addr(data->addr);
+
+ memset(&addrinfo, '\0', sizeof(addrinfo));
+
+ // TODO: assumes that user/kernel address space is shared
+ // TODO: x86-specific code below
+ if (addr < TASK_SIZE_MAX) {
+ addrinfo.type = MEMTYPE_USER;
+ } else if (addr - (unsigned long)task_stack_page(current) < THREAD_SIZE) {
+ addrinfo.type = MEMTYPE_KERNEL_OWN_TASK_STACK;
+ } else if (addr >= PAGE_OFFSET && addr < VMALLOC_START && virt_addr_valid(addr)) {
+ const struct page *page = virt_to_page(addr);
+ /* page_folio() is allowed without holding a reference */
+ const struct folio *folio = page_folio(page);
+ struct folio copied_folio_start = {};
+
+ // TODO dirty hacks relying on struct layouts
+ rcu_read_lock();
+ *(u128*)&copied_folio_start = cmpxchg128((u128*)folio, 0, 0);
+ if (folio_test_slab(&copied_folio_start)) {
+ // very dirty, passing in a partially initialized copied folio struct
+ const char *slab_name = folio_cache_name(&copied_folio_start);
+
+ addrinfo.type = MEMTYPE_KERNEL_SLAB;
+ strscpy(addrinfo.slab.name, slab_name, sizeof(addrinfo.slab.name));
+ } else {
+ addrinfo.type = MEMTYPE_KERNEL_LINEAR_MAPPING;
+ }
+ rcu_read_unlock();
+ } else if (addr >= VMALLOC_START && addr < VMALLOC_END) {
+ addrinfo.type = MEMTYPE_KERNEL_VMALLOC;
+ } else if (addr >= __START_KERNEL_map && addr < MODULES_VADDR) {
+ addrinfo.type = MEMTYPE_KERNEL_TEXT;
+ } else if (addr >= MODULES_VADDR && addr < MODULES_END) {
+ addrinfo.type = MEMTYPE_KERNEL_MODULE;
+ } else {
+ addrinfo.type = MEMTYPE_KERNEL;
+ }
+ perf_output_put(handle, addrinfo);
+ }
+
if (sample_type & PERF_SAMPLE_ID)
perf_output_put(handle, data->id);
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 238293b1dbe1..9fec6e90b534 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -468,8 +468,9 @@ static int shutdown_cache(struct kmem_cache *s)
void slab_kmem_cache_release(struct kmem_cache *s)
{
__kmem_cache_release(s);
- kfree_const(s->name);
- kmem_cache_free(kmem_cache, s);
+ if (!is_kernel_rodata(s->name))
+ kfree_rcu_mightsleep(s->name);
+ kfree_rcu_mightsleep(s);
}
void kmem_cache_destroy(struct kmem_cache *s)
@@ -1087,6 +1088,15 @@ static int slab_show(struct seq_file *m, void *p)
return 0;
}
+const char *folio_cache_name(const struct folio *folio)
+{
+ struct slab *s = folio_slab(folio);
+ struct kmem_cache *cache = s->slab_cache;
+
+ // TODO unclean annotation
+ return rcu_dereference(cache->name);
+}
+
void dump_unreclaimable_slab(void)
{
struct kmem_cache *s;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment