1dcfe378cSSuren Baghdasaryan /* SPDX-License-Identifier: GPL-2.0 */
2dcfe378cSSuren Baghdasaryan /*
3dcfe378cSSuren Baghdasaryan * page allocation tagging
4dcfe378cSSuren Baghdasaryan */
5dcfe378cSSuren Baghdasaryan #ifndef _LINUX_PGALLOC_TAG_H
6dcfe378cSSuren Baghdasaryan #define _LINUX_PGALLOC_TAG_H
7dcfe378cSSuren Baghdasaryan
8dcfe378cSSuren Baghdasaryan #include <linux/alloc_tag.h>
9dcfe378cSSuren Baghdasaryan
10dcfe378cSSuren Baghdasaryan #ifdef CONFIG_MEM_ALLOC_PROFILING
11dcfe378cSSuren Baghdasaryan
12dcfe378cSSuren Baghdasaryan #include <linux/page_ext.h>
13dcfe378cSSuren Baghdasaryan
144835f747SSuren Baghdasaryan extern struct page_ext_operations page_alloc_tagging_ops;
154835f747SSuren Baghdasaryan extern unsigned long alloc_tag_ref_mask;
164835f747SSuren Baghdasaryan extern int alloc_tag_ref_offs;
174835f747SSuren Baghdasaryan extern struct alloc_tag_kernel_section kernel_tags;
184835f747SSuren Baghdasaryan
194835f747SSuren Baghdasaryan DECLARE_STATIC_KEY_FALSE(mem_profiling_compressed);
204835f747SSuren Baghdasaryan
214835f747SSuren Baghdasaryan typedef u16 pgalloc_tag_idx;
224835f747SSuren Baghdasaryan
2342895a86SSuren Baghdasaryan union pgtag_ref_handle {
2442895a86SSuren Baghdasaryan union codetag_ref *ref; /* reference in page extension */
254835f747SSuren Baghdasaryan struct page *page; /* reference in page flags */
2642895a86SSuren Baghdasaryan };
2742895a86SSuren Baghdasaryan
284835f747SSuren Baghdasaryan /* Reserved indexes */
294835f747SSuren Baghdasaryan #define CODETAG_ID_NULL 0
304835f747SSuren Baghdasaryan #define CODETAG_ID_EMPTY 1
314835f747SSuren Baghdasaryan #define CODETAG_ID_FIRST 2
324835f747SSuren Baghdasaryan
334835f747SSuren Baghdasaryan #ifdef CONFIG_MODULES
344835f747SSuren Baghdasaryan
354835f747SSuren Baghdasaryan extern struct alloc_tag_module_section module_tags;
364835f747SSuren Baghdasaryan
module_idx_to_tag(pgalloc_tag_idx idx)374835f747SSuren Baghdasaryan static inline struct alloc_tag *module_idx_to_tag(pgalloc_tag_idx idx)
384835f747SSuren Baghdasaryan {
394835f747SSuren Baghdasaryan return &module_tags.first_tag[idx - kernel_tags.count];
404835f747SSuren Baghdasaryan }
414835f747SSuren Baghdasaryan
module_tag_to_idx(struct alloc_tag * tag)424835f747SSuren Baghdasaryan static inline pgalloc_tag_idx module_tag_to_idx(struct alloc_tag *tag)
434835f747SSuren Baghdasaryan {
444835f747SSuren Baghdasaryan return CODETAG_ID_FIRST + kernel_tags.count + (tag - module_tags.first_tag);
454835f747SSuren Baghdasaryan }
464835f747SSuren Baghdasaryan
474835f747SSuren Baghdasaryan #else /* CONFIG_MODULES */
484835f747SSuren Baghdasaryan
module_idx_to_tag(pgalloc_tag_idx idx)494835f747SSuren Baghdasaryan static inline struct alloc_tag *module_idx_to_tag(pgalloc_tag_idx idx)
504835f747SSuren Baghdasaryan {
514835f747SSuren Baghdasaryan pr_warn("invalid page tag reference %lu\n", (unsigned long)idx);
524835f747SSuren Baghdasaryan return NULL;
534835f747SSuren Baghdasaryan }
544835f747SSuren Baghdasaryan
module_tag_to_idx(struct alloc_tag * tag)554835f747SSuren Baghdasaryan static inline pgalloc_tag_idx module_tag_to_idx(struct alloc_tag *tag)
564835f747SSuren Baghdasaryan {
574835f747SSuren Baghdasaryan pr_warn("invalid page tag 0x%lx\n", (unsigned long)tag);
584835f747SSuren Baghdasaryan return CODETAG_ID_NULL;
594835f747SSuren Baghdasaryan }
604835f747SSuren Baghdasaryan
614835f747SSuren Baghdasaryan #endif /* CONFIG_MODULES */
624835f747SSuren Baghdasaryan
idx_to_ref(pgalloc_tag_idx idx,union codetag_ref * ref)634835f747SSuren Baghdasaryan static inline void idx_to_ref(pgalloc_tag_idx idx, union codetag_ref *ref)
644835f747SSuren Baghdasaryan {
654835f747SSuren Baghdasaryan switch (idx) {
664835f747SSuren Baghdasaryan case (CODETAG_ID_NULL):
674835f747SSuren Baghdasaryan ref->ct = NULL;
684835f747SSuren Baghdasaryan break;
694835f747SSuren Baghdasaryan case (CODETAG_ID_EMPTY):
704835f747SSuren Baghdasaryan set_codetag_empty(ref);
714835f747SSuren Baghdasaryan break;
724835f747SSuren Baghdasaryan default:
734835f747SSuren Baghdasaryan idx -= CODETAG_ID_FIRST;
744835f747SSuren Baghdasaryan ref->ct = idx < kernel_tags.count ?
754835f747SSuren Baghdasaryan &kernel_tags.first_tag[idx].ct :
764835f747SSuren Baghdasaryan &module_idx_to_tag(idx)->ct;
774835f747SSuren Baghdasaryan break;
784835f747SSuren Baghdasaryan }
794835f747SSuren Baghdasaryan }
804835f747SSuren Baghdasaryan
ref_to_idx(union codetag_ref * ref)814835f747SSuren Baghdasaryan static inline pgalloc_tag_idx ref_to_idx(union codetag_ref *ref)
824835f747SSuren Baghdasaryan {
834835f747SSuren Baghdasaryan struct alloc_tag *tag;
844835f747SSuren Baghdasaryan
854835f747SSuren Baghdasaryan if (!ref->ct)
864835f747SSuren Baghdasaryan return CODETAG_ID_NULL;
874835f747SSuren Baghdasaryan
884835f747SSuren Baghdasaryan if (is_codetag_empty(ref))
894835f747SSuren Baghdasaryan return CODETAG_ID_EMPTY;
904835f747SSuren Baghdasaryan
914835f747SSuren Baghdasaryan tag = ct_to_alloc_tag(ref->ct);
924835f747SSuren Baghdasaryan if (tag >= kernel_tags.first_tag && tag < kernel_tags.first_tag + kernel_tags.count)
934835f747SSuren Baghdasaryan return CODETAG_ID_FIRST + (tag - kernel_tags.first_tag);
944835f747SSuren Baghdasaryan
954835f747SSuren Baghdasaryan return module_tag_to_idx(tag);
964835f747SSuren Baghdasaryan }
974835f747SSuren Baghdasaryan
984835f747SSuren Baghdasaryan
99dcfe378cSSuren Baghdasaryan
100dcfe378cSSuren Baghdasaryan /* Should be called only if mem_alloc_profiling_enabled() */
get_page_tag_ref(struct page * page,union codetag_ref * ref,union pgtag_ref_handle * handle)10142895a86SSuren Baghdasaryan static inline bool get_page_tag_ref(struct page *page, union codetag_ref *ref,
10242895a86SSuren Baghdasaryan union pgtag_ref_handle *handle)
103dcfe378cSSuren Baghdasaryan {
10442895a86SSuren Baghdasaryan if (!page)
10542895a86SSuren Baghdasaryan return false;
10642895a86SSuren Baghdasaryan
1074835f747SSuren Baghdasaryan if (static_key_enabled(&mem_profiling_compressed)) {
1084835f747SSuren Baghdasaryan pgalloc_tag_idx idx;
1094835f747SSuren Baghdasaryan
1104835f747SSuren Baghdasaryan idx = (page->flags >> alloc_tag_ref_offs) & alloc_tag_ref_mask;
1114835f747SSuren Baghdasaryan idx_to_ref(idx, ref);
1124835f747SSuren Baghdasaryan handle->page = page;
1134835f747SSuren Baghdasaryan } else {
1144835f747SSuren Baghdasaryan struct page_ext *page_ext;
1154835f747SSuren Baghdasaryan union codetag_ref *tmp;
1164835f747SSuren Baghdasaryan
11742895a86SSuren Baghdasaryan page_ext = page_ext_get(page);
11842895a86SSuren Baghdasaryan if (!page_ext)
11942895a86SSuren Baghdasaryan return false;
12042895a86SSuren Baghdasaryan
12142895a86SSuren Baghdasaryan tmp = (union codetag_ref *)page_ext_data(page_ext, &page_alloc_tagging_ops);
12242895a86SSuren Baghdasaryan ref->ct = tmp->ct;
12342895a86SSuren Baghdasaryan handle->ref = tmp;
1244835f747SSuren Baghdasaryan }
1254835f747SSuren Baghdasaryan
12642895a86SSuren Baghdasaryan return true;
127dcfe378cSSuren Baghdasaryan }
128dcfe378cSSuren Baghdasaryan
put_page_tag_ref(union pgtag_ref_handle handle)12942895a86SSuren Baghdasaryan static inline void put_page_tag_ref(union pgtag_ref_handle handle)
130dcfe378cSSuren Baghdasaryan {
13142895a86SSuren Baghdasaryan if (WARN_ON(!handle.ref))
132a273559eSSuren Baghdasaryan return;
133a273559eSSuren Baghdasaryan
1344835f747SSuren Baghdasaryan if (!static_key_enabled(&mem_profiling_compressed))
13542895a86SSuren Baghdasaryan page_ext_put((void *)handle.ref - page_alloc_tagging_ops.offset);
13642895a86SSuren Baghdasaryan }
13742895a86SSuren Baghdasaryan
update_page_tag_ref(union pgtag_ref_handle handle,union codetag_ref * ref)1384835f747SSuren Baghdasaryan static inline void update_page_tag_ref(union pgtag_ref_handle handle, union codetag_ref *ref)
13942895a86SSuren Baghdasaryan {
1404835f747SSuren Baghdasaryan if (static_key_enabled(&mem_profiling_compressed)) {
1414835f747SSuren Baghdasaryan struct page *page = handle.page;
1424835f747SSuren Baghdasaryan unsigned long old_flags;
1434835f747SSuren Baghdasaryan unsigned long flags;
1444835f747SSuren Baghdasaryan unsigned long idx;
1454835f747SSuren Baghdasaryan
1464835f747SSuren Baghdasaryan if (WARN_ON(!page || !ref))
1474835f747SSuren Baghdasaryan return;
1484835f747SSuren Baghdasaryan
1494835f747SSuren Baghdasaryan idx = (unsigned long)ref_to_idx(ref);
1504835f747SSuren Baghdasaryan idx = (idx & alloc_tag_ref_mask) << alloc_tag_ref_offs;
1514835f747SSuren Baghdasaryan do {
1524835f747SSuren Baghdasaryan old_flags = READ_ONCE(page->flags);
1534835f747SSuren Baghdasaryan flags = old_flags;
1544835f747SSuren Baghdasaryan flags &= ~(alloc_tag_ref_mask << alloc_tag_ref_offs);
1554835f747SSuren Baghdasaryan flags |= idx;
1564835f747SSuren Baghdasaryan } while (unlikely(!try_cmpxchg(&page->flags, &old_flags, flags)));
1574835f747SSuren Baghdasaryan } else {
15842895a86SSuren Baghdasaryan if (WARN_ON(!handle.ref || !ref))
15942895a86SSuren Baghdasaryan return;
16042895a86SSuren Baghdasaryan
16142895a86SSuren Baghdasaryan handle.ref->ct = ref->ct;
162dcfe378cSSuren Baghdasaryan }
1634835f747SSuren Baghdasaryan }
164dcfe378cSSuren Baghdasaryan
16593d5440eSSuren Baghdasaryan /* Should be called only if mem_alloc_profiling_enabled() */
16693d5440eSSuren Baghdasaryan void __clear_page_tag_ref(struct page *page);
16793d5440eSSuren Baghdasaryan
clear_page_tag_ref(struct page * page)168a8fc28daSSuren Baghdasaryan static inline void clear_page_tag_ref(struct page *page)
169a8fc28daSSuren Baghdasaryan {
17093d5440eSSuren Baghdasaryan if (mem_alloc_profiling_enabled())
17193d5440eSSuren Baghdasaryan __clear_page_tag_ref(page);
172dcfe378cSSuren Baghdasaryan }
173dcfe378cSSuren Baghdasaryan
17451ff4d74SSuren Baghdasaryan /* Should be called only if mem_alloc_profiling_enabled() */
__pgalloc_tag_get(struct page * page)17551ff4d74SSuren Baghdasaryan static inline struct alloc_tag *__pgalloc_tag_get(struct page *page)
176cc92eba1SSuren Baghdasaryan {
177cc92eba1SSuren Baghdasaryan struct alloc_tag *tag = NULL;
17842895a86SSuren Baghdasaryan union pgtag_ref_handle handle;
17942895a86SSuren Baghdasaryan union codetag_ref ref;
180cc92eba1SSuren Baghdasaryan
18142895a86SSuren Baghdasaryan if (get_page_tag_ref(page, &ref, &handle)) {
18242895a86SSuren Baghdasaryan alloc_tag_sub_check(&ref);
18342895a86SSuren Baghdasaryan if (ref.ct)
18442895a86SSuren Baghdasaryan tag = ct_to_alloc_tag(ref.ct);
18542895a86SSuren Baghdasaryan put_page_tag_ref(handle);
186cc92eba1SSuren Baghdasaryan }
187cc92eba1SSuren Baghdasaryan
188cc92eba1SSuren Baghdasaryan return tag;
189cc92eba1SSuren Baghdasaryan }
190cc92eba1SSuren Baghdasaryan
pgalloc_tag_get(struct page * page)191*0ae0227fSDavid Wang static inline struct alloc_tag *pgalloc_tag_get(struct page *page)
192*0ae0227fSDavid Wang {
193*0ae0227fSDavid Wang if (mem_alloc_profiling_enabled())
194*0ae0227fSDavid Wang return __pgalloc_tag_get(page);
195*0ae0227fSDavid Wang return NULL;
196*0ae0227fSDavid Wang }
197*0ae0227fSDavid Wang
198b7fc16a1SSuren Baghdasaryan void pgalloc_tag_split(struct folio *folio, int old_order, int new_order);
19951f43d5dSDavid Wang void pgalloc_tag_swap(struct folio *new, struct folio *old);
200b7fc16a1SSuren Baghdasaryan
2014835f747SSuren Baghdasaryan void __init alloc_tag_sec_init(void);
2024835f747SSuren Baghdasaryan
203dcfe378cSSuren Baghdasaryan #else /* CONFIG_MEM_ALLOC_PROFILING */
204dcfe378cSSuren Baghdasaryan
clear_page_tag_ref(struct page * page)205a8fc28daSSuren Baghdasaryan static inline void clear_page_tag_ref(struct page *page) {}
alloc_tag_sec_init(void)2064835f747SSuren Baghdasaryan static inline void alloc_tag_sec_init(void) {}
pgalloc_tag_split(struct folio * folio,int old_order,int new_order)207b7fc16a1SSuren Baghdasaryan static inline void pgalloc_tag_split(struct folio *folio, int old_order, int new_order) {}
pgalloc_tag_swap(struct folio * new,struct folio * old)20851f43d5dSDavid Wang static inline void pgalloc_tag_swap(struct folio *new, struct folio *old) {}
pgalloc_tag_get(struct page * page)209*0ae0227fSDavid Wang static inline struct alloc_tag *pgalloc_tag_get(struct page *page) { return NULL; }
210dcfe378cSSuren Baghdasaryan
211dcfe378cSSuren Baghdasaryan #endif /* CONFIG_MEM_ALLOC_PROFILING */
212dcfe378cSSuren Baghdasaryan
213dcfe378cSSuren Baghdasaryan #endif /* _LINUX_PGALLOC_TAG_H */
214