bpf/kernellatency.c (202 lines of code) (raw):
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
// +build ignore
#include "vmlinux.h"
#include "bpf_helpers.h"
#include "bpf_core_read.h"
#include "bpf_tracing.h"
#include "inspector.h"
#define RX_KLATENCY 1
#define TX_KLATENCY 2
#define THRESH 10000000
struct txlatency_t {
u64 queuexmit;
u64 local;
u64 output;
u64 finish;
};
struct rxlatency_t {
u64 rcv;
u64 rcvfinish;
u64 local;
u64 localfinish;
};
struct insp_kl_event_t {
char target[TASK_COMM_LEN];
struct tuple tuple;
struct skb_meta skb_meta;
u32 pid;
u32 cpu;
u32 direction;
u64 latency;
u64 point1;
u64 point2;
u64 point3;
u64 point4;
};
// struct insp_kernelrx_event_t {
// char target[TASK_COMM_LEN];
// struct tuple tuple;
// struct skb_meta skb_meta;
// u32 pid;
// u32 cpu;
// struct rxlatency_t latency;
// s64 stack_id;
// };
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct sk_buff *);
__type(value, struct rxlatency_t);
__uint(max_entries, 10000);
} insp_kernelrx_entry SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct sk_buff *);
__type(value, struct txlatency_t);
__uint(max_entries, 10000);
} insp_kerneltx_entry SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(max_entries, 1000);
__uint(key_size, sizeof(u32));
__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
} insp_klatency_stack SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
} insp_klatency_event SEC(".maps");
struct insp_kl_event_t *unused_event __attribute__((unused));
static inline int update_rxlat(void *ctx, struct sk_buff * skb, struct rxlatency_t * lat )
{
if (lat->localfinish > lat->rcv) {
u64 latency;
latency = lat->localfinish - lat->rcv;
if (latency > THRESH) {
struct insp_kl_event_t event = {0};
bpf_get_current_comm(&event.target, sizeof(event.target));
set_tuple(skb, &event.tuple);
set_meta(skb,&event.skb_meta);
event.pid = bpf_get_current_pid_tgid() >> 32;
event.cpu = bpf_get_smp_processor_id();
event.direction = RX_KLATENCY;
event.latency = latency;
bpf_probe_read(&event.point1,sizeof(event.point1),&lat->rcv);
bpf_probe_read(&event.point2,sizeof(event.point1),&lat->rcvfinish);
bpf_probe_read(&event.point3,sizeof(event.point1),&lat->local);
bpf_probe_read(&event.point4,sizeof(event.point1),&lat->localfinish);
bpf_perf_event_output(ctx, &insp_klatency_event, BPF_F_CURRENT_CPU, &event, sizeof(event));
}
}
return 0;
}
static inline int update_txlat(void *ctx, struct sk_buff * skb, struct txlatency_t * lat )
{
if (lat->finish > lat->queuexmit) {
u64 latency;
latency = lat->finish - lat->queuexmit;
if (latency > THRESH) {
struct insp_kl_event_t event = {0};
bpf_get_current_comm(&event.target, sizeof(event.target));
set_tuple(skb, &event.tuple);
set_meta(skb,&event.skb_meta);
event.pid = bpf_get_current_pid_tgid()>> 32;
event.cpu = bpf_get_smp_processor_id();
event.direction = TX_KLATENCY;
event.latency = latency;
bpf_probe_read(&event.point1,sizeof(event.point1),&lat->queuexmit);
bpf_probe_read(&event.point2,sizeof(event.point1),&lat->local);
bpf_probe_read(&event.point3,sizeof(event.point1),&lat->output);
bpf_probe_read(&event.point4,sizeof(event.point1),&lat->finish);
// bpf_core_read(&event.latency,sizeof(event.latency),&lat);
bpf_perf_event_output(ctx, &insp_klatency_event, BPF_F_CURRENT_CPU, &event, sizeof(event));
}
}
return 0;
}
SEC("kprobe/ip_rcv")
int klatency_ip_rcv(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
struct rxlatency_t lat = {0};
lat.rcv = bpf_ktime_get_ns();
bpf_map_update_elem(&insp_kernelrx_entry, &skb, &lat, BPF_ANY);
return 0;
}
SEC("kprobe/ip_rcv_finish")
int klatency_ip_rcv_finish(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
struct rxlatency_t * lat = bpf_map_lookup_elem(&insp_kernelrx_entry,&skb);
if (lat) {
lat->rcvfinish = bpf_ktime_get_ns();
// bpf_printk("iprcv %llu iprcvfinish %llu\n",lat->rcv,lat->rcvfinish);
}
return 0;
}
SEC("kprobe/ip_local_deliver")
int klatency_ip_local_deliver(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
struct rxlatency_t * lat = bpf_map_lookup_elem(&insp_kernelrx_entry,&skb);
if (lat) {
lat->local = bpf_ktime_get_ns();
}
return 0;
}
SEC("kprobe/ip_local_deliver_finish")
int klatency_ip_local_deliver_finish(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
struct rxlatency_t * lat = bpf_map_lookup_elem(&insp_kernelrx_entry,&skb);
if (lat) {
lat->localfinish = bpf_ktime_get_ns();
update_rxlat(ctx,skb,lat);
bpf_map_delete_elem(&insp_kernelrx_entry, &skb);
}
return 0;
}
SEC("kprobe/__ip_queue_xmit")
int klatency_ip_queue_xmit(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM2(ctx);
struct txlatency_t lat = {0};
lat.queuexmit = bpf_ktime_get_ns();
bpf_map_update_elem(&insp_kerneltx_entry, &skb, &lat, BPF_ANY);
return 0;
}
SEC("kprobe/ip_local_out")
int klatency_ip_local(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
struct txlatency_t * lat = bpf_map_lookup_elem(&insp_kerneltx_entry,&skb);
if (lat) {
lat->local = bpf_ktime_get_ns();
}
return 0;
}
SEC("kprobe/ip_output")
int klatency_ip_output(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
struct txlatency_t * lat = bpf_map_lookup_elem(&insp_kerneltx_entry,&skb);
if (lat) {
lat->output = bpf_ktime_get_ns();
}
return 0;
}
SEC("kprobe/ip_finish_output2")
int klatency_ip_finish_output2(struct pt_regs * ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
struct txlatency_t * lat = bpf_map_lookup_elem(&insp_kerneltx_entry,&skb);
if (lat) {
lat->finish = bpf_ktime_get_ns();
update_txlat(ctx,skb,lat);
bpf_map_delete_elem(&insp_kerneltx_entry, &skb);
}
return 0;
}
SEC("kprobe/kfree_skb")
int report_kfree(struct pt_regs *ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
struct rxlatency_t * latrx = bpf_map_lookup_elem(&insp_kernelrx_entry,&skb);
if (latrx) {
bpf_map_delete_elem(&insp_kernelrx_entry, &skb);
}
struct txlatency_t * lattx = bpf_map_lookup_elem(&insp_kerneltx_entry,&skb);
if (lattx) {
bpf_map_delete_elem(&insp_kerneltx_entry, &skb);
}
return 0;
}
SEC("kprobe/consume_skb")
int report_consume(struct pt_regs *ctx){
struct sk_buff * skb = (struct sk_buff *)PT_REGS_PARM1(ctx);
struct rxlatency_t * latrx = bpf_map_lookup_elem(&insp_kernelrx_entry,&skb);
if (latrx) {
bpf_map_delete_elem(&insp_kernelrx_entry, &skb);
}
struct txlatency_t * lattx = bpf_map_lookup_elem(&insp_kerneltx_entry,&skb);
if (lattx) {
bpf_map_delete_elem(&insp_kerneltx_entry, &skb);
}
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";