// SPDX-License-Identifier: GPL-2.0#include <linux/bpf.h>#include <bpf/bpf_tracing.h>#include <stdbool.h>#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_ARRAY); __uint(max_entries, 1); __type(key, unsigned int); __type(value, unsigned long long);} stop_tracing SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1);} signal_stop_tracing SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_PROG_ARRAY); __uint(key_size, sizeof(unsigned int)); __uint(max_entries, 1); __array(values, unsigned int (void *));} bpf_action SEC(".maps") = { .values = { [0] = 0 },}; /* 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; 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(struct trace_event_raw_timerlat_sample *tp_args){ int value = 0; /* Suppress further sample processing */ map_set(&stop_tracing, 0, 1); /* Signal to userspace */ bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0); /* * Call into BPF action program, if attached. * Otherwise, just silently fail. */ bpf_tail_call(tp_args, &bpf_action, 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 (map_get(&stop_tracing, 0)) 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(tp_args); } 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(tp_args); } else { update_main_hist(&hist_user, bucket); update_summary(&summary_user, latency, bucket); if (thread_threshold != 0 && latency_us >= thread_threshold) set_stop_tracing(tp_args); } return 0;}
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_tracing.h>
#include <stdbool.h>
#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_ARRAY);
__uint(max_entries, 1);
__type(key, unsigned int);
__type(value, unsigned long long);
} stop_tracing SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1);
} signal_stop_tracing SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(key_size, sizeof(unsigned int));
__uint(max_entries, 1);
__array(values, unsigned int (void *));
} bpf_action SEC(".maps") = {
.values = {
[0] = 0
},
};
/* 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;
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(struct trace_event_raw_timerlat_sample *tp_args)
{
int value = 0;
/* Suppress further sample processing */
map_set(&stop_tracing, 0, 1);
/* Signal to userspace */
bpf_ringbuf_output(&signal_stop_tracing, &value, sizeof(value), 0);
/*
* Call into BPF action program, if attached.
* Otherwise, just silently fail.
*/
bpf_tail_call(tp_args, &bpf_action, 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 (map_get(&stop_tracing, 0))
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(tp_args);
} 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(tp_args);
} else {
update_main_hist(&hist_user, bucket);
update_summary(&summary_user, latency, bucket);
if (thread_threshold != 0 && latency_us >= thread_threshold)
set_stop_tracing(tp_args);
}
return 0;
}