1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#pragma once
8
9#include <arch/ops.h>
10#include <kernel/atomic.h>
11#include <kernel/percpu.h>
12
13#include <zircon/compiler.h>
14
15__BEGIN_CDECLS
16
17// Kernel counters are a facility designed to help field diagnostics and
18// to help devs properly dimension the load/clients/size of the kernel
19// constructs. It answers questions like:
20//   - after N seconds how many outstanding <x> things are allocated?
21//   - up to this point has <Y> ever happened?
22//
23// Currently the only query interface to the counters is the console
24// k counters command. Issue 'k counters help' to learn what it can do.
25//
26// Kernel counters public API:
27// 1- define a new counter.
28//      KCOUNTER(counter_name, "<counter name>");
29//
30// 2- counters start at zero, increment the counter:
31//      kcounter_add(counter_name, 1);
32//
33//
34// Naming the counters
35// The naming convention is "kernel.subsystem.thing_or_action"
36// for example "kernel.dispatcher.destroy"
37//             "kernel.exceptions.fpu"
38//             "kernel.handles.new"
39//
40//  Reading the counters in code
41//  Don't. The counters are mantained in a per-cpu arena and
42//  atomic operations are never used to set their value so
43//  they are both imprecise and reflect only the operations
44//  on a particular core.
45
46struct k_counter_desc {
47    const char* name;
48};
49static_assert(sizeof(struct k_counter_desc) ==
50              sizeof(((struct percpu){}).counters[0]),
51              "the kernel.ld ASSERT knows that these sizes match");
52
53// Define the descriptor and reserve the arena space for the counters.
54// Because of -fdata-sections, each kcounter_arena_* array will be
55// placed in a .bss.kcounter.* section; kernel.ld recognizes those names
56// and places them all together to become the contiguous kcounters_arena
57// array.  Note that each kcounter_arena_* does not correspond with the
58// slots used for this particular counter (that would have terrible
59// cache effects); it just reserves enough space for counters_init() to
60// dole out in per-CPU chunks.
61#define KCOUNTER(var, name)                                         \
62    __USED int64_t kcounter_arena_##var[SMP_MAX_CPUS]               \
63        __asm__("kcounter." name);                                  \
64    __USED __SECTION("kcountdesc." name)                            \
65    static const struct k_counter_desc var[] = { { name } }
66
67// Via magic in kernel.ld, all the descriptors wind up in a contiguous
68// array bounded by these two symbols, sorted by name.
69extern const struct k_counter_desc kcountdesc_begin[], kcountdesc_end[];
70
71// The order of the descriptors is the order of the slots in each percpu array.
72static inline size_t kcounter_index(const struct k_counter_desc* var) {
73    return var - kcountdesc_begin;
74}
75
76// The counter, as named |var| and defined is just an offset into
77// per-cpu table, therefore to add an atomic is not required.
78static inline int64_t* kcounter_slot(const struct k_counter_desc* var) {
79    return &get_local_percpu()->counters[kcounter_index(var)];
80}
81
82static inline void kcounter_add(const struct k_counter_desc* var,
83                                int64_t add) {
84#if defined(__aarch64__)
85    // use a relaxed atomic load/store for arm64 to avoid a potentially nasty
86    // race between the regular load/store operations on for a +1. Relaxed
87    // atomic load/stores are about as efficient as a regular load/store.
88    atomic_add_64_relaxed(kcounter_slot(var), add);
89#else
90    // x86 can do the add in a single non atomic instruction, so the data loss
91    // of a preemption in the middle of this sequence is fairly minimal.
92    *kcounter_slot(var) += add;
93#endif
94}
95
96__END_CDECLS
97