2#include "pedigree/kernel/LatencyAccounting.h"
4#if PEDIGREE_LATENCY_ACCOUNTING
5#include "pedigree/kernel/process/CpuAffinity.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/time/Time.h"
10namespace LatencyAccounting {
12struct alignas(64) CpuCounters {
13 uint64_t sequence = 0;
16CpuCounters counters[CpuAffinityMask::MaximumCpus];
17bool clockReady =
false;
19CpuCounters* local(
bool includeNmi =
false) {
20 if (!__atomic_load_n(&clockReady, __ATOMIC_ACQUIRE)) {
24 if (cpu >= CpuAffinityMask::MaximumCpus ||
25 !__atomic_load_n(&counters[cpu].data.values[OnlineSince], __ATOMIC_RELAXED) ||
29 return &counters[cpu];
32void begin(CpuCounters& cpu) {
34 __atomic_store_n(&cpu.sequence, cpu.sequence + 1, __ATOMIC_RELAXED);
35 __atomic_thread_fence(__ATOMIC_RELEASE);
37void end(CpuCounters& cpu) {
38 __atomic_store_n(&cpu.sequence, cpu.sequence + 1, __ATOMIC_RELEASE);
40uint64_t get(CpuCounters& cpu, Field field) {
41 return __atomic_load_n(&cpu.data.values[field], __ATOMIC_RELAXED);
43void set(CpuCounters& cpu, Field field, uint64_t value) {
44 __atomic_store_n(&cpu.data.values[field], value, __ATOMIC_RELAXED);
46void add(CpuCounters& cpu, Field field, uint64_t value) {
47 set(cpu, field, get(cpu, field) + value);
53 if (index >= CpuAffinityMask::MaximumCpus) {
56 CpuCounters& cpu = counters[index];
57 const uint64_t now = Time::sampleCpuTime().timestamp;
59 set(cpu, OnlineSince, now ? now : 1);
60 set(cpu, IrqOffOpenSince, now ? now : 1);
62 __atomic_store_n(&clockReady,
true, __ATOMIC_RELEASE);
66 return local() !=
nullptr;
69void recordIrqState(
bool enabled, uintptr_t site) {
70 CpuCounters* cpu = local();
74 const uint64_t start = get(*cpu, IrqOffOpenSince);
75 if (enabled != (start != 0)) {
78 const uint64_t now = Time::sampleCpuTime().timestamp;
81 const uint64_t elapsed = now >= start ? now - start : 0;
82 add(*cpu, IrqOffNanoseconds, elapsed);
83 add(*cpu, IrqOffCount, 1);
84 if (elapsed > get(*cpu, IrqOffMax)) {
85 set(*cpu, IrqOffMax, elapsed);
86 set(*cpu, IrqOffMaxEnd, now);
87 set(*cpu, IrqOffMaxSite, get(*cpu, IrqOffOpenSite));
89 if (elapsed >= Time::Multiplier::Millisecond) {
90 add(*cpu, IrqOffOver1ms, 1);
92 if (elapsed >= 10 * Time::Multiplier::Millisecond) {
93 add(*cpu, IrqOffOver10ms, 1);
95 set(*cpu, IrqOffOpenSince, 0);
96 set(*cpu, IrqOffOpenSite, 0);
98 set(*cpu, IrqOffOpenSince, now ? now : 1);
99 set(*cpu, IrqOffOpenSite, site);
104void recordDeferredCpu(uint64_t elapsed) {
105 if (CpuCounters* cpu = local(
true)) {
109 __atomic_fetch_add(&cpu->data.values[DeferredCpuNanoseconds], elapsed, __ATOMIC_RELAXED);
113void recordDeferredWall(uint64_t elapsed) {
114 if (CpuCounters* cpu = local()) {
116 add(*cpu, DeferredWallNanoseconds, elapsed);
117 add(*cpu, DeferredCount, 1);
118 if (elapsed > get(*cpu, DeferredMax)) {
119 set(*cpu, DeferredMax, elapsed);
125bool snapshot(
size_t index, Snapshot& result) {
126 if (index >= CpuAffinityMask::MaximumCpus) {
129 CpuCounters& cpu = counters[index];
131 for (
size_t attempt = 0; attempt < 32; ++attempt) {
132 const uint64_t before = __atomic_load_n(&cpu.sequence, __ATOMIC_ACQUIRE);
136 for (
size_t field = 0; field < Count; ++field) {
137 result.values[field] = __atomic_load_n(&cpu.data.values[field], __ATOMIC_RELAXED);
139 __atomic_thread_fence(__ATOMIC_ACQUIRE);
140 if (before == __atomic_load_n(&cpu.sequence, __ATOMIC_RELAXED)) {
141 return result.values[OnlineSince] != 0;
148extern "C" void pedigree_irq_time_state(
bool enabled) {
149 LatencyAccounting::recordIrqState(enabled,
150 reinterpret_cast<uintptr_t
>(__builtin_return_address(0)));
static ProcessorInformation & information()