174 lines · 4.1 KB · C
RawDownload
// 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;}