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