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