0001
0002
0003 #include "vmlinux.h"
0004 #include <bpf/bpf_helpers.h>
0005 #include <bpf/bpf_tracing.h>
0006 #include <bpf/bpf_core_read.h>
0007
0008
0009 #define MAX_STACKS 8
0010
0011
0012 #define MAX_ENTRIES 10240
0013
0014 struct contention_key {
0015 __s32 stack_id;
0016 };
0017
0018 struct contention_data {
0019 __u64 total_time;
0020 __u64 min_time;
0021 __u64 max_time;
0022 __u32 count;
0023 __u32 flags;
0024 };
0025
0026 struct tstamp_data {
0027 __u64 timestamp;
0028 __u64 lock;
0029 __u32 flags;
0030 __s32 stack_id;
0031 };
0032
0033
0034 struct {
0035 __uint(type, BPF_MAP_TYPE_STACK_TRACE);
0036 __uint(key_size, sizeof(__u32));
0037 __uint(value_size, MAX_STACKS * sizeof(__u64));
0038 __uint(max_entries, MAX_ENTRIES);
0039 } stacks SEC(".maps");
0040
0041
0042 struct {
0043 __uint(type, BPF_MAP_TYPE_TASK_STORAGE);
0044 __uint(map_flags, BPF_F_NO_PREALLOC);
0045 __type(key, int);
0046 __type(value, struct tstamp_data);
0047 } tstamp SEC(".maps");
0048
0049
0050 struct {
0051 __uint(type, BPF_MAP_TYPE_HASH);
0052 __uint(key_size, sizeof(struct contention_key));
0053 __uint(value_size, sizeof(struct contention_data));
0054 __uint(max_entries, MAX_ENTRIES);
0055 } lock_stat SEC(".maps");
0056
0057 struct {
0058 __uint(type, BPF_MAP_TYPE_HASH);
0059 __uint(key_size, sizeof(__u32));
0060 __uint(value_size, sizeof(__u8));
0061 __uint(max_entries, 1);
0062 } cpu_filter SEC(".maps");
0063
0064 struct {
0065 __uint(type, BPF_MAP_TYPE_HASH);
0066 __uint(key_size, sizeof(__u32));
0067 __uint(value_size, sizeof(__u8));
0068 __uint(max_entries, 1);
0069 } task_filter SEC(".maps");
0070
0071
0072 int enabled;
0073 int has_cpu;
0074 int has_task;
0075
0076
0077 unsigned long lost;
0078
0079 static inline int can_record(void)
0080 {
0081 if (has_cpu) {
0082 __u32 cpu = bpf_get_smp_processor_id();
0083 __u8 *ok;
0084
0085 ok = bpf_map_lookup_elem(&cpu_filter, &cpu);
0086 if (!ok)
0087 return 0;
0088 }
0089
0090 if (has_task) {
0091 __u8 *ok;
0092 __u32 pid = bpf_get_current_pid_tgid();
0093
0094 ok = bpf_map_lookup_elem(&task_filter, &pid);
0095 if (!ok)
0096 return 0;
0097 }
0098
0099 return 1;
0100 }
0101
0102 SEC("tp_btf/contention_begin")
0103 int contention_begin(u64 *ctx)
0104 {
0105 struct task_struct *curr;
0106 struct tstamp_data *pelem;
0107
0108 if (!enabled || !can_record())
0109 return 0;
0110
0111 curr = bpf_get_current_task_btf();
0112 pelem = bpf_task_storage_get(&tstamp, curr, NULL,
0113 BPF_LOCAL_STORAGE_GET_F_CREATE);
0114 if (!pelem || pelem->lock)
0115 return 0;
0116
0117 pelem->timestamp = bpf_ktime_get_ns();
0118 pelem->lock = (__u64)ctx[0];
0119 pelem->flags = (__u32)ctx[1];
0120 pelem->stack_id = bpf_get_stackid(ctx, &stacks, BPF_F_FAST_STACK_CMP);
0121
0122 if (pelem->stack_id < 0)
0123 lost++;
0124 return 0;
0125 }
0126
0127 SEC("tp_btf/contention_end")
0128 int contention_end(u64 *ctx)
0129 {
0130 struct task_struct *curr;
0131 struct tstamp_data *pelem;
0132 struct contention_key key;
0133 struct contention_data *data;
0134 __u64 duration;
0135
0136 if (!enabled)
0137 return 0;
0138
0139 curr = bpf_get_current_task_btf();
0140 pelem = bpf_task_storage_get(&tstamp, curr, NULL, 0);
0141 if (!pelem || pelem->lock != ctx[0])
0142 return 0;
0143
0144 duration = bpf_ktime_get_ns() - pelem->timestamp;
0145
0146 key.stack_id = pelem->stack_id;
0147 data = bpf_map_lookup_elem(&lock_stat, &key);
0148 if (!data) {
0149 struct contention_data first = {
0150 .total_time = duration,
0151 .max_time = duration,
0152 .min_time = duration,
0153 .count = 1,
0154 .flags = pelem->flags,
0155 };
0156
0157 bpf_map_update_elem(&lock_stat, &key, &first, BPF_NOEXIST);
0158 pelem->lock = 0;
0159 return 0;
0160 }
0161
0162 __sync_fetch_and_add(&data->total_time, duration);
0163 __sync_fetch_and_add(&data->count, 1);
0164
0165
0166 if (data->max_time < duration)
0167 data->max_time = duration;
0168 if (data->min_time > duration)
0169 data->min_time = duration;
0170
0171 pelem->lock = 0;
0172 return 0;
0173 }
0174
0175 char LICENSE[] SEC("license") = "Dual BSD/GPL";