162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
262306a36Sopenharmony_ci// Copyright (c) 2020 Facebook
362306a36Sopenharmony_ci#include <vmlinux.h>
462306a36Sopenharmony_ci#include <bpf/bpf_helpers.h>
562306a36Sopenharmony_ci#include <bpf/bpf_tracing.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistruct bpf_perf_event_value___local {
862306a36Sopenharmony_ci	__u64 counter;
962306a36Sopenharmony_ci	__u64 enabled;
1062306a36Sopenharmony_ci	__u64 running;
1162306a36Sopenharmony_ci} __attribute__((preserve_access_index));
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* map of perf event fds, num_cpu * num_metric entries */
1462306a36Sopenharmony_cistruct {
1562306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
1662306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
1762306a36Sopenharmony_ci	__uint(value_size, sizeof(int));
1862306a36Sopenharmony_ci} events SEC(".maps");
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* readings at fentry */
2162306a36Sopenharmony_cistruct {
2262306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
2362306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
2462306a36Sopenharmony_ci	__uint(value_size, sizeof(struct bpf_perf_event_value___local));
2562306a36Sopenharmony_ci} fentry_readings SEC(".maps");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* accumulated readings */
2862306a36Sopenharmony_cistruct {
2962306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
3062306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
3162306a36Sopenharmony_ci	__uint(value_size, sizeof(struct bpf_perf_event_value___local));
3262306a36Sopenharmony_ci} accum_readings SEC(".maps");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* sample counts, one per cpu */
3562306a36Sopenharmony_cistruct {
3662306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
3762306a36Sopenharmony_ci	__uint(key_size, sizeof(u32));
3862306a36Sopenharmony_ci	__uint(value_size, sizeof(u64));
3962306a36Sopenharmony_ci} counts SEC(".maps");
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ciconst volatile __u32 num_cpu = 1;
4262306a36Sopenharmony_ciconst volatile __u32 num_metric = 1;
4362306a36Sopenharmony_ci#define MAX_NUM_MATRICS 4
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciSEC("fentry/XXX")
4662306a36Sopenharmony_ciint BPF_PROG(fentry_XXX)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct bpf_perf_event_value___local *ptrs[MAX_NUM_MATRICS];
4962306a36Sopenharmony_ci	u32 key = bpf_get_smp_processor_id();
5062306a36Sopenharmony_ci	u32 i;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* look up before reading, to reduce error */
5362306a36Sopenharmony_ci	for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++) {
5462306a36Sopenharmony_ci		u32 flag = i;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		ptrs[i] = bpf_map_lookup_elem(&fentry_readings, &flag);
5762306a36Sopenharmony_ci		if (!ptrs[i])
5862306a36Sopenharmony_ci			return 0;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++) {
6262306a36Sopenharmony_ci		struct bpf_perf_event_value___local reading;
6362306a36Sopenharmony_ci		int err;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		err = bpf_perf_event_read_value(&events, key, (void *)&reading,
6662306a36Sopenharmony_ci						sizeof(reading));
6762306a36Sopenharmony_ci		if (err)
6862306a36Sopenharmony_ci			return 0;
6962306a36Sopenharmony_ci		*(ptrs[i]) = reading;
7062306a36Sopenharmony_ci		key += num_cpu;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic inline void
7762306a36Sopenharmony_cifexit_update_maps(u32 id, struct bpf_perf_event_value___local *after)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct bpf_perf_event_value___local *before, diff;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	before = bpf_map_lookup_elem(&fentry_readings, &id);
8262306a36Sopenharmony_ci	/* only account samples with a valid fentry_reading */
8362306a36Sopenharmony_ci	if (before && before->counter) {
8462306a36Sopenharmony_ci		struct bpf_perf_event_value___local *accum;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		diff.counter = after->counter - before->counter;
8762306a36Sopenharmony_ci		diff.enabled = after->enabled - before->enabled;
8862306a36Sopenharmony_ci		diff.running = after->running - before->running;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		accum = bpf_map_lookup_elem(&accum_readings, &id);
9162306a36Sopenharmony_ci		if (accum) {
9262306a36Sopenharmony_ci			accum->counter += diff.counter;
9362306a36Sopenharmony_ci			accum->enabled += diff.enabled;
9462306a36Sopenharmony_ci			accum->running += diff.running;
9562306a36Sopenharmony_ci		}
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciSEC("fexit/XXX")
10062306a36Sopenharmony_ciint BPF_PROG(fexit_XXX)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct bpf_perf_event_value___local readings[MAX_NUM_MATRICS];
10362306a36Sopenharmony_ci	u32 cpu = bpf_get_smp_processor_id();
10462306a36Sopenharmony_ci	u32 i, zero = 0;
10562306a36Sopenharmony_ci	int err;
10662306a36Sopenharmony_ci	u64 *count;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* read all events before updating the maps, to reduce error */
10962306a36Sopenharmony_ci	for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++) {
11062306a36Sopenharmony_ci		err = bpf_perf_event_read_value(&events, cpu + i * num_cpu,
11162306a36Sopenharmony_ci						(void *)(readings + i),
11262306a36Sopenharmony_ci						sizeof(*readings));
11362306a36Sopenharmony_ci		if (err)
11462306a36Sopenharmony_ci			return 0;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	count = bpf_map_lookup_elem(&counts, &zero);
11762306a36Sopenharmony_ci	if (count) {
11862306a36Sopenharmony_ci		*count += 1;
11962306a36Sopenharmony_ci		for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++)
12062306a36Sopenharmony_ci			fexit_update_maps(i, &readings[i]);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cichar LICENSE[] SEC("license") = "Dual BSD/GPL";
126