// SPDX-License-Identifier: GPL-2.0 #include #include #include #include "timerlat_bpf.h" #define nosubprog __always_inline #define MAX_ENTRIES_DEFAULT 4096 char LICENSE[] SEC("license") = "GPL"; struct trace_event_raw_timerlat_sample { unsigned long long timer_latency; int context; } __attribute__((preserve_access_index)); struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, MAX_ENTRIES_DEFAULT); __type(key, unsigned int); __type(value, unsigned long long); } hist_irq SEC(".maps"), hist_thread SEC(".maps"), hist_user SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(max_entries, SUMMARY_FIELD_N); __type(key, unsigned int); __type(value, unsigned long long); } summary_irq SEC(".maps"), summary_thread SEC(".maps"), summary_user SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1); } signal_stop_tracing SEC(".maps"); /* Params to be set by rtla */ const volatile int bucket_size = 1; const volatile int output_divisor = 1000; const volatile int entries = 256; const volatile int irq_threshold; const volatile int thread_threshold; const volatile bool aa_only; int stop_tracing; nosubprog unsigned long long map_get(void *map, unsigned int key) { unsigned long long *value_ptr; value_ptr = bpf_map_lookup_elem(map, &key); return !value_ptr ? 0 : *value_ptr; } nosubprog void map_set(void *map, unsigned int key, unsigned long long value) { bpf_map_update_elem(map, &key, &value, BPF_ANY); } nosubprog void map_increment(void *map, unsigned int key) { map_set(map, key, map_get(map, key) + 1); } nosubprog void update_main_hist(void *map, int bucket) { if (entries == 0) /* No histogram */ return; if (bucket >= entries) /* Overflow */ return; map_increment(map, bucket); } nosubprog void update_summary(void *map, unsigned long long latency, int bucket) { if (aa_only) /* Auto-analysis only, nothing to be done here */ return; map_set(map, SUMMARY_CURRENT, latency); if (bucket >= entries) /* Overflow */ map_increment(map, SUMMARY_OVERFLOW); if (latency > map_get(map, SUMMARY_MAX)) map_set(map, SUMMARY_MAX, latency); if (latency < map_get(map, SUMMARY_MIN) || map_get(map, SUMMARY_COUNT) == 0) map_set(map, SUMMARY_MIN, latency); map_increment(map, SUMMARY_COUNT); map_set(map, SUMMARY_SUM, map_get(map, SUMMARY_SUM) + latency); } nosubprog void set_stop_tracing(void) { int value = 0; /* Suppress further sample processing */ stop_tracing = 1; /* Signal to userspace */ bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0); } SEC("tp/osnoise/timerlat_sample") int handle_timerlat_sample(struct trace_event_raw_timerlat_sample *tp_args) { unsigned long long latency, latency_us; int bucket; if (stop_tracing) return 0; latency = tp_args->timer_latency / output_divisor; latency_us = tp_args->timer_latency / 1000; bucket = latency / bucket_size; if (tp_args->context == 0) { update_main_hist(&hist_irq, bucket); update_summary(&summary_irq, latency, bucket); if (irq_threshold != 0 && latency_us >= irq_threshold) set_stop_tracing(); } else if (tp_args->context == 1) { update_main_hist(&hist_thread, bucket); update_summary(&summary_thread, latency, bucket); if (thread_threshold != 0 && latency_us >= thread_threshold) set_stop_tracing(); } else { update_main_hist(&hist_user, bucket); update_summary(&summary_user, latency, bucket); } return 0; }