162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#ifndef _MM_PERCPU_INTERNAL_H 362306a36Sopenharmony_ci#define _MM_PERCPU_INTERNAL_H 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/types.h> 662306a36Sopenharmony_ci#include <linux/percpu.h> 762306a36Sopenharmony_ci#include <linux/memcontrol.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * pcpu_block_md is the metadata block struct. 1162306a36Sopenharmony_ci * Each chunk's bitmap is split into a number of full blocks. 1262306a36Sopenharmony_ci * All units are in terms of bits. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The scan hint is the largest known contiguous area before the contig hint. 1562306a36Sopenharmony_ci * It is not necessarily the actual largest contig hint though. There is an 1662306a36Sopenharmony_ci * invariant that the scan_hint_start > contig_hint_start iff 1762306a36Sopenharmony_ci * scan_hint == contig_hint. This is necessary because when scanning forward, 1862306a36Sopenharmony_ci * we don't know if a new contig hint would be better than the current one. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistruct pcpu_block_md { 2162306a36Sopenharmony_ci int scan_hint; /* scan hint for block */ 2262306a36Sopenharmony_ci int scan_hint_start; /* block relative starting 2362306a36Sopenharmony_ci position of the scan hint */ 2462306a36Sopenharmony_ci int contig_hint; /* contig hint for block */ 2562306a36Sopenharmony_ci int contig_hint_start; /* block relative starting 2662306a36Sopenharmony_ci position of the contig hint */ 2762306a36Sopenharmony_ci int left_free; /* size of free space along 2862306a36Sopenharmony_ci the left side of the block */ 2962306a36Sopenharmony_ci int right_free; /* size of free space along 3062306a36Sopenharmony_ci the right side of the block */ 3162306a36Sopenharmony_ci int first_free; /* block position of first free */ 3262306a36Sopenharmony_ci int nr_bits; /* total bits responsible for */ 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct pcpu_chunk { 3662306a36Sopenharmony_ci#ifdef CONFIG_PERCPU_STATS 3762306a36Sopenharmony_ci int nr_alloc; /* # of allocations */ 3862306a36Sopenharmony_ci size_t max_alloc_size; /* largest allocation size */ 3962306a36Sopenharmony_ci#endif 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci struct list_head list; /* linked to pcpu_slot lists */ 4262306a36Sopenharmony_ci int free_bytes; /* free bytes in the chunk */ 4362306a36Sopenharmony_ci struct pcpu_block_md chunk_md; 4462306a36Sopenharmony_ci unsigned long *bound_map; /* boundary map */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * base_addr is the base address of this chunk. 4862306a36Sopenharmony_ci * To reduce false sharing, current layout is optimized to make sure 4962306a36Sopenharmony_ci * base_addr locate in the different cacheline with free_bytes and 5062306a36Sopenharmony_ci * chunk_md. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci void *base_addr ____cacheline_aligned_in_smp; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci unsigned long *alloc_map; /* allocation map */ 5562306a36Sopenharmony_ci struct pcpu_block_md *md_blocks; /* metadata blocks */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci void *data; /* chunk data */ 5862306a36Sopenharmony_ci bool immutable; /* no [de]population allowed */ 5962306a36Sopenharmony_ci bool isolated; /* isolated from active chunk 6062306a36Sopenharmony_ci slots */ 6162306a36Sopenharmony_ci int start_offset; /* the overlap with the previous 6262306a36Sopenharmony_ci region to have a page aligned 6362306a36Sopenharmony_ci base_addr */ 6462306a36Sopenharmony_ci int end_offset; /* additional area required to 6562306a36Sopenharmony_ci have the region end page 6662306a36Sopenharmony_ci aligned */ 6762306a36Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM 6862306a36Sopenharmony_ci struct obj_cgroup **obj_cgroups; /* vector of object cgroups */ 6962306a36Sopenharmony_ci#endif 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci int nr_pages; /* # of pages served by this chunk */ 7262306a36Sopenharmony_ci int nr_populated; /* # of populated pages */ 7362306a36Sopenharmony_ci int nr_empty_pop_pages; /* # of empty populated pages */ 7462306a36Sopenharmony_ci unsigned long populated[]; /* populated bitmap */ 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciextern spinlock_t pcpu_lock; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciextern struct list_head *pcpu_chunk_lists; 8062306a36Sopenharmony_ciextern int pcpu_nr_slots; 8162306a36Sopenharmony_ciextern int pcpu_sidelined_slot; 8262306a36Sopenharmony_ciextern int pcpu_to_depopulate_slot; 8362306a36Sopenharmony_ciextern int pcpu_nr_empty_pop_pages; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciextern struct pcpu_chunk *pcpu_first_chunk; 8662306a36Sopenharmony_ciextern struct pcpu_chunk *pcpu_reserved_chunk; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * pcpu_chunk_nr_blocks - converts nr_pages to # of md_blocks 9062306a36Sopenharmony_ci * @chunk: chunk of interest 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * This conversion is from the number of physical pages that the chunk 9362306a36Sopenharmony_ci * serves to the number of bitmap blocks used. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic inline int pcpu_chunk_nr_blocks(struct pcpu_chunk *chunk) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return chunk->nr_pages * PAGE_SIZE / PCPU_BITMAP_BLOCK_SIZE; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * pcpu_nr_pages_to_map_bits - converts the pages to size of bitmap 10262306a36Sopenharmony_ci * @pages: number of physical pages 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * This conversion is from physical pages to the number of bits 10562306a36Sopenharmony_ci * required in the bitmap. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic inline int pcpu_nr_pages_to_map_bits(int pages) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return pages * PAGE_SIZE / PCPU_MIN_ALLOC_SIZE; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * pcpu_chunk_map_bits - helper to convert nr_pages to size of bitmap 11462306a36Sopenharmony_ci * @chunk: chunk of interest 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * This conversion is from the number of physical pages that the chunk 11762306a36Sopenharmony_ci * serves to the number of bits in the bitmap. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistatic inline int pcpu_chunk_map_bits(struct pcpu_chunk *chunk) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return pcpu_nr_pages_to_map_bits(chunk->nr_pages); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * pcpu_obj_full_size - helper to calculate size of each accounted object 12662306a36Sopenharmony_ci * @size: size of area to allocate in bytes 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * For each accounted object there is an extra space which is used to store 12962306a36Sopenharmony_ci * obj_cgroup membership if kmemcg is not disabled. Charge it too. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic inline size_t pcpu_obj_full_size(size_t size) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci size_t extra_size = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM 13662306a36Sopenharmony_ci if (!mem_cgroup_kmem_disabled()) 13762306a36Sopenharmony_ci extra_size += size / PCPU_MIN_ALLOC_SIZE * sizeof(struct obj_cgroup *); 13862306a36Sopenharmony_ci#endif 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return size * num_possible_cpus() + extra_size; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#ifdef CONFIG_PERCPU_STATS 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#include <linux/spinlock.h> 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistruct percpu_stats { 14862306a36Sopenharmony_ci u64 nr_alloc; /* lifetime # of allocations */ 14962306a36Sopenharmony_ci u64 nr_dealloc; /* lifetime # of deallocations */ 15062306a36Sopenharmony_ci u64 nr_cur_alloc; /* current # of allocations */ 15162306a36Sopenharmony_ci u64 nr_max_alloc; /* max # of live allocations */ 15262306a36Sopenharmony_ci u32 nr_chunks; /* current # of live chunks */ 15362306a36Sopenharmony_ci u32 nr_max_chunks; /* max # of live chunks */ 15462306a36Sopenharmony_ci size_t min_alloc_size; /* min allocation size */ 15562306a36Sopenharmony_ci size_t max_alloc_size; /* max allocation size */ 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciextern struct percpu_stats pcpu_stats; 15962306a36Sopenharmony_ciextern struct pcpu_alloc_info pcpu_stats_ai; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * For debug purposes. We don't care about the flexible array. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic inline void pcpu_stats_save_ai(const struct pcpu_alloc_info *ai) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci memcpy(&pcpu_stats_ai, ai, sizeof(struct pcpu_alloc_info)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* initialize min_alloc_size to unit_size */ 16962306a36Sopenharmony_ci pcpu_stats.min_alloc_size = pcpu_stats_ai.unit_size; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* 17362306a36Sopenharmony_ci * pcpu_stats_area_alloc - increment area allocation stats 17462306a36Sopenharmony_ci * @chunk: the location of the area being allocated 17562306a36Sopenharmony_ci * @size: size of area to allocate in bytes 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * CONTEXT: 17862306a36Sopenharmony_ci * pcpu_lock. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic inline void pcpu_stats_area_alloc(struct pcpu_chunk *chunk, size_t size) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci lockdep_assert_held(&pcpu_lock); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci pcpu_stats.nr_alloc++; 18562306a36Sopenharmony_ci pcpu_stats.nr_cur_alloc++; 18662306a36Sopenharmony_ci pcpu_stats.nr_max_alloc = 18762306a36Sopenharmony_ci max(pcpu_stats.nr_max_alloc, pcpu_stats.nr_cur_alloc); 18862306a36Sopenharmony_ci pcpu_stats.min_alloc_size = 18962306a36Sopenharmony_ci min(pcpu_stats.min_alloc_size, size); 19062306a36Sopenharmony_ci pcpu_stats.max_alloc_size = 19162306a36Sopenharmony_ci max(pcpu_stats.max_alloc_size, size); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci chunk->nr_alloc++; 19462306a36Sopenharmony_ci chunk->max_alloc_size = max(chunk->max_alloc_size, size); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * pcpu_stats_area_dealloc - decrement allocation stats 19962306a36Sopenharmony_ci * @chunk: the location of the area being deallocated 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * CONTEXT: 20262306a36Sopenharmony_ci * pcpu_lock. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_cistatic inline void pcpu_stats_area_dealloc(struct pcpu_chunk *chunk) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci lockdep_assert_held(&pcpu_lock); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci pcpu_stats.nr_dealloc++; 20962306a36Sopenharmony_ci pcpu_stats.nr_cur_alloc--; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci chunk->nr_alloc--; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * pcpu_stats_chunk_alloc - increment chunk stats 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic inline void pcpu_stats_chunk_alloc(void) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci unsigned long flags; 22062306a36Sopenharmony_ci spin_lock_irqsave(&pcpu_lock, flags); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci pcpu_stats.nr_chunks++; 22362306a36Sopenharmony_ci pcpu_stats.nr_max_chunks = 22462306a36Sopenharmony_ci max(pcpu_stats.nr_max_chunks, pcpu_stats.nr_chunks); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci spin_unlock_irqrestore(&pcpu_lock, flags); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * pcpu_stats_chunk_dealloc - decrement chunk stats 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic inline void pcpu_stats_chunk_dealloc(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci unsigned long flags; 23562306a36Sopenharmony_ci spin_lock_irqsave(&pcpu_lock, flags); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pcpu_stats.nr_chunks--; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci spin_unlock_irqrestore(&pcpu_lock, flags); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci#else 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic inline void pcpu_stats_save_ai(const struct pcpu_alloc_info *ai) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic inline void pcpu_stats_area_alloc(struct pcpu_chunk *chunk, size_t size) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic inline void pcpu_stats_area_dealloc(struct pcpu_chunk *chunk) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic inline void pcpu_stats_chunk_alloc(void) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic inline void pcpu_stats_chunk_dealloc(void) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci#endif /* !CONFIG_PERCPU_STATS */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#endif 267