ebpf_exporter
166 строк · 4.5 Кб
1#include <vmlinux.h>2#include <bpf/bpf_tracing.h>3#include "maps.bpf.h"4
5// Loosely based on https://github.com/xdp-project/xdp-project/blob/master/areas/latency/softirq_net_latency.bt
6
7// 30 buckets for latency, max range is 0.5s .. 1.0s
8#define MAX_LATENCY_SLOT 319
10#define check_net_rx(vec_nr) \11if (vec_nr != NET_RX_SOFTIRQ) { \12return 0; \13}14
15/* This provide easy way to disable measuring 'runtime'.
16* This avoids hooking 'softirq_exit' as it can be expensive and for NET_RX
17* this isn't the right hook as runtime is affected by NAPI packet bulking.
18*/
19#define CONFIG_MEASURE_RUNTIME 120
21// We only use one index in the array for this
22u32 net_rx_idx = 0;23
24struct softirq_latency_key_t {25u32 bucket;26};27
28struct {29__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);30__uint(max_entries, 1);31__type(key, u32);32__type(value, u64);33} softirq_raised_total SEC(".maps");34
35struct {36__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);37__uint(max_entries, 1);38__type(key, u32);39__type(value, u64);40} softirq_serviced_total SEC(".maps");41
42struct {43__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);44__uint(max_entries, 1);45__type(key, u32);46__type(value, u64);47} softirq_raise_timestamp SEC(".maps");48
49struct {50__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);51__uint(max_entries, MAX_LATENCY_SLOT + 2);52__type(key, u32);53__type(value, u64);54} softirq_wait_seconds SEC(".maps");55
56#ifdef CONFIG_MEASURE_RUNTIME57struct {58__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);59__uint(max_entries, 1);60__type(key, u32);61__type(value, u64);62} softirq_entry_timestamp SEC(".maps");63
64struct {65__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);66__uint(max_entries, MAX_LATENCY_SLOT + 2);67__type(key, u32);68__type(value, u64);69} softirq_runtime_seconds SEC(".maps");70#endif71
72SEC("tp_btf/softirq_raise")73int BPF_PROG(softirq_raise, unsigned int vec_nr)74{
75u64 *existing_ts_ptr, *raised_total_ptr, ts;76
77check_net_rx(vec_nr);78
79ts = bpf_ktime_get_ns();80
81read_array_ptr(&softirq_raised_total, &net_rx_idx, raised_total_ptr);82*raised_total_ptr += 1;83
84read_array_ptr(&softirq_raise_timestamp, &net_rx_idx, existing_ts_ptr);85
86// Set the timestamp only if it is not set, so that we always measure the oldest non-entered raise87if (!*existing_ts_ptr) {88*existing_ts_ptr = ts;89}90
91return 0;92}
93
94SEC("tp_btf/softirq_entry")95int BPF_PROG(softirq_entry, unsigned int vec_nr)96{
97u64 delta_ns, *raise_ts_ptr, *serviced_total_ptr, ts;98struct softirq_latency_key_t key = {};99
100check_net_rx(vec_nr);101
102ts = bpf_ktime_get_ns();103
104read_array_ptr(&softirq_serviced_total, &net_rx_idx, serviced_total_ptr);105*serviced_total_ptr += 1;106
107read_array_ptr(&softirq_raise_timestamp, &net_rx_idx, raise_ts_ptr);108
109// Interrupt was re-rased after ts was obtained, resulting in negative duration110if (*raise_ts_ptr > ts) {111return 0;112}113
114// Interrupt entry started with no corresponding raise, resulting in large duration115if (!*raise_ts_ptr) {116return 0;117}118
119delta_ns = ts - *raise_ts_ptr;120
121increment_exp2_histogram_nosync(&softirq_wait_seconds, key, delta_ns, MAX_LATENCY_SLOT);122
123// Allow raise timestamp to be set again124*raise_ts_ptr = 0;125
126#ifdef CONFIG_MEASURE_RUNTIME127u64 *existing_entry_ts_ptr;128
129read_array_ptr(&softirq_entry_timestamp, &net_rx_idx, existing_entry_ts_ptr);130
131// There is some time from function start to here, so overall service time includes it132*existing_entry_ts_ptr = ts;133#endif134return 0;135}
136
137#ifdef CONFIG_MEASURE_RUNTIME138SEC("tp_btf/softirq_exit")139int BPF_PROG(softirq_exit, unsigned int vec_nr)140{
141u64 delta_ns, *entry_ts_ptr, ts;142struct softirq_latency_key_t key = {};143
144check_net_rx(vec_nr);145
146ts = bpf_ktime_get_ns();147
148read_array_ptr(&softirq_entry_timestamp, &net_rx_idx, entry_ts_ptr);149
150// Interrupt exited with no corresponding entry, resulting in large duration151if (!*entry_ts_ptr) {152return 0;153}154
155delta_ns = ts - *entry_ts_ptr;156
157increment_exp2_histogram_nosync(&softirq_runtime_seconds, key, delta_ns, MAX_LATENCY_SLOT);158
159// Reset entry ts to prevent skipped entries to be counted at exit160*entry_ts_ptr = 0;161
162return 0;163}
164#endif165
166char LICENSE[] SEC("license") = "GPL";167