1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2020 Wenbo Zhang
3 #include <vmlinux.h>
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_tracing.h>
6 #include "cpufreq.h"
7 #include "maps.bpf.h"
8
9 __u32 freqs_mhz[MAX_CPU_NR] = {};
10 static struct hist zero;
11 struct hist syswide = {};
12 bool filter_cg = false;
13
14 struct {
15 __uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
16 __type(key, u32);
17 __type(value, u32);
18 __uint(max_entries, 1);
19 } cgroup_map SEC(".maps");
20
21 struct {
22 __uint(type, BPF_MAP_TYPE_HASH);
23 __uint(max_entries, MAX_ENTRIES);
24 __type(key, struct hkey);
25 __type(value, struct hist);
26 } hists SEC(".maps");
27
28 #define clamp_umax(VAR, UMAX) \
29 asm volatile ( \
30 "if %0 <= %[max] goto +1\n" \
31 "%0 = %[max]\n" \
32 : "+r"(VAR) \
33 : [max]"i"(UMAX) \
34 )
35
36 SEC("tp_btf/cpu_frequency")
BPF_PROG(cpu_frequency,unsigned int state,unsigned int cpu_id)37 int BPF_PROG(cpu_frequency, unsigned int state, unsigned int cpu_id)
38 {
39 if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
40 return 0;
41
42 if (cpu_id >= MAX_CPU_NR)
43 return 0;
44
45 clamp_umax(cpu_id, MAX_CPU_NR - 1);
46 freqs_mhz[cpu_id] = state / 1000;
47 return 0;
48 }
49
50 SEC("perf_event")
do_sample(struct bpf_perf_event_data * ctx)51 int do_sample(struct bpf_perf_event_data *ctx)
52 {
53 u32 freq_mhz, pid = bpf_get_current_pid_tgid();
54 u64 slot, cpu = bpf_get_smp_processor_id();
55 struct hist *hist;
56 struct hkey hkey;
57
58 if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
59 return 0;
60
61 if (cpu >= MAX_CPU_NR)
62 return 0;
63 clamp_umax(cpu, MAX_CPU_NR - 1);
64 freq_mhz = freqs_mhz[cpu];
65 if (!freq_mhz)
66 return 0;
67 /*
68 * The range of the linear histogram is 0 ~ 5000mhz,
69 * and the step size is 200.
70 */
71 slot = freq_mhz / HIST_STEP_SIZE;
72 if (slot >= MAX_SLOTS)
73 slot = MAX_SLOTS - 1;
74 __sync_fetch_and_add(&syswide.slots[slot], 1);
75 if (!pid)
76 return 0;
77 bpf_get_current_comm(&hkey.comm, sizeof(hkey.comm));
78 hist = bpf_map_lookup_or_try_init(&hists, &hkey, &zero);
79 if (!hist)
80 return 0;
81 __sync_fetch_and_add(&hist->slots[slot], 1);
82 return 0;
83 }
84
85 char LICENSE[] SEC("license") = "GPL";
86