at v6.16 5.3 kB view raw
1/* SPDX-License-Identifier: GPL-2.0 */ 2/* 3 * page allocation tagging 4 */ 5#ifndef _LINUX_PGALLOC_TAG_H 6#define _LINUX_PGALLOC_TAG_H 7 8#include <linux/alloc_tag.h> 9 10#ifdef CONFIG_MEM_ALLOC_PROFILING 11 12#include <linux/page_ext.h> 13 14extern struct page_ext_operations page_alloc_tagging_ops; 15extern unsigned long alloc_tag_ref_mask; 16extern int alloc_tag_ref_offs; 17extern struct alloc_tag_kernel_section kernel_tags; 18 19DECLARE_STATIC_KEY_FALSE(mem_profiling_compressed); 20 21typedef u16 pgalloc_tag_idx; 22 23union pgtag_ref_handle { 24 union codetag_ref *ref; /* reference in page extension */ 25 struct page *page; /* reference in page flags */ 26}; 27 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 35extern struct alloc_tag_module_section module_tags; 36 37static 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 42static 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 49static 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 55static 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 63static 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 81static 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 99 100/* Should be called only if mem_alloc_profiling_enabled() */ 101static inline bool get_page_tag_ref(struct page *page, union codetag_ref *ref, 102 union pgtag_ref_handle *handle) 103{ 104 if (!page) 105 return false; 106 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 } 125 126 return true; 127} 128 129static inline void put_page_tag_ref(union pgtag_ref_handle handle) 130{ 131 if (WARN_ON(!handle.ref)) 132 return; 133 134 if (!static_key_enabled(&mem_profiling_compressed)) 135 page_ext_put((void *)handle.ref - page_alloc_tagging_ops.offset); 136} 137 138static inline void update_page_tag_ref(union pgtag_ref_handle handle, union codetag_ref *ref) 139{ 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 } 163} 164 165/* Should be called only if mem_alloc_profiling_enabled() */ 166void __clear_page_tag_ref(struct page *page); 167 168static inline void clear_page_tag_ref(struct page *page) 169{ 170 if (mem_alloc_profiling_enabled()) 171 __clear_page_tag_ref(page); 172} 173 174/* Should be called only if mem_alloc_profiling_enabled() */ 175static inline struct alloc_tag *__pgalloc_tag_get(struct page *page) 176{ 177 struct alloc_tag *tag = NULL; 178 union pgtag_ref_handle handle; 179 union codetag_ref ref; 180 181 if (get_page_tag_ref(page, &ref, &handle)) { 182 alloc_tag_sub_check(&ref); 183 if (ref.ct) 184 tag = ct_to_alloc_tag(ref.ct); 185 put_page_tag_ref(handle); 186 } 187 188 return tag; 189} 190 191static inline struct alloc_tag *pgalloc_tag_get(struct page *page) 192{ 193 if (mem_alloc_profiling_enabled()) 194 return __pgalloc_tag_get(page); 195 return NULL; 196} 197 198void pgalloc_tag_split(struct folio *folio, int old_order, int new_order); 199void pgalloc_tag_swap(struct folio *new, struct folio *old); 200 201void __init alloc_tag_sec_init(void); 202 203#else /* CONFIG_MEM_ALLOC_PROFILING */ 204 205static inline void clear_page_tag_ref(struct page *page) {} 206static inline void alloc_tag_sec_init(void) {} 207static inline void pgalloc_tag_split(struct folio *folio, int old_order, int new_order) {} 208static inline void pgalloc_tag_swap(struct folio *new, struct folio *old) {} 209static inline struct alloc_tag *pgalloc_tag_get(struct page *page) { return NULL; } 210 211#endif /* CONFIG_MEM_ALLOC_PROFILING */ 212 213#endif /* _LINUX_PGALLOC_TAG_H */