xref: /openbmc/linux/mm/percpu-stats.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
230a5b536SDennis Zhou /*
330a5b536SDennis Zhou  * mm/percpu-debug.c
430a5b536SDennis Zhou  *
530a5b536SDennis Zhou  * Copyright (C) 2017		Facebook Inc.
6bfacd38fSDennis Zhou  * Copyright (C) 2017		Dennis Zhou <dennis@kernel.org>
730a5b536SDennis Zhou  *
830a5b536SDennis Zhou  * Prints statistics about the percpu allocator and backing chunks.
930a5b536SDennis Zhou  */
1030a5b536SDennis Zhou #include <linux/debugfs.h>
1130a5b536SDennis Zhou #include <linux/list.h>
1230a5b536SDennis Zhou #include <linux/percpu.h>
1330a5b536SDennis Zhou #include <linux/seq_file.h>
1430a5b536SDennis Zhou #include <linux/sort.h>
1530a5b536SDennis Zhou #include <linux/vmalloc.h>
1630a5b536SDennis Zhou 
1730a5b536SDennis Zhou #include "percpu-internal.h"
1830a5b536SDennis Zhou 
1930a5b536SDennis Zhou #define P(X, Y) \
2002459164SDennis Zhou (Facebook) 	seq_printf(m, "  %-20s: %12lld\n", X, (long long int)Y)
2130a5b536SDennis Zhou 
2230a5b536SDennis Zhou struct percpu_stats pcpu_stats;
2330a5b536SDennis Zhou struct pcpu_alloc_info pcpu_stats_ai;
2430a5b536SDennis Zhou 
cmpint(const void * a,const void * b)2530a5b536SDennis Zhou static int cmpint(const void *a, const void *b)
2630a5b536SDennis Zhou {
2730a5b536SDennis Zhou 	return *(int *)a - *(int *)b;
2830a5b536SDennis Zhou }
2930a5b536SDennis Zhou 
3030a5b536SDennis Zhou /*
3140064aecSDennis Zhou (Facebook)  * Iterates over all chunks to find the max nr_alloc entries.
3230a5b536SDennis Zhou  */
find_max_nr_alloc(void)3340064aecSDennis Zhou (Facebook) static int find_max_nr_alloc(void)
3430a5b536SDennis Zhou {
3530a5b536SDennis Zhou 	struct pcpu_chunk *chunk;
3640064aecSDennis Zhou (Facebook) 	int slot, max_nr_alloc;
3730a5b536SDennis Zhou 
3840064aecSDennis Zhou (Facebook) 	max_nr_alloc = 0;
3930a5b536SDennis Zhou 	for (slot = 0; slot < pcpu_nr_slots; slot++)
40faf65ddeSRoman Gushchin 		list_for_each_entry(chunk, &pcpu_chunk_lists[slot], list)
41faf65ddeSRoman Gushchin 			max_nr_alloc = max(max_nr_alloc, chunk->nr_alloc);
4230a5b536SDennis Zhou 
4340064aecSDennis Zhou (Facebook) 	return max_nr_alloc;
4430a5b536SDennis Zhou }
4530a5b536SDennis Zhou 
4630a5b536SDennis Zhou /*
4730a5b536SDennis Zhou  * Prints out chunk state. Fragmentation is considered between
4830a5b536SDennis Zhou  * the beginning of the chunk to the last allocation.
4940064aecSDennis Zhou (Facebook)  *
5040064aecSDennis Zhou (Facebook)  * All statistics are in bytes unless stated otherwise.
5130a5b536SDennis Zhou  */
chunk_map_stats(struct seq_file * m,struct pcpu_chunk * chunk,int * buffer)5230a5b536SDennis Zhou static void chunk_map_stats(struct seq_file *m, struct pcpu_chunk *chunk,
53cd6a884dSDennis Zhou (Facebook) 			    int *buffer)
5430a5b536SDennis Zhou {
5592c14cabSDennis Zhou 	struct pcpu_block_md *chunk_md = &chunk->chunk_md;
5640064aecSDennis Zhou (Facebook) 	int i, last_alloc, as_len, start, end;
5730a5b536SDennis Zhou 	int *alloc_sizes, *p;
5830a5b536SDennis Zhou 	/* statistics */
5930a5b536SDennis Zhou 	int sum_frag = 0, max_frag = 0;
6030a5b536SDennis Zhou 	int cur_min_alloc = 0, cur_med_alloc = 0, cur_max_alloc = 0;
6130a5b536SDennis Zhou 
6230a5b536SDennis Zhou 	alloc_sizes = buffer;
6330a5b536SDennis Zhou 
6430a5b536SDennis Zhou 	/*
6540064aecSDennis Zhou (Facebook) 	 * find_last_bit returns the start value if nothing found.
6640064aecSDennis Zhou (Facebook) 	 * Therefore, we must determine if it is a failure of find_last_bit
6740064aecSDennis Zhou (Facebook) 	 * and set the appropriate value.
6830a5b536SDennis Zhou 	 */
6940064aecSDennis Zhou (Facebook) 	last_alloc = find_last_bit(chunk->alloc_map,
7040064aecSDennis Zhou (Facebook) 				   pcpu_chunk_map_bits(chunk) -
7140064aecSDennis Zhou (Facebook) 				   chunk->end_offset / PCPU_MIN_ALLOC_SIZE - 1);
7240064aecSDennis Zhou (Facebook) 	last_alloc = test_bit(last_alloc, chunk->alloc_map) ?
7340064aecSDennis Zhou (Facebook) 		     last_alloc + 1 : 0;
7440064aecSDennis Zhou (Facebook) 
7540064aecSDennis Zhou (Facebook) 	as_len = 0;
762e08d20dSDennis Zhou 	start = chunk->start_offset / PCPU_MIN_ALLOC_SIZE;
7740064aecSDennis Zhou (Facebook) 
7840064aecSDennis Zhou (Facebook) 	/*
7940064aecSDennis Zhou (Facebook) 	 * If a bit is set in the allocation map, the bound_map identifies
8040064aecSDennis Zhou (Facebook) 	 * where the allocation ends.  If the allocation is not set, the
8140064aecSDennis Zhou (Facebook) 	 * bound_map does not identify free areas as it is only kept accurate
8240064aecSDennis Zhou (Facebook) 	 * on allocation, not free.
8340064aecSDennis Zhou (Facebook) 	 *
8440064aecSDennis Zhou (Facebook) 	 * Positive values are allocations and negative values are free
8540064aecSDennis Zhou (Facebook) 	 * fragments.
8640064aecSDennis Zhou (Facebook) 	 */
8740064aecSDennis Zhou (Facebook) 	while (start < last_alloc) {
8840064aecSDennis Zhou (Facebook) 		if (test_bit(start, chunk->alloc_map)) {
8940064aecSDennis Zhou (Facebook) 			end = find_next_bit(chunk->bound_map, last_alloc,
9040064aecSDennis Zhou (Facebook) 					    start + 1);
9140064aecSDennis Zhou (Facebook) 			alloc_sizes[as_len] = 1;
9240064aecSDennis Zhou (Facebook) 		} else {
9340064aecSDennis Zhou (Facebook) 			end = find_next_bit(chunk->alloc_map, last_alloc,
9440064aecSDennis Zhou (Facebook) 					    start + 1);
9540064aecSDennis Zhou (Facebook) 			alloc_sizes[as_len] = -1;
9630a5b536SDennis Zhou 		}
9730a5b536SDennis Zhou 
9840064aecSDennis Zhou (Facebook) 		alloc_sizes[as_len++] *= (end - start) * PCPU_MIN_ALLOC_SIZE;
9930a5b536SDennis Zhou 
10040064aecSDennis Zhou (Facebook) 		start = end;
10140064aecSDennis Zhou (Facebook) 	}
10240064aecSDennis Zhou (Facebook) 
10340064aecSDennis Zhou (Facebook) 	/*
10440064aecSDennis Zhou (Facebook) 	 * The negative values are free fragments and thus sorting gives the
10540064aecSDennis Zhou (Facebook) 	 * free fragments at the beginning in largest first order.
10640064aecSDennis Zhou (Facebook) 	 */
10740064aecSDennis Zhou (Facebook) 	if (as_len > 0) {
10840064aecSDennis Zhou (Facebook) 		sort(alloc_sizes, as_len, sizeof(int), cmpint, NULL);
10940064aecSDennis Zhou (Facebook) 
11040064aecSDennis Zhou (Facebook) 		/* iterate through the unallocated fragments */
11130a5b536SDennis Zhou 		for (i = 0, p = alloc_sizes; *p < 0 && i < as_len; i++, p++) {
11230a5b536SDennis Zhou 			sum_frag -= *p;
11330a5b536SDennis Zhou 			max_frag = max(max_frag, -1 * (*p));
11430a5b536SDennis Zhou 		}
11530a5b536SDennis Zhou 
11630a5b536SDennis Zhou 		cur_min_alloc = alloc_sizes[i];
11730a5b536SDennis Zhou 		cur_med_alloc = alloc_sizes[(i + as_len - 1) / 2];
11830a5b536SDennis Zhou 		cur_max_alloc = alloc_sizes[as_len - 1];
11930a5b536SDennis Zhou 	}
12030a5b536SDennis Zhou 
12130a5b536SDennis Zhou 	P("nr_alloc", chunk->nr_alloc);
12230a5b536SDennis Zhou 	P("max_alloc_size", chunk->max_alloc_size);
1230cecf50cSDennis Zhou (Facebook) 	P("empty_pop_pages", chunk->nr_empty_pop_pages);
12492c14cabSDennis Zhou 	P("first_bit", chunk_md->first_free);
12540064aecSDennis Zhou (Facebook) 	P("free_bytes", chunk->free_bytes);
12692c14cabSDennis Zhou 	P("contig_bytes", chunk_md->contig_hint * PCPU_MIN_ALLOC_SIZE);
12730a5b536SDennis Zhou 	P("sum_frag", sum_frag);
12830a5b536SDennis Zhou 	P("max_frag", max_frag);
12930a5b536SDennis Zhou 	P("cur_min_alloc", cur_min_alloc);
13030a5b536SDennis Zhou 	P("cur_med_alloc", cur_med_alloc);
13130a5b536SDennis Zhou 	P("cur_max_alloc", cur_max_alloc);
13230a5b536SDennis Zhou 	seq_putc(m, '\n');
13330a5b536SDennis Zhou }
13430a5b536SDennis Zhou 
percpu_stats_show(struct seq_file * m,void * v)13530a5b536SDennis Zhou static int percpu_stats_show(struct seq_file *m, void *v)
13630a5b536SDennis Zhou {
13730a5b536SDennis Zhou 	struct pcpu_chunk *chunk;
13840064aecSDennis Zhou (Facebook) 	int slot, max_nr_alloc;
139cd6a884dSDennis Zhou (Facebook) 	int *buffer;
14030a5b536SDennis Zhou 
14130a5b536SDennis Zhou alloc_buffer:
14230a5b536SDennis Zhou 	spin_lock_irq(&pcpu_lock);
14340064aecSDennis Zhou (Facebook) 	max_nr_alloc = find_max_nr_alloc();
14430a5b536SDennis Zhou 	spin_unlock_irq(&pcpu_lock);
14530a5b536SDennis Zhou 
14640064aecSDennis Zhou (Facebook) 	/* there can be at most this many free and allocated fragments */
147*3000f2e2SPaolo Bonzini 	buffer = vmalloc_array(2 * max_nr_alloc + 1, sizeof(int));
14830a5b536SDennis Zhou 	if (!buffer)
14930a5b536SDennis Zhou 		return -ENOMEM;
15030a5b536SDennis Zhou 
15130a5b536SDennis Zhou 	spin_lock_irq(&pcpu_lock);
15230a5b536SDennis Zhou 
15330a5b536SDennis Zhou 	/* if the buffer allocated earlier is too small */
15440064aecSDennis Zhou (Facebook) 	if (max_nr_alloc < find_max_nr_alloc()) {
15530a5b536SDennis Zhou 		spin_unlock_irq(&pcpu_lock);
15630a5b536SDennis Zhou 		vfree(buffer);
15730a5b536SDennis Zhou 		goto alloc_buffer;
15830a5b536SDennis Zhou 	}
15930a5b536SDennis Zhou 
16030a5b536SDennis Zhou #define PL(X)								\
16102459164SDennis Zhou (Facebook) 	seq_printf(m, "  %-20s: %12lld\n", #X, (long long int)pcpu_stats_ai.X)
16230a5b536SDennis Zhou 
16330a5b536SDennis Zhou 	seq_printf(m,
16430a5b536SDennis Zhou 			"Percpu Memory Statistics\n"
16530a5b536SDennis Zhou 			"Allocation Info:\n"
16630a5b536SDennis Zhou 			"----------------------------------------\n");
16730a5b536SDennis Zhou 	PL(unit_size);
16830a5b536SDennis Zhou 	PL(static_size);
16930a5b536SDennis Zhou 	PL(reserved_size);
17030a5b536SDennis Zhou 	PL(dyn_size);
17130a5b536SDennis Zhou 	PL(atom_size);
17230a5b536SDennis Zhou 	PL(alloc_size);
17330a5b536SDennis Zhou 	seq_putc(m, '\n');
17430a5b536SDennis Zhou 
17530a5b536SDennis Zhou #undef PL
17630a5b536SDennis Zhou 
17730a5b536SDennis Zhou #define PU(X) \
17802459164SDennis Zhou (Facebook) 	seq_printf(m, "  %-20s: %12llu\n", #X, (unsigned long long)pcpu_stats.X)
17930a5b536SDennis Zhou 
18030a5b536SDennis Zhou 	seq_printf(m,
18130a5b536SDennis Zhou 			"Global Stats:\n"
18230a5b536SDennis Zhou 			"----------------------------------------\n");
18330a5b536SDennis Zhou 	PU(nr_alloc);
18430a5b536SDennis Zhou 	PU(nr_dealloc);
18530a5b536SDennis Zhou 	PU(nr_cur_alloc);
18630a5b536SDennis Zhou 	PU(nr_max_alloc);
18730a5b536SDennis Zhou 	PU(nr_chunks);
18830a5b536SDennis Zhou 	PU(nr_max_chunks);
18930a5b536SDennis Zhou 	PU(min_alloc_size);
19030a5b536SDennis Zhou 	PU(max_alloc_size);
191faf65ddeSRoman Gushchin 	P("empty_pop_pages", pcpu_nr_empty_pop_pages);
19230a5b536SDennis Zhou 	seq_putc(m, '\n');
19330a5b536SDennis Zhou 
19430a5b536SDennis Zhou #undef PU
19530a5b536SDennis Zhou 
19630a5b536SDennis Zhou 	seq_printf(m,
19730a5b536SDennis Zhou 			"Per Chunk Stats:\n"
19830a5b536SDennis Zhou 			"----------------------------------------\n");
19930a5b536SDennis Zhou 
20030a5b536SDennis Zhou 	if (pcpu_reserved_chunk) {
20130a5b536SDennis Zhou 		seq_puts(m, "Chunk: <- Reserved Chunk\n");
20230a5b536SDennis Zhou 		chunk_map_stats(m, pcpu_reserved_chunk, buffer);
20330a5b536SDennis Zhou 	}
20430a5b536SDennis Zhou 
20530a5b536SDennis Zhou 	for (slot = 0; slot < pcpu_nr_slots; slot++) {
206faf65ddeSRoman Gushchin 		list_for_each_entry(chunk, &pcpu_chunk_lists[slot], list) {
207f1833241SRoman Gushchin 			if (chunk == pcpu_first_chunk)
20830a5b536SDennis Zhou 				seq_puts(m, "Chunk: <- First Chunk\n");
209f1833241SRoman Gushchin 			else if (slot == pcpu_to_depopulate_slot)
210f1833241SRoman Gushchin 				seq_puts(m, "Chunk (to_depopulate)\n");
211f1833241SRoman Gushchin 			else if (slot == pcpu_sidelined_slot)
212f1833241SRoman Gushchin 				seq_puts(m, "Chunk (sidelined):\n");
213f1833241SRoman Gushchin 			else
21430a5b536SDennis Zhou 				seq_puts(m, "Chunk:\n");
21530a5b536SDennis Zhou 			chunk_map_stats(m, chunk, buffer);
21630a5b536SDennis Zhou 		}
2173c7be18aSRoman Gushchin 	}
21830a5b536SDennis Zhou 
21930a5b536SDennis Zhou 	spin_unlock_irq(&pcpu_lock);
22030a5b536SDennis Zhou 
22130a5b536SDennis Zhou 	vfree(buffer);
22230a5b536SDennis Zhou 
22330a5b536SDennis Zhou 	return 0;
22430a5b536SDennis Zhou }
2255ad35093SAndy Shevchenko DEFINE_SHOW_ATTRIBUTE(percpu_stats);
22630a5b536SDennis Zhou 
init_percpu_stats_debugfs(void)22730a5b536SDennis Zhou static int __init init_percpu_stats_debugfs(void)
22830a5b536SDennis Zhou {
22930a5b536SDennis Zhou 	debugfs_create_file("percpu_stats", 0444, NULL, NULL,
23030a5b536SDennis Zhou 			&percpu_stats_fops);
23130a5b536SDennis Zhou 
23230a5b536SDennis Zhou 	return 0;
23330a5b536SDennis Zhou }
23430a5b536SDennis Zhou 
23530a5b536SDennis Zhou late_initcall(init_percpu_stats_debugfs);
236