18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef _MM_PERCPU_INTERNAL_H 38c2ecf20Sopenharmony_ci#define _MM_PERCPU_INTERNAL_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/types.h> 68c2ecf20Sopenharmony_ci#include <linux/percpu.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * There are two chunk types: root and memcg-aware. 108c2ecf20Sopenharmony_ci * Chunks of each type have separate slots list. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Memcg-aware chunks have an attached vector of obj_cgroup pointers, which is 138c2ecf20Sopenharmony_ci * used to store memcg membership data of a percpu object. Obj_cgroups are 148c2ecf20Sopenharmony_ci * ref-counted pointers to a memory cgroup with an ability to switch dynamically 158c2ecf20Sopenharmony_ci * to the parent memory cgroup. This allows to reclaim a deleted memory cgroup 168c2ecf20Sopenharmony_ci * without reclaiming of all outstanding objects, which hold a reference at it. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_cienum pcpu_chunk_type { 198c2ecf20Sopenharmony_ci PCPU_CHUNK_ROOT, 208c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM 218c2ecf20Sopenharmony_ci PCPU_CHUNK_MEMCG, 228c2ecf20Sopenharmony_ci#endif 238c2ecf20Sopenharmony_ci PCPU_NR_CHUNK_TYPES, 248c2ecf20Sopenharmony_ci PCPU_FAIL_ALLOC = PCPU_NR_CHUNK_TYPES 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * pcpu_block_md is the metadata block struct. 298c2ecf20Sopenharmony_ci * Each chunk's bitmap is split into a number of full blocks. 308c2ecf20Sopenharmony_ci * All units are in terms of bits. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * The scan hint is the largest known contiguous area before the contig hint. 338c2ecf20Sopenharmony_ci * It is not necessarily the actual largest contig hint though. There is an 348c2ecf20Sopenharmony_ci * invariant that the scan_hint_start > contig_hint_start iff 358c2ecf20Sopenharmony_ci * scan_hint == contig_hint. This is necessary because when scanning forward, 368c2ecf20Sopenharmony_ci * we don't know if a new contig hint would be better than the current one. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistruct pcpu_block_md { 398c2ecf20Sopenharmony_ci int scan_hint; /* scan hint for block */ 408c2ecf20Sopenharmony_ci int scan_hint_start; /* block relative starting 418c2ecf20Sopenharmony_ci position of the scan hint */ 428c2ecf20Sopenharmony_ci int contig_hint; /* contig hint for block */ 438c2ecf20Sopenharmony_ci int contig_hint_start; /* block relative starting 448c2ecf20Sopenharmony_ci position of the contig hint */ 458c2ecf20Sopenharmony_ci int left_free; /* size of free space along 468c2ecf20Sopenharmony_ci the left side of the block */ 478c2ecf20Sopenharmony_ci int right_free; /* size of free space along 488c2ecf20Sopenharmony_ci the right side of the block */ 498c2ecf20Sopenharmony_ci int first_free; /* block position of first free */ 508c2ecf20Sopenharmony_ci int nr_bits; /* total bits responsible for */ 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct pcpu_chunk { 548c2ecf20Sopenharmony_ci#ifdef CONFIG_PERCPU_STATS 558c2ecf20Sopenharmony_ci int nr_alloc; /* # of allocations */ 568c2ecf20Sopenharmony_ci size_t max_alloc_size; /* largest allocation size */ 578c2ecf20Sopenharmony_ci#endif 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci struct list_head list; /* linked to pcpu_slot lists */ 608c2ecf20Sopenharmony_ci int free_bytes; /* free bytes in the chunk */ 618c2ecf20Sopenharmony_ci struct pcpu_block_md chunk_md; 628c2ecf20Sopenharmony_ci void *base_addr; /* base address of this chunk */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci unsigned long *alloc_map; /* allocation map */ 658c2ecf20Sopenharmony_ci unsigned long *bound_map; /* boundary map */ 668c2ecf20Sopenharmony_ci struct pcpu_block_md *md_blocks; /* metadata blocks */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci void *data; /* chunk data */ 698c2ecf20Sopenharmony_ci bool immutable; /* no [de]population allowed */ 708c2ecf20Sopenharmony_ci int start_offset; /* the overlap with the previous 718c2ecf20Sopenharmony_ci region to have a page aligned 728c2ecf20Sopenharmony_ci base_addr */ 738c2ecf20Sopenharmony_ci int end_offset; /* additional area required to 748c2ecf20Sopenharmony_ci have the region end page 758c2ecf20Sopenharmony_ci aligned */ 768c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM 778c2ecf20Sopenharmony_ci struct obj_cgroup **obj_cgroups; /* vector of object cgroups */ 788c2ecf20Sopenharmony_ci#endif 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci int nr_pages; /* # of pages served by this chunk */ 818c2ecf20Sopenharmony_ci int nr_populated; /* # of populated pages */ 828c2ecf20Sopenharmony_ci int nr_empty_pop_pages; /* # of empty populated pages */ 838c2ecf20Sopenharmony_ci unsigned long populated[]; /* populated bitmap */ 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciextern spinlock_t pcpu_lock; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciextern struct list_head *pcpu_chunk_lists; 898c2ecf20Sopenharmony_ciextern int pcpu_nr_slots; 908c2ecf20Sopenharmony_ciextern int pcpu_nr_empty_pop_pages[]; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciextern struct pcpu_chunk *pcpu_first_chunk; 938c2ecf20Sopenharmony_ciextern struct pcpu_chunk *pcpu_reserved_chunk; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * pcpu_chunk_nr_blocks - converts nr_pages to # of md_blocks 978c2ecf20Sopenharmony_ci * @chunk: chunk of interest 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * This conversion is from the number of physical pages that the chunk 1008c2ecf20Sopenharmony_ci * serves to the number of bitmap blocks used. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic inline int pcpu_chunk_nr_blocks(struct pcpu_chunk *chunk) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return chunk->nr_pages * PAGE_SIZE / PCPU_BITMAP_BLOCK_SIZE; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/** 1088c2ecf20Sopenharmony_ci * pcpu_nr_pages_to_map_bits - converts the pages to size of bitmap 1098c2ecf20Sopenharmony_ci * @pages: number of physical pages 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * This conversion is from physical pages to the number of bits 1128c2ecf20Sopenharmony_ci * required in the bitmap. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic inline int pcpu_nr_pages_to_map_bits(int pages) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return pages * PAGE_SIZE / PCPU_MIN_ALLOC_SIZE; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/** 1208c2ecf20Sopenharmony_ci * pcpu_chunk_map_bits - helper to convert nr_pages to size of bitmap 1218c2ecf20Sopenharmony_ci * @chunk: chunk of interest 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * This conversion is from the number of physical pages that the chunk 1248c2ecf20Sopenharmony_ci * serves to the number of bits in the bitmap. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistatic inline int pcpu_chunk_map_bits(struct pcpu_chunk *chunk) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return pcpu_nr_pages_to_map_bits(chunk->nr_pages); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM 1328c2ecf20Sopenharmony_cistatic inline enum pcpu_chunk_type pcpu_chunk_type(struct pcpu_chunk *chunk) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci if (chunk->obj_cgroups) 1358c2ecf20Sopenharmony_ci return PCPU_CHUNK_MEMCG; 1368c2ecf20Sopenharmony_ci return PCPU_CHUNK_ROOT; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic inline bool pcpu_is_memcg_chunk(enum pcpu_chunk_type chunk_type) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return chunk_type == PCPU_CHUNK_MEMCG; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#else 1458c2ecf20Sopenharmony_cistatic inline enum pcpu_chunk_type pcpu_chunk_type(struct pcpu_chunk *chunk) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return PCPU_CHUNK_ROOT; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic inline bool pcpu_is_memcg_chunk(enum pcpu_chunk_type chunk_type) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci return false; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci#endif 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic inline struct list_head *pcpu_chunk_list(enum pcpu_chunk_type chunk_type) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return &pcpu_chunk_lists[pcpu_nr_slots * 1598c2ecf20Sopenharmony_ci pcpu_is_memcg_chunk(chunk_type)]; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#ifdef CONFIG_PERCPU_STATS 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistruct percpu_stats { 1678c2ecf20Sopenharmony_ci u64 nr_alloc; /* lifetime # of allocations */ 1688c2ecf20Sopenharmony_ci u64 nr_dealloc; /* lifetime # of deallocations */ 1698c2ecf20Sopenharmony_ci u64 nr_cur_alloc; /* current # of allocations */ 1708c2ecf20Sopenharmony_ci u64 nr_max_alloc; /* max # of live allocations */ 1718c2ecf20Sopenharmony_ci u32 nr_chunks; /* current # of live chunks */ 1728c2ecf20Sopenharmony_ci u32 nr_max_chunks; /* max # of live chunks */ 1738c2ecf20Sopenharmony_ci size_t min_alloc_size; /* min allocaiton size */ 1748c2ecf20Sopenharmony_ci size_t max_alloc_size; /* max allocation size */ 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciextern struct percpu_stats pcpu_stats; 1788c2ecf20Sopenharmony_ciextern struct pcpu_alloc_info pcpu_stats_ai; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * For debug purposes. We don't care about the flexible array. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic inline void pcpu_stats_save_ai(const struct pcpu_alloc_info *ai) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci memcpy(&pcpu_stats_ai, ai, sizeof(struct pcpu_alloc_info)); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* initialize min_alloc_size to unit_size */ 1888c2ecf20Sopenharmony_ci pcpu_stats.min_alloc_size = pcpu_stats_ai.unit_size; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * pcpu_stats_area_alloc - increment area allocation stats 1938c2ecf20Sopenharmony_ci * @chunk: the location of the area being allocated 1948c2ecf20Sopenharmony_ci * @size: size of area to allocate in bytes 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * CONTEXT: 1978c2ecf20Sopenharmony_ci * pcpu_lock. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic inline void pcpu_stats_area_alloc(struct pcpu_chunk *chunk, size_t size) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci lockdep_assert_held(&pcpu_lock); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci pcpu_stats.nr_alloc++; 2048c2ecf20Sopenharmony_ci pcpu_stats.nr_cur_alloc++; 2058c2ecf20Sopenharmony_ci pcpu_stats.nr_max_alloc = 2068c2ecf20Sopenharmony_ci max(pcpu_stats.nr_max_alloc, pcpu_stats.nr_cur_alloc); 2078c2ecf20Sopenharmony_ci pcpu_stats.min_alloc_size = 2088c2ecf20Sopenharmony_ci min(pcpu_stats.min_alloc_size, size); 2098c2ecf20Sopenharmony_ci pcpu_stats.max_alloc_size = 2108c2ecf20Sopenharmony_ci max(pcpu_stats.max_alloc_size, size); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci chunk->nr_alloc++; 2138c2ecf20Sopenharmony_ci chunk->max_alloc_size = max(chunk->max_alloc_size, size); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * pcpu_stats_area_dealloc - decrement allocation stats 2188c2ecf20Sopenharmony_ci * @chunk: the location of the area being deallocated 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * CONTEXT: 2218c2ecf20Sopenharmony_ci * pcpu_lock. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_cistatic inline void pcpu_stats_area_dealloc(struct pcpu_chunk *chunk) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci lockdep_assert_held(&pcpu_lock); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci pcpu_stats.nr_dealloc++; 2288c2ecf20Sopenharmony_ci pcpu_stats.nr_cur_alloc--; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci chunk->nr_alloc--; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * pcpu_stats_chunk_alloc - increment chunk stats 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistatic inline void pcpu_stats_chunk_alloc(void) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci unsigned long flags; 2398c2ecf20Sopenharmony_ci spin_lock_irqsave(&pcpu_lock, flags); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pcpu_stats.nr_chunks++; 2428c2ecf20Sopenharmony_ci pcpu_stats.nr_max_chunks = 2438c2ecf20Sopenharmony_ci max(pcpu_stats.nr_max_chunks, pcpu_stats.nr_chunks); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pcpu_lock, flags); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * pcpu_stats_chunk_dealloc - decrement chunk stats 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic inline void pcpu_stats_chunk_dealloc(void) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci unsigned long flags; 2548c2ecf20Sopenharmony_ci spin_lock_irqsave(&pcpu_lock, flags); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci pcpu_stats.nr_chunks--; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pcpu_lock, flags); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci#else 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic inline void pcpu_stats_save_ai(const struct pcpu_alloc_info *ai) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic inline void pcpu_stats_area_alloc(struct pcpu_chunk *chunk, size_t size) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline void pcpu_stats_area_dealloc(struct pcpu_chunk *chunk) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic inline void pcpu_stats_chunk_alloc(void) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic inline void pcpu_stats_chunk_dealloc(void) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci#endif /* !CONFIG_PERCPU_STATS */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci#endif 286