Blame view
mm/percpu-stats.c
6.04 KB
55716d264 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
30a5b5367 percpu: expose st... |
2 3 4 5 |
/* * mm/percpu-debug.c * * Copyright (C) 2017 Facebook Inc. |
bfacd38f8 percpu: update co... |
6 |
* Copyright (C) 2017 Dennis Zhou <dennis@kernel.org> |
30a5b5367 percpu: expose st... |
7 |
* |
30a5b5367 percpu: expose st... |
8 9 10 11 12 13 14 15 16 17 18 19 |
* Prints statistics about the percpu allocator and backing chunks. */ #include <linux/debugfs.h> #include <linux/list.h> #include <linux/percpu.h> #include <linux/seq_file.h> #include <linux/sort.h> #include <linux/vmalloc.h> #include "percpu-internal.h" #define P(X, Y) \ |
02459164a percpu: change th... |
20 21 |
seq_printf(m, " %-20s: %12lld ", X, (long long int)Y) |
30a5b5367 percpu: expose st... |
22 23 24 25 26 27 28 29 30 31 |
struct percpu_stats pcpu_stats; struct pcpu_alloc_info pcpu_stats_ai; static int cmpint(const void *a, const void *b) { return *(int *)a - *(int *)b; } /* |
40064aeca percpu: replace a... |
32 |
* Iterates over all chunks to find the max nr_alloc entries. |
30a5b5367 percpu: expose st... |
33 |
*/ |
40064aeca percpu: replace a... |
34 |
static int find_max_nr_alloc(void) |
30a5b5367 percpu: expose st... |
35 36 |
{ struct pcpu_chunk *chunk; |
40064aeca percpu: replace a... |
37 |
int slot, max_nr_alloc; |
3c7be18ac mm: memcg/percpu:... |
38 |
enum pcpu_chunk_type type; |
30a5b5367 percpu: expose st... |
39 |
|
40064aeca percpu: replace a... |
40 |
max_nr_alloc = 0; |
3c7be18ac mm: memcg/percpu:... |
41 42 43 44 45 46 |
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++) for (slot = 0; slot < pcpu_nr_slots; slot++) list_for_each_entry(chunk, &pcpu_chunk_list(type)[slot], list) max_nr_alloc = max(max_nr_alloc, chunk->nr_alloc); |
30a5b5367 percpu: expose st... |
47 |
|
40064aeca percpu: replace a... |
48 |
return max_nr_alloc; |
30a5b5367 percpu: expose st... |
49 50 51 52 53 |
} /* * Prints out chunk state. Fragmentation is considered between * the beginning of the chunk to the last allocation. |
40064aeca percpu: replace a... |
54 55 |
* * All statistics are in bytes unless stated otherwise. |
30a5b5367 percpu: expose st... |
56 57 |
*/ static void chunk_map_stats(struct seq_file *m, struct pcpu_chunk *chunk, |
cd6a884d0 percpu: pcpu-stat... |
58 |
int *buffer) |
30a5b5367 percpu: expose st... |
59 |
{ |
92c14cab4 percpu: convert c... |
60 |
struct pcpu_block_md *chunk_md = &chunk->chunk_md; |
40064aeca percpu: replace a... |
61 |
int i, last_alloc, as_len, start, end; |
30a5b5367 percpu: expose st... |
62 63 64 65 66 67 |
int *alloc_sizes, *p; /* statistics */ int sum_frag = 0, max_frag = 0; int cur_min_alloc = 0, cur_med_alloc = 0, cur_max_alloc = 0; alloc_sizes = buffer; |
30a5b5367 percpu: expose st... |
68 |
|
40064aeca percpu: replace a... |
69 70 71 72 73 74 75 76 77 78 79 80 |
/* * find_last_bit returns the start value if nothing found. * Therefore, we must determine if it is a failure of find_last_bit * and set the appropriate value. */ last_alloc = find_last_bit(chunk->alloc_map, pcpu_chunk_map_bits(chunk) - chunk->end_offset / PCPU_MIN_ALLOC_SIZE - 1); last_alloc = test_bit(last_alloc, chunk->alloc_map) ? last_alloc + 1 : 0; as_len = 0; |
2e08d20d7 percpu: fix start... |
81 |
start = chunk->start_offset / PCPU_MIN_ALLOC_SIZE; |
40064aeca percpu: replace a... |
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
/* * If a bit is set in the allocation map, the bound_map identifies * where the allocation ends. If the allocation is not set, the * bound_map does not identify free areas as it is only kept accurate * on allocation, not free. * * Positive values are allocations and negative values are free * fragments. */ while (start < last_alloc) { if (test_bit(start, chunk->alloc_map)) { end = find_next_bit(chunk->bound_map, last_alloc, start + 1); alloc_sizes[as_len] = 1; } else { end = find_next_bit(chunk->alloc_map, last_alloc, start + 1); alloc_sizes[as_len] = -1; |
30a5b5367 percpu: expose st... |
101 |
} |
40064aeca percpu: replace a... |
102 103 104 105 106 107 108 109 110 111 112 |
alloc_sizes[as_len++] *= (end - start) * PCPU_MIN_ALLOC_SIZE; start = end; } /* * The negative values are free fragments and thus sorting gives the * free fragments at the beginning in largest first order. */ if (as_len > 0) { sort(alloc_sizes, as_len, sizeof(int), cmpint, NULL); |
30a5b5367 percpu: expose st... |
113 |
|
40064aeca percpu: replace a... |
114 |
/* iterate through the unallocated fragments */ |
30a5b5367 percpu: expose st... |
115 116 117 118 119 120 121 122 123 124 125 126 |
for (i = 0, p = alloc_sizes; *p < 0 && i < as_len; i++, p++) { sum_frag -= *p; max_frag = max(max_frag, -1 * (*p)); } cur_min_alloc = alloc_sizes[i]; cur_med_alloc = alloc_sizes[(i + as_len - 1) / 2]; cur_max_alloc = alloc_sizes[as_len - 1]; } P("nr_alloc", chunk->nr_alloc); P("max_alloc_size", chunk->max_alloc_size); |
0cecf50cf percpu: introduce... |
127 |
P("empty_pop_pages", chunk->nr_empty_pop_pages); |
92c14cab4 percpu: convert c... |
128 |
P("first_bit", chunk_md->first_free); |
40064aeca percpu: replace a... |
129 |
P("free_bytes", chunk->free_bytes); |
92c14cab4 percpu: convert c... |
130 |
P("contig_bytes", chunk_md->contig_hint * PCPU_MIN_ALLOC_SIZE); |
30a5b5367 percpu: expose st... |
131 132 133 134 135 |
P("sum_frag", sum_frag); P("max_frag", max_frag); P("cur_min_alloc", cur_min_alloc); P("cur_med_alloc", cur_med_alloc); P("cur_max_alloc", cur_max_alloc); |
3c7be18ac mm: memcg/percpu:... |
136 137 138 |
#ifdef CONFIG_MEMCG_KMEM P("memcg_aware", pcpu_is_memcg_chunk(pcpu_chunk_type(chunk))); #endif |
30a5b5367 percpu: expose st... |
139 140 141 142 143 144 145 |
seq_putc(m, ' '); } static int percpu_stats_show(struct seq_file *m, void *v) { struct pcpu_chunk *chunk; |
40064aeca percpu: replace a... |
146 |
int slot, max_nr_alloc; |
cd6a884d0 percpu: pcpu-stat... |
147 |
int *buffer; |
3c7be18ac mm: memcg/percpu:... |
148 |
enum pcpu_chunk_type type; |
30a5b5367 percpu: expose st... |
149 150 151 |
alloc_buffer: spin_lock_irq(&pcpu_lock); |
40064aeca percpu: replace a... |
152 |
max_nr_alloc = find_max_nr_alloc(); |
30a5b5367 percpu: expose st... |
153 |
spin_unlock_irq(&pcpu_lock); |
40064aeca percpu: replace a... |
154 |
/* there can be at most this many free and allocated fragments */ |
42bc47b35 treewide: Use arr... |
155 |
buffer = vmalloc(array_size(sizeof(int), (2 * max_nr_alloc + 1))); |
30a5b5367 percpu: expose st... |
156 157 158 159 160 161 |
if (!buffer) return -ENOMEM; spin_lock_irq(&pcpu_lock); /* if the buffer allocated earlier is too small */ |
40064aeca percpu: replace a... |
162 |
if (max_nr_alloc < find_max_nr_alloc()) { |
30a5b5367 percpu: expose st... |
163 164 165 166 167 168 |
spin_unlock_irq(&pcpu_lock); vfree(buffer); goto alloc_buffer; } #define PL(X) \ |
02459164a percpu: change th... |
169 170 |
seq_printf(m, " %-20s: %12lld ", #X, (long long int)pcpu_stats_ai.X) |
30a5b5367 percpu: expose st... |
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
seq_printf(m, "Percpu Memory Statistics " "Allocation Info: " "---------------------------------------- "); PL(unit_size); PL(static_size); PL(reserved_size); PL(dyn_size); PL(atom_size); PL(alloc_size); seq_putc(m, ' '); #undef PL #define PU(X) \ |
02459164a percpu: change th... |
191 192 |
seq_printf(m, " %-20s: %12llu ", #X, (unsigned long long)pcpu_stats.X) |
30a5b5367 percpu: expose st... |
193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
seq_printf(m, "Global Stats: " "---------------------------------------- "); PU(nr_alloc); PU(nr_dealloc); PU(nr_cur_alloc); PU(nr_max_alloc); PU(nr_chunks); PU(nr_max_chunks); PU(min_alloc_size); PU(max_alloc_size); |
6b9b6f399 percpu: expose pc... |
207 |
P("empty_pop_pages", pcpu_nr_empty_pop_pages); |
30a5b5367 percpu: expose st... |
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
seq_putc(m, ' '); #undef PU seq_printf(m, "Per Chunk Stats: " "---------------------------------------- "); if (pcpu_reserved_chunk) { seq_puts(m, "Chunk: <- Reserved Chunk "); chunk_map_stats(m, pcpu_reserved_chunk, buffer); } |
3c7be18ac mm: memcg/percpu:... |
224 225 226 227 228 229 230 231 232 233 234 235 236 |
for (type = 0; type < PCPU_NR_CHUNK_TYPES; type++) { for (slot = 0; slot < pcpu_nr_slots; slot++) { list_for_each_entry(chunk, &pcpu_chunk_list(type)[slot], list) { if (chunk == pcpu_first_chunk) { seq_puts(m, "Chunk: <- First Chunk "); chunk_map_stats(m, chunk, buffer); } else { seq_puts(m, "Chunk: "); chunk_map_stats(m, chunk, buffer); } |
30a5b5367 percpu: expose st... |
237 |
} |
30a5b5367 percpu: expose st... |
238 239 240 241 242 243 244 245 246 |
} } spin_unlock_irq(&pcpu_lock); vfree(buffer); return 0; } |
5ad350936 mm: reuse DEFINE_... |
247 |
DEFINE_SHOW_ATTRIBUTE(percpu_stats); |
30a5b5367 percpu: expose st... |
248 249 250 251 252 253 254 255 256 257 |
static int __init init_percpu_stats_debugfs(void) { debugfs_create_file("percpu_stats", 0444, NULL, NULL, &percpu_stats_fops); return 0; } late_initcall(init_percpu_stats_debugfs); |