]> git.karo-electronics.de Git - karo-tx-linux.git/blob - mm/percpu-stats.c
xen/grant-table: log the lack of grants
[karo-tx-linux.git] / mm / percpu-stats.c
1 /*
2  * mm/percpu-debug.c
3  *
4  * Copyright (C) 2017           Facebook Inc.
5  * Copyright (C) 2017           Dennis Zhou <dennisz@fb.com>
6  *
7  * This file is released under the GPLv2.
8  *
9  * Prints statistics about the percpu allocator and backing chunks.
10  */
11 #include <linux/debugfs.h>
12 #include <linux/list.h>
13 #include <linux/percpu.h>
14 #include <linux/seq_file.h>
15 #include <linux/sort.h>
16 #include <linux/vmalloc.h>
17
18 #include "percpu-internal.h"
19
20 #define P(X, Y) \
21         seq_printf(m, "  %-24s: %8lld\n", X, (long long int)Y)
22
23 struct percpu_stats pcpu_stats;
24 struct pcpu_alloc_info pcpu_stats_ai;
25
26 static int cmpint(const void *a, const void *b)
27 {
28         return *(int *)a - *(int *)b;
29 }
30
31 /*
32  * Iterates over all chunks to find the max # of map entries used.
33  */
34 static int find_max_map_used(void)
35 {
36         struct pcpu_chunk *chunk;
37         int slot, max_map_used;
38
39         max_map_used = 0;
40         for (slot = 0; slot < pcpu_nr_slots; slot++)
41                 list_for_each_entry(chunk, &pcpu_slot[slot], list)
42                         max_map_used = max(max_map_used, chunk->map_used);
43
44         return max_map_used;
45 }
46
47 /*
48  * Prints out chunk state. Fragmentation is considered between
49  * the beginning of the chunk to the last allocation.
50  */
51 static void chunk_map_stats(struct seq_file *m, struct pcpu_chunk *chunk,
52                             void *buffer)
53 {
54         int i, s_index, last_alloc, alloc_sign, as_len;
55         int *alloc_sizes, *p;
56         /* statistics */
57         int sum_frag = 0, max_frag = 0;
58         int cur_min_alloc = 0, cur_med_alloc = 0, cur_max_alloc = 0;
59
60         alloc_sizes = buffer;
61         s_index = chunk->has_reserved ? 1 : 0;
62
63         /* find last allocation */
64         last_alloc = -1;
65         for (i = chunk->map_used - 1; i >= s_index; i--) {
66                 if (chunk->map[i] & 1) {
67                         last_alloc = i;
68                         break;
69                 }
70         }
71
72         /* if the chunk is not empty - ignoring reserve */
73         if (last_alloc >= s_index) {
74                 as_len = last_alloc + 1 - s_index;
75
76                 /*
77                  * Iterate through chunk map computing size info.
78                  * The first bit is overloaded to be a used flag.
79                  * negative = free space, positive = allocated
80                  */
81                 for (i = 0, p = chunk->map + s_index; i < as_len; i++, p++) {
82                         alloc_sign = (*p & 1) ? 1 : -1;
83                         alloc_sizes[i] = alloc_sign *
84                                 ((p[1] & ~1) - (p[0] & ~1));
85                 }
86
87                 sort(alloc_sizes, as_len, sizeof(chunk->map[0]), cmpint, NULL);
88
89                 /* Iterate through the unallocated fragements. */
90                 for (i = 0, p = alloc_sizes; *p < 0 && i < as_len; i++, p++) {
91                         sum_frag -= *p;
92                         max_frag = max(max_frag, -1 * (*p));
93                 }
94
95                 cur_min_alloc = alloc_sizes[i];
96                 cur_med_alloc = alloc_sizes[(i + as_len - 1) / 2];
97                 cur_max_alloc = alloc_sizes[as_len - 1];
98         }
99
100         P("nr_alloc", chunk->nr_alloc);
101         P("max_alloc_size", chunk->max_alloc_size);
102         P("free_size", chunk->free_size);
103         P("contig_hint", chunk->contig_hint);
104         P("sum_frag", sum_frag);
105         P("max_frag", max_frag);
106         P("cur_min_alloc", cur_min_alloc);
107         P("cur_med_alloc", cur_med_alloc);
108         P("cur_max_alloc", cur_max_alloc);
109         seq_putc(m, '\n');
110 }
111
112 static int percpu_stats_show(struct seq_file *m, void *v)
113 {
114         struct pcpu_chunk *chunk;
115         int slot, max_map_used;
116         void *buffer;
117
118 alloc_buffer:
119         spin_lock_irq(&pcpu_lock);
120         max_map_used = find_max_map_used();
121         spin_unlock_irq(&pcpu_lock);
122
123         buffer = vmalloc(max_map_used * sizeof(pcpu_first_chunk->map[0]));
124         if (!buffer)
125                 return -ENOMEM;
126
127         spin_lock_irq(&pcpu_lock);
128
129         /* if the buffer allocated earlier is too small */
130         if (max_map_used < find_max_map_used()) {
131                 spin_unlock_irq(&pcpu_lock);
132                 vfree(buffer);
133                 goto alloc_buffer;
134         }
135
136 #define PL(X) \
137         seq_printf(m, "  %-24s: %8lld\n", #X, (long long int)pcpu_stats_ai.X)
138
139         seq_printf(m,
140                         "Percpu Memory Statistics\n"
141                         "Allocation Info:\n"
142                         "----------------------------------------\n");
143         PL(unit_size);
144         PL(static_size);
145         PL(reserved_size);
146         PL(dyn_size);
147         PL(atom_size);
148         PL(alloc_size);
149         seq_putc(m, '\n');
150
151 #undef PL
152
153 #define PU(X) \
154         seq_printf(m, "  %-18s: %14llu\n", #X, (unsigned long long)pcpu_stats.X)
155
156         seq_printf(m,
157                         "Global Stats:\n"
158                         "----------------------------------------\n");
159         PU(nr_alloc);
160         PU(nr_dealloc);
161         PU(nr_cur_alloc);
162         PU(nr_max_alloc);
163         PU(nr_chunks);
164         PU(nr_max_chunks);
165         PU(min_alloc_size);
166         PU(max_alloc_size);
167         seq_putc(m, '\n');
168
169 #undef PU
170
171         seq_printf(m,
172                         "Per Chunk Stats:\n"
173                         "----------------------------------------\n");
174
175         if (pcpu_reserved_chunk) {
176                 seq_puts(m, "Chunk: <- Reserved Chunk\n");
177                 chunk_map_stats(m, pcpu_reserved_chunk, buffer);
178         }
179
180         for (slot = 0; slot < pcpu_nr_slots; slot++) {
181                 list_for_each_entry(chunk, &pcpu_slot[slot], list) {
182                         if (chunk == pcpu_first_chunk) {
183                                 seq_puts(m, "Chunk: <- First Chunk\n");
184                                 chunk_map_stats(m, chunk, buffer);
185
186
187                         } else {
188                                 seq_puts(m, "Chunk:\n");
189                                 chunk_map_stats(m, chunk, buffer);
190                         }
191
192                 }
193         }
194
195         spin_unlock_irq(&pcpu_lock);
196
197         vfree(buffer);
198
199         return 0;
200 }
201
202 static int percpu_stats_open(struct inode *inode, struct file *filp)
203 {
204         return single_open(filp, percpu_stats_show, NULL);
205 }
206
207 static const struct file_operations percpu_stats_fops = {
208         .open           = percpu_stats_open,
209         .read           = seq_read,
210         .llseek         = seq_lseek,
211         .release        = single_release,
212 };
213
214 static int __init init_percpu_stats_debugfs(void)
215 {
216         debugfs_create_file("percpu_stats", 0444, NULL, NULL,
217                         &percpu_stats_fops);
218
219         return 0;
220 }
221
222 late_initcall(init_percpu_stats_debugfs);