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