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