1 /*
2  * time_in_state eBPF program
3  *
4  * Copyright (C) 2018 Google
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version
8  * 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16 
17 #include <bpf_helpers.h>
18 #include <bpf_timeinstate.h>
19 
20 DEFINE_BPF_MAP_GRW(uid_time_in_state_map, PERCPU_HASH, time_key_t, tis_val_t, 1024, AID_SYSTEM)
21 
22 DEFINE_BPF_MAP_GRW(uid_concurrent_times_map, PERCPU_HASH, time_key_t, concurrent_val_t, 1024, AID_SYSTEM)
23 DEFINE_BPF_MAP_GRW(uid_last_update_map, HASH, uint32_t, uint64_t, 1024, AID_SYSTEM)
24 
25 DEFINE_BPF_MAP_GWO(cpu_last_update_map, PERCPU_ARRAY, uint32_t, uint64_t, 1, AID_SYSTEM)
26 
27 DEFINE_BPF_MAP_GWO(cpu_policy_map, ARRAY, uint32_t, uint32_t, 1024, AID_SYSTEM)
28 DEFINE_BPF_MAP_GWO(policy_freq_idx_map, ARRAY, uint32_t, uint8_t, 1024, AID_SYSTEM)
29 
30 DEFINE_BPF_MAP_GWO(freq_to_idx_map, HASH, freq_idx_key_t, uint8_t, 2048, AID_SYSTEM)
31 
32 DEFINE_BPF_MAP_GWO(nr_active_map, ARRAY, uint32_t, uint32_t, 1, AID_SYSTEM)
33 DEFINE_BPF_MAP_GWO(policy_nr_active_map, ARRAY, uint32_t, uint32_t, 1024, AID_SYSTEM)
34 
35 struct switch_args {
36     unsigned long long ignore;
37     char prev_comm[16];
38     int prev_pid;
39     int prev_prio;
40     long long prev_state;
41     char next_comm[16];
42     int next_pid;
43     int next_prio;
44 };
45 
46 DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch)
47 (struct switch_args* args) {
48     const int ALLOW = 1;  // return 1 to avoid blocking simpleperf from receiving events.
49     uint32_t zero = 0;
50     uint64_t* last = bpf_cpu_last_update_map_lookup_elem(&zero);
51     if (!last) return ALLOW;
52     uint64_t old_last = *last;
53     uint64_t time = bpf_ktime_get_ns();
54     *last = time;
55 
56     uint32_t* active = bpf_nr_active_map_lookup_elem(&zero);
57     if (!active) return ALLOW;
58 
59     uint32_t cpu = bpf_get_smp_processor_id();
60     uint32_t* policyp = bpf_cpu_policy_map_lookup_elem(&cpu);
61     if (!policyp) return ALLOW;
62     uint32_t policy = *policyp;
63 
64     uint32_t* policy_active = bpf_policy_nr_active_map_lookup_elem(&policy);
65     if (!policy_active) return ALLOW;
66 
67     uint32_t nactive = *active - 1;
68     uint32_t policy_nactive = *policy_active - 1;
69 
70     if (!args->prev_pid || (!old_last && args->next_pid)) {
71         __sync_fetch_and_add(active, 1);
72         __sync_fetch_and_add(policy_active, 1);
73     }
74 
75     // Return here in 2 scenarios:
76     // 1) prev_pid == 0, so we're exiting idle. No UID stats need updating, and active CPUs can't be
77     //    decreasing.
78     // 2) old_last == 0, so this is the first time we've seen this CPU. Any delta will be invalid,
79     //    and our active CPU counts don't include this CPU yet so we shouldn't decrement them even
80     //    if we're going idle.
81     if (!args->prev_pid || !old_last) return ALLOW;
82 
83     if (!args->next_pid) {
84         __sync_fetch_and_add(active, -1);
85         __sync_fetch_and_add(policy_active, -1);
86     }
87 
88     uint8_t* freq_idxp = bpf_policy_freq_idx_map_lookup_elem(&policy);
89     if (!freq_idxp || !*freq_idxp) return ALLOW;
90     // freq_to_idx_map uses 1 as its minimum index so that *freq_idxp == 0 only when uninitialized
91     uint8_t freq_idx = *freq_idxp - 1;
92 
93     uint32_t uid = bpf_get_current_uid_gid();
94     time_key_t key = {.uid = uid, .bucket = freq_idx / FREQS_PER_ENTRY};
95     tis_val_t* val = bpf_uid_time_in_state_map_lookup_elem(&key);
96     if (!val) {
97         tis_val_t zero_val = {.ar = {0}};
98         bpf_uid_time_in_state_map_update_elem(&key, &zero_val, BPF_NOEXIST);
99         val = bpf_uid_time_in_state_map_lookup_elem(&key);
100     }
101     uint64_t delta = time - old_last;
102     if (val) val->ar[freq_idx % FREQS_PER_ENTRY] += delta;
103 
104     key.bucket = nactive / CPUS_PER_ENTRY;
105     concurrent_val_t* ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
106     if (!ct) {
107         concurrent_val_t zero_val = {.active = {0}, .policy = {0}};
108         bpf_uid_concurrent_times_map_update_elem(&key, &zero_val, BPF_NOEXIST);
109         ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
110     }
111     if (ct) ct->active[nactive % CPUS_PER_ENTRY] += delta;
112 
113     if (policy_nactive / CPUS_PER_ENTRY != key.bucket) {
114         key.bucket = policy_nactive / CPUS_PER_ENTRY;
115         ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
116         if (!ct) {
117             concurrent_val_t zero_val = {.active = {0}, .policy = {0}};
118             bpf_uid_concurrent_times_map_update_elem(&key, &zero_val, BPF_NOEXIST);
119             ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
120         }
121     }
122     if (ct) ct->policy[policy_nactive % CPUS_PER_ENTRY] += delta;
123     uint64_t* uid_last_update = bpf_uid_last_update_map_lookup_elem(&uid);
124     if (uid_last_update) {
125         *uid_last_update = time;
126     } else {
127         bpf_uid_last_update_map_update_elem(&uid, &time, BPF_NOEXIST);
128     }
129     return ALLOW;
130 }
131 
132 struct cpufreq_args {
133     unsigned long long ignore;
134     unsigned int state;
135     unsigned int cpu_id;
136 };
137 
138 DEFINE_BPF_PROG("tracepoint/power/cpu_frequency", AID_ROOT, AID_SYSTEM, tp_cpufreq)
139 (struct cpufreq_args* args) {
140     uint32_t cpu = args->cpu_id;
141     unsigned int new = args->state;
142     uint32_t* policyp = bpf_cpu_policy_map_lookup_elem(&cpu);
143     if (!policyp) return 0;
144     uint32_t policy = *policyp;
145     freq_idx_key_t key = {.policy = policy, .freq = new};
146     uint8_t* idxp = bpf_freq_to_idx_map_lookup_elem(&key);
147     if (!idxp) return 0;
148     uint8_t idx = *idxp;
149     bpf_policy_freq_idx_map_update_elem(&policy, &idx, BPF_ANY);
150     return 0;
151 }
152 
153 LICENSE("GPL");
154