brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 7ec7143 Raw
281 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/version.h>4#include <linux/ptrace.h>5#include <uapi/linux/bpf.h>6#include <bpf/bpf_helpers.h>7 8/*9 * The CPU number, cstate number and pstate number are based10 * on 96boards Hikey with octa CA53 CPUs.11 *12 * Every CPU have three idle states for cstate:13 *   WFI, CPU_OFF, CLUSTER_OFF14 *15 * Every CPU have 5 operating points:16 *   208MHz, 432MHz, 729MHz, 960MHz, 1200MHz17 *18 * This code is based on these assumption and other platforms19 * need to adjust these definitions.20 */21#define MAX_CPU			822#define MAX_PSTATE_ENTRIES	523#define MAX_CSTATE_ENTRIES	324 25static int cpu_opps[] = { 208000, 432000, 729000, 960000, 1200000 };26 27/*28 * my_map structure is used to record cstate and pstate index and29 * timestamp (Idx, Ts), when new event incoming we need to update30 * combination for new state index and timestamp (Idx`, Ts`).31 *32 * Based on (Idx, Ts) and (Idx`, Ts`) we can calculate the time33 * interval for the previous state: Duration(Idx) = Ts` - Ts.34 *35 * Every CPU has one below array for recording state index and36 * timestamp, and record for cstate and pstate saperately:37 *38 * +--------------------------+39 * | cstate timestamp         |40 * +--------------------------+41 * | cstate index             |42 * +--------------------------+43 * | pstate timestamp         |44 * +--------------------------+45 * | pstate index             |46 * +--------------------------+47 */48#define MAP_OFF_CSTATE_TIME	049#define MAP_OFF_CSTATE_IDX	150#define MAP_OFF_PSTATE_TIME	251#define MAP_OFF_PSTATE_IDX	352#define MAP_OFF_NUM		453 54struct {55	__uint(type, BPF_MAP_TYPE_ARRAY);56	__type(key, u32);57	__type(value, u64);58	__uint(max_entries, MAX_CPU * MAP_OFF_NUM);59} my_map SEC(".maps");60 61/* cstate_duration records duration time for every idle state per CPU */62struct {63	__uint(type, BPF_MAP_TYPE_ARRAY);64	__type(key, u32);65	__type(value, u64);66	__uint(max_entries, MAX_CPU * MAX_CSTATE_ENTRIES);67} cstate_duration SEC(".maps");68 69/* pstate_duration records duration time for every operating point per CPU */70struct {71	__uint(type, BPF_MAP_TYPE_ARRAY);72	__type(key, u32);73	__type(value, u64);74	__uint(max_entries, MAX_CPU * MAX_PSTATE_ENTRIES);75} pstate_duration SEC(".maps");76 77/*78 * The trace events for cpu_idle and cpu_frequency are taken from:79 * /sys/kernel/tracing/events/power/cpu_idle/format80 * /sys/kernel/tracing/events/power/cpu_frequency/format81 *82 * These two events have same format, so define one common structure.83 */84struct cpu_args {85	u64 pad;86	u32 state;87	u32 cpu_id;88};89 90/* calculate pstate index, returns MAX_PSTATE_ENTRIES for failure */91static u32 find_cpu_pstate_idx(u32 frequency)92{93	u32 i;94 95	for (i = 0; i < sizeof(cpu_opps) / sizeof(u32); i++) {96		if (frequency == cpu_opps[i])97			return i;98	}99 100	return i;101}102 103SEC("tracepoint/power/cpu_idle")104int bpf_prog1(struct cpu_args *ctx)105{106	u64 *cts, *pts, *cstate, *pstate, prev_state, cur_ts, delta;107	u32 key, cpu, pstate_idx;108	u64 *val;109 110	if (ctx->cpu_id > MAX_CPU)111		return 0;112 113	cpu = ctx->cpu_id;114 115	key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_TIME;116	cts = bpf_map_lookup_elem(&my_map, &key);117	if (!cts)118		return 0;119 120	key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_IDX;121	cstate = bpf_map_lookup_elem(&my_map, &key);122	if (!cstate)123		return 0;124 125	key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_TIME;126	pts = bpf_map_lookup_elem(&my_map, &key);127	if (!pts)128		return 0;129 130	key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_IDX;131	pstate = bpf_map_lookup_elem(&my_map, &key);132	if (!pstate)133		return 0;134 135	prev_state = *cstate;136	*cstate = ctx->state;137 138	if (!*cts) {139		*cts = bpf_ktime_get_ns();140		return 0;141	}142 143	cur_ts = bpf_ktime_get_ns();144	delta = cur_ts - *cts;145	*cts = cur_ts;146 147	/*148	 * When state doesn't equal to (u32)-1, the cpu will enter149	 * one idle state; for this case we need to record interval150	 * for the pstate.151	 *152	 *                 OPP2153	 *            +---------------------+154	 *     OPP1   |                     |155	 *   ---------+                     |156	 *                                  |  Idle state157	 *                                  +---------------158	 *159	 *            |<- pstate duration ->|160	 *            ^                     ^161	 *           pts                  cur_ts162	 */163	if (ctx->state != (u32)-1) {164 165		/* record pstate after have first cpu_frequency event */166		if (!*pts)167			return 0;168 169		delta = cur_ts - *pts;170 171		pstate_idx = find_cpu_pstate_idx(*pstate);172		if (pstate_idx >= MAX_PSTATE_ENTRIES)173			return 0;174 175		key = cpu * MAX_PSTATE_ENTRIES + pstate_idx;176		val = bpf_map_lookup_elem(&pstate_duration, &key);177		if (val)178			__sync_fetch_and_add((long *)val, delta);179 180	/*181	 * When state equal to (u32)-1, the cpu just exits from one182	 * specific idle state; for this case we need to record183	 * interval for the pstate.184	 *185	 *       OPP2186	 *   -----------+187	 *              |                          OPP1188	 *              |                     +-----------189	 *              |     Idle state      |190	 *              +---------------------+191	 *192	 *              |<- cstate duration ->|193	 *              ^                     ^194	 *             cts                  cur_ts195	 */196	} else {197 198		key = cpu * MAX_CSTATE_ENTRIES + prev_state;199		val = bpf_map_lookup_elem(&cstate_duration, &key);200		if (val)201			__sync_fetch_and_add((long *)val, delta);202	}203 204	/* Update timestamp for pstate as new start time */205	if (*pts)206		*pts = cur_ts;207 208	return 0;209}210 211SEC("tracepoint/power/cpu_frequency")212int bpf_prog2(struct cpu_args *ctx)213{214	u64 *pts, *cstate, *pstate, cur_ts, delta;215	u32 key, cpu, pstate_idx;216	u64 *val;217 218	cpu = ctx->cpu_id;219 220	key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_TIME;221	pts = bpf_map_lookup_elem(&my_map, &key);222	if (!pts)223		return 0;224 225	key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_IDX;226	pstate = bpf_map_lookup_elem(&my_map, &key);227	if (!pstate)228		return 0;229 230	key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_IDX;231	cstate = bpf_map_lookup_elem(&my_map, &key);232	if (!cstate)233		return 0;234 235	*pstate = ctx->state;236 237	if (!*pts) {238		*pts = bpf_ktime_get_ns();239		return 0;240	}241 242	cur_ts = bpf_ktime_get_ns();243	delta = cur_ts - *pts;244	*pts = cur_ts;245 246	/* When CPU is in idle, bail out to skip pstate statistics */247	if (*cstate != (u32)(-1))248		return 0;249 250	/*251	 * The cpu changes to another different OPP (in below diagram252	 * change frequency from OPP3 to OPP1), need recording interval253	 * for previous frequency OPP3 and update timestamp as start254	 * time for new frequency OPP1.255	 *256	 *                 OPP3257	 *            +---------------------+258	 *     OPP2   |                     |259	 *   ---------+                     |260	 *                                  |    OPP1261	 *                                  +---------------262	 *263	 *            |<- pstate duration ->|264	 *            ^                     ^265	 *           pts                  cur_ts266	 */267	pstate_idx = find_cpu_pstate_idx(*pstate);268	if (pstate_idx >= MAX_PSTATE_ENTRIES)269		return 0;270 271	key = cpu * MAX_PSTATE_ENTRIES + pstate_idx;272	val = bpf_map_lookup_elem(&pstate_duration, &key);273	if (val)274		__sync_fetch_and_add((long *)val, delta);275 276	return 0;277}278 279char _license[] SEC("license") = "GPL";280u32 _version SEC("version") = LINUX_VERSION_CODE;281