0e386bcc250e1a0290e9a4dd7fc5711c7eb4ac05
[akaros.git] / kern / drivers / dev / mem.c
1 /* Copyright (c) 2016 Google Inc
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * #mem, memory diagnostics (arenas and slabs)
6  */
7
8 #include <ns.h>
9 #include <kmalloc.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <assert.h>
13 #include <error.h>
14 #include <syscall.h>
15 #include <sys/queue.h>
16
17 struct dev mem_devtab;
18
19 static char *devname(void)
20 {
21         return mem_devtab.name;
22 }
23
24 enum {
25         Qdir,
26         Qarena_stats,
27         Qslab_stats,
28         Qfree,
29         Qkmemstat,
30 };
31
32 static struct dirtab mem_dir[] = {
33         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0555},
34         {"arena_stats", {Qarena_stats, 0, QTFILE}, 0, 0444},
35         {"slab_stats", {Qslab_stats, 0, QTFILE}, 0, 0444},
36         {"free", {Qfree, 0, QTFILE}, 0, 0444},
37         {"kmemstat", {Qkmemstat, 0, QTFILE}, 0, 0444},
38 };
39
40 static struct chan *mem_attach(char *spec)
41 {
42         return devattach(devname(), spec);
43 }
44
45 static struct walkqid *mem_walk(struct chan *c, struct chan *nc, char **name,
46                                                                 unsigned int nname)
47 {
48         return devwalk(c, nc, name, nname, mem_dir, ARRAY_SIZE(mem_dir), devgen);
49 }
50
51 static size_t mem_stat(struct chan *c, uint8_t *db, size_t n)
52 {
53         return devstat(c, db, n, mem_dir, ARRAY_SIZE(mem_dir), devgen);
54 }
55
56 /* Prints arena's stats to the sza, adjusting the sza's sofar. */
57 static void fetch_arena_stats(struct arena *arena, struct sized_alloc *sza)
58 {
59         struct btag *bt_i;
60         struct rb_node *rb_i;
61         struct arena *a_i;
62         struct kmem_cache *kc_i;
63
64         size_t nr_allocs = 0;
65         size_t nr_imports = 0;
66         size_t amt_alloc = 0;
67         size_t amt_free = 0;
68         size_t amt_imported = 0;
69         size_t empty_hash_chain = 0;
70         size_t longest_hash_chain = 0;
71
72         sza_printf(sza, "Arena: %s (%p)\n--------------\n", arena->name, arena);
73         sza_printf(sza, "\tquantum: %d, qcache_max: %d\n", arena->quantum,
74                    arena->qcache_max);
75         sza_printf(sza, "\tsource: %s\n",
76                    arena->source ? arena->source->name : "none");
77         spin_lock_irqsave(&arena->lock);
78         for (int i = 0; i < ARENA_NR_FREE_LISTS; i++) {
79                 int j = 0;
80
81                 if (!BSD_LIST_EMPTY(&arena->free_segs[i])) {
82                         sza_printf(sza, "\tList of [2^%d - 2^%d):\n", i, i + 1);
83                         BSD_LIST_FOREACH(bt_i, &arena->free_segs[i], misc_link)
84                                 j++;
85                         sza_printf(sza, "\t\tNr free segs: %d\n", j);
86                 }
87         }
88         for (int i = 0; i < arena->hh.nr_hash_lists; i++) {
89                 int j = 0;
90
91                 if (BSD_LIST_EMPTY(&arena->alloc_hash[i]))
92                         empty_hash_chain++;
93                 BSD_LIST_FOREACH(bt_i, &arena->alloc_hash[i], misc_link)
94                         j++;
95                 longest_hash_chain = MAX(longest_hash_chain, j);
96         }
97         sza_printf(sza, "\tSegments:\n\t--------------\n");
98         for (rb_i = rb_first(&arena->all_segs); rb_i; rb_i = rb_next(rb_i)) {
99                 bt_i = container_of(rb_i, struct btag, all_link);
100                 if (bt_i->status == BTAG_SPAN) {
101                         nr_imports++;
102                         amt_imported += bt_i->size;
103                 }
104                 if (bt_i->status == BTAG_FREE)
105                         amt_free += bt_i->size;
106                 if (bt_i->status == BTAG_ALLOC) {
107                         nr_allocs++;
108                         amt_alloc += bt_i->size;
109                 }
110         }
111         sza_printf(sza, "\tStats:\n\t-----------------\n");
112         sza_printf(sza, "\t\tAmt free: %llu (%p)\n", amt_free, amt_free);
113         sza_printf(sza, "\t\tAmt alloc: %llu (%p), nr allocs %d\n",
114                    amt_alloc, amt_alloc, nr_allocs);
115         sza_printf(sza, "\t\tAmt total segs: %llu, amt alloc segs %llu\n",
116                    arena->amt_total_segs, arena->amt_alloc_segs);
117         sza_printf(sza, "\t\tAmt imported: %llu (%p), nr imports %d\n",
118                    amt_imported, amt_imported, nr_imports);
119         sza_printf(sza, "\t\tNr hash %d, empty hash: %d, longest hash %d\n",
120                    arena->hh.nr_hash_lists, empty_hash_chain,
121                    longest_hash_chain);
122         spin_unlock_irqsave(&arena->lock);
123         sza_printf(sza, "\tImporting Arenas:\n\t-----------------\n");
124         TAILQ_FOREACH(a_i, &arena->__importing_arenas, import_link)
125                 sza_printf(sza, "\t\t%s\n", a_i->name);
126         sza_printf(sza, "\tImporting Slabs:\n\t-----------------\n");
127         TAILQ_FOREACH(kc_i, &arena->__importing_slabs, import_link)
128                 sza_printf(sza, "\t\t%s\n", kc_i->name);
129 }
130
131 static struct sized_alloc *build_arena_stats(void)
132 {
133         struct sized_alloc *sza;
134         size_t alloc_amt = 0;
135         struct arena *a_i;
136
137         qlock(&arenas_and_slabs_lock);
138         /* Rough guess about how many chars per arena we'll need. */
139         TAILQ_FOREACH(a_i, &all_arenas, next)
140                 alloc_amt += 1000;
141         sza = sized_kzmalloc(alloc_amt, MEM_WAIT);
142         TAILQ_FOREACH(a_i, &all_arenas, next)
143                 fetch_arena_stats(a_i, sza);
144         qunlock(&arenas_and_slabs_lock);
145         return sza;
146 }
147
148 /* Prints arena's stats to the sza, updating its sofar. */
149 static void fetch_slab_stats(struct kmem_cache *kc, struct sized_alloc *sza)
150 {
151         struct kmem_slab *s_i;
152         struct kmem_bufctl *bc_i;
153
154         size_t nr_unalloc_objs = 0;
155         size_t empty_hash_chain = 0;
156         size_t longest_hash_chain = 0;
157
158         spin_lock_irqsave(&kc->cache_lock);
159         sza_printf(sza, "\nKmem_cache: %s\n---------------------\n", kc->name);
160         sza_printf(sza, "Source: %s\n", kc->source->name);
161         sza_printf(sza, "Objsize (incl align): %d\n", kc->obj_size);
162         sza_printf(sza, "Align: %d\n", kc->align);
163         TAILQ_FOREACH(s_i, &kc->empty_slab_list, link) {
164                 assert(!s_i->num_busy_obj);
165                 nr_unalloc_objs += s_i->num_total_obj;
166         }
167         TAILQ_FOREACH(s_i, &kc->partial_slab_list, link)
168                 nr_unalloc_objs += s_i->num_total_obj - s_i->num_busy_obj;
169         sza_printf(sza, "Nr unallocated in slab layer: %lu\n", nr_unalloc_objs);
170         sza_printf(sza, "Nr allocated from slab layer: %d\n", kc->nr_cur_alloc);
171         for (int i = 0; i < kc->hh.nr_hash_lists; i++) {
172                 int j = 0;
173
174                 if (BSD_LIST_EMPTY(&kc->alloc_hash[i]))
175                         empty_hash_chain++;
176                 BSD_LIST_FOREACH(bc_i, &kc->alloc_hash[i], link)
177                         j++;
178                 longest_hash_chain = MAX(longest_hash_chain, j);
179         }
180         sza_printf(sza, "Nr hash %d, empty hash: %d, longest hash %d, loadlim %d\n",
181                    kc->hh.nr_hash_lists, empty_hash_chain,
182                    longest_hash_chain, kc->hh.load_limit);
183         spin_unlock_irqsave(&kc->cache_lock);
184         spin_lock_irqsave(&kc->depot.lock);
185         sza_printf(sza, "Depot magsize: %d\n", kc->depot.magsize);
186         sza_printf(sza, "Nr empty mags: %d\n", kc->depot.nr_empty);
187         sza_printf(sza, "Nr non-empty mags: %d\n", kc->depot.nr_not_empty);
188         spin_unlock_irqsave(&kc->depot.lock);
189 }
190
191 static struct sized_alloc *build_slab_stats(void)
192 {
193         struct sized_alloc *sza;
194         size_t alloc_amt = 0;
195         struct kmem_cache *kc_i;
196
197         qlock(&arenas_and_slabs_lock);
198         TAILQ_FOREACH(kc_i, &all_kmem_caches, all_kmc_link)
199                 alloc_amt += 500;
200         sza = sized_kzmalloc(alloc_amt, MEM_WAIT);
201         TAILQ_FOREACH(kc_i, &all_kmem_caches, all_kmc_link)
202                 fetch_slab_stats(kc_i, sza);
203         qunlock(&arenas_and_slabs_lock);
204         return sza;
205 }
206
207 static struct sized_alloc *build_free(void)
208 {
209         struct arena *a_i;
210         struct sized_alloc *sza;
211         size_t amt_total = 0;
212         size_t amt_alloc = 0;
213
214         sza = sized_kzmalloc(500, MEM_WAIT);
215         qlock(&arenas_and_slabs_lock);
216         TAILQ_FOREACH(a_i, &all_arenas, next) {
217                 if (!a_i->is_base)
218                         continue;
219                 amt_total += a_i->amt_total_segs;
220                 amt_alloc += a_i->amt_alloc_segs;
221         }
222         qunlock(&arenas_and_slabs_lock);
223         sza_printf(sza, "Total Memory : %15llu\n", amt_total);
224         sza_printf(sza, "Used Memory  : %15llu\n", amt_alloc);
225         sza_printf(sza, "Free Memory  : %15llu\n", amt_total - amt_alloc);
226         return sza;
227 }
228
229 #define KMEMSTAT_NAME                   30
230 #define KMEMSTAT_OBJSIZE                8
231 #define KMEMSTAT_TOTAL                  15
232 #define KMEMSTAT_ALLOCED                15
233 #define KMEMSTAT_NR_ALLOCS              12
234 #define KMEMSTAT_LINE_LN (8 + KMEMSTAT_NAME + KMEMSTAT_OBJSIZE + KMEMSTAT_TOTAL\
235                           + KMEMSTAT_ALLOCED + KMEMSTAT_NR_ALLOCS)
236
237 const char kmemstat_fmt[]     = "%-*s: %c :%*llu:%*llu:%*llu:%*llu\n";
238 const char kmemstat_hdr_fmt[] = "%-*s:Typ:%*s:%*s:%*s:%*s\n";
239
240 static void fetch_arena_line(struct arena *arena, struct sized_alloc *sza,
241                              int indent)
242 {
243         for (int i = 0; i < indent; i++)
244                 sza_printf(sza, "    ");
245         sza_printf(sza, kmemstat_fmt,
246                    KMEMSTAT_NAME - indent * 4, arena->name,
247                    'A',
248                    KMEMSTAT_OBJSIZE, arena->quantum,
249                    KMEMSTAT_TOTAL, arena->amt_total_segs,
250                    KMEMSTAT_ALLOCED, arena->amt_alloc_segs,
251                    KMEMSTAT_NR_ALLOCS, arena->nr_allocs_ever);
252 }
253
254 static void fetch_slab_line(struct kmem_cache *kc, struct sized_alloc *sza,
255                             int indent)
256 {
257         struct kmem_pcpu_cache *pcc;
258         struct kmem_slab *s_i;
259         size_t nr_unalloc_objs = 0;
260         size_t nr_allocs_ever = 0;
261
262         spin_lock_irqsave(&kc->cache_lock);
263         TAILQ_FOREACH(s_i, &kc->empty_slab_list, link)
264                 nr_unalloc_objs += s_i->num_total_obj;
265         TAILQ_FOREACH(s_i, &kc->partial_slab_list, link)
266                 nr_unalloc_objs += s_i->num_total_obj - s_i->num_busy_obj;
267         nr_allocs_ever = kc->nr_direct_allocs_ever;
268         spin_unlock_irqsave(&kc->cache_lock);
269         /* Lockless peak at the pcpu state */
270         for (int i = 0; i < kmc_nr_pcpu_caches(); i++) {
271                 pcc = &kc->pcpu_caches[i];
272                 nr_allocs_ever += pcc->nr_allocs_ever;
273         }
274
275         for (int i = 0; i < indent; i++)
276                 sza_printf(sza, "    ");
277         sza_printf(sza, kmemstat_fmt,
278                    KMEMSTAT_NAME - indent * 4, kc->name,
279                    'S',
280                    KMEMSTAT_OBJSIZE, kc->obj_size,
281                    KMEMSTAT_TOTAL, kc->obj_size * (nr_unalloc_objs +
282                                                    kc->nr_cur_alloc),
283                    KMEMSTAT_ALLOCED, kc->obj_size * kc->nr_cur_alloc,
284                    KMEMSTAT_NR_ALLOCS, nr_allocs_ever);
285 }
286
287 static void fetch_arena_and_kids(struct arena *arena, struct sized_alloc *sza,
288                                  int indent)
289 {
290         struct arena *a_i;
291         struct kmem_cache *kc_i;
292
293         fetch_arena_line(arena, sza, indent);
294         TAILQ_FOREACH(a_i, &arena->__importing_arenas, import_link)
295                 fetch_arena_and_kids(a_i, sza, indent + 1);
296         TAILQ_FOREACH(kc_i, &arena->__importing_slabs, import_link)
297                 fetch_slab_line(kc_i, sza, indent + 1);
298 }
299
300 static struct sized_alloc *build_kmemstat(void)
301 {
302         struct arena *a_i;
303         struct kmem_cache *kc_i;
304         struct sized_alloc *sza;
305         size_t alloc_amt = 100;
306
307         qlock(&arenas_and_slabs_lock);
308         TAILQ_FOREACH(a_i, &all_arenas, next)
309                 alloc_amt += 100;
310         TAILQ_FOREACH(kc_i, &all_kmem_caches, all_kmc_link)
311                 alloc_amt += 100;
312         sza = sized_kzmalloc(alloc_amt, MEM_WAIT);
313         sza_printf(sza, kmemstat_hdr_fmt,
314                    KMEMSTAT_NAME, "Arena/Slab Name",
315                    KMEMSTAT_OBJSIZE, "Objsize",
316                    KMEMSTAT_TOTAL, "Total Amt",
317                    KMEMSTAT_ALLOCED, "Alloc Amt",
318                    KMEMSTAT_NR_ALLOCS, "Allocs Ever");
319         for (int i = 0; i < KMEMSTAT_LINE_LN; i++)
320                 sza_printf(sza, "-");
321         sza_printf(sza, "\n");
322         TAILQ_FOREACH(a_i, &all_arenas, next) {
323                 if (a_i->source)
324                         continue;
325                 fetch_arena_and_kids(a_i, sza, 0);
326         }
327         qunlock(&arenas_and_slabs_lock);
328         return sza;
329 }
330
331 static struct chan *mem_open(struct chan *c, int omode)
332 {
333         if (c->qid.type & QTDIR) {
334                 if (openmode(omode) != O_READ)
335                         error(EPERM, "Tried opening directory not read-only");
336         }
337         switch (c->qid.path) {
338         case Qarena_stats:
339                 c->synth_buf = build_arena_stats();
340                 break;
341         case Qslab_stats:
342                 c->synth_buf = build_slab_stats();
343                 break;
344         case Qfree:
345                 c->synth_buf = build_free();
346                 break;
347         case Qkmemstat:
348                 c->synth_buf = build_kmemstat();
349                 break;
350         }
351         c->mode = openmode(omode);
352         c->flag |= COPEN;
353         c->offset = 0;
354         return c;
355 }
356
357 static void mem_close(struct chan *c)
358 {
359         if (!(c->flag & COPEN))
360                 return;
361         switch (c->qid.path) {
362         case Qarena_stats:
363         case Qslab_stats:
364         case Qfree:
365         case Qkmemstat:
366                 kfree(c->synth_buf);
367                 break;
368         }
369 }
370
371 static size_t mem_read(struct chan *c, void *ubuf, size_t n, off64_t offset)
372 {
373         struct sized_alloc *sza;
374
375         switch (c->qid.path) {
376         case Qdir:
377                 return devdirread(c, ubuf, n, mem_dir, ARRAY_SIZE(mem_dir),
378                                                   devgen);
379         case Qarena_stats:
380         case Qslab_stats:
381         case Qfree:
382         case Qkmemstat:
383                 sza = c->synth_buf;
384                 return readstr(offset, ubuf, n, sza->buf);
385         default:
386                 panic("Bad Qid %p!", c->qid.path);
387         }
388         return -1;
389 }
390
391 static size_t mem_write(struct chan *c, void *ubuf, size_t n, off64_t offset)
392 {
393         switch (c->qid.path) {
394         default:
395                 error(EFAIL, "Unable to write to %s", devname());
396         }
397         return n;
398 }
399
400 struct dev mem_devtab __devtab = {
401         .name = "mem",
402         .reset = devreset,
403         .init = devinit,
404         .shutdown = devshutdown,
405         .attach = mem_attach,
406         .walk = mem_walk,
407         .stat = mem_stat,
408         .open = mem_open,
409         .create = devcreate,
410         .close = mem_close,
411         .read = mem_read,
412         .bread = devbread,
413         .write = mem_write,
414         .bwrite = devbwrite,
415         .remove = devremove,
416         .wstat = devwstat,
417         .power = devpower,
418         .chaninfo = devchaninfo,
419 };