188886309SYosry Ahmed // SPDX-License-Identifier: GPL-2.0-only
288886309SYosry Ahmed /*
388886309SYosry Ahmed  * Copyright 2022 Google LLC.
488886309SYosry Ahmed  */
588886309SYosry Ahmed #include "vmlinux.h"
688886309SYosry Ahmed #include <bpf/bpf_helpers.h>
788886309SYosry Ahmed #include <bpf/bpf_tracing.h>
888886309SYosry Ahmed #include <bpf/bpf_core_read.h>
988886309SYosry Ahmed 
1088886309SYosry Ahmed char _license[] SEC("license") = "GPL";
1188886309SYosry Ahmed 
12*e0401dceSYosry Ahmed struct percpu_attach_counter {
1388886309SYosry Ahmed 	/* Previous percpu state, to figure out if we have new updates */
1488886309SYosry Ahmed 	__u64 prev;
1588886309SYosry Ahmed 	/* Current percpu state */
1688886309SYosry Ahmed 	__u64 state;
1788886309SYosry Ahmed };
1888886309SYosry Ahmed 
19*e0401dceSYosry Ahmed struct attach_counter {
2088886309SYosry Ahmed 	/* State propagated through children, pending aggregation */
2188886309SYosry Ahmed 	__u64 pending;
2288886309SYosry Ahmed 	/* Total state, including all cpus and all children */
2388886309SYosry Ahmed 	__u64 state;
2488886309SYosry Ahmed };
2588886309SYosry Ahmed 
2688886309SYosry Ahmed struct {
2788886309SYosry Ahmed 	__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
28*e0401dceSYosry Ahmed 	__uint(max_entries, 1024);
2988886309SYosry Ahmed 	__type(key, __u64);
30*e0401dceSYosry Ahmed 	__type(value, struct percpu_attach_counter);
31*e0401dceSYosry Ahmed } percpu_attach_counters SEC(".maps");
3288886309SYosry Ahmed 
3388886309SYosry Ahmed struct {
3488886309SYosry Ahmed 	__uint(type, BPF_MAP_TYPE_HASH);
35*e0401dceSYosry Ahmed 	__uint(max_entries, 1024);
3688886309SYosry Ahmed 	__type(key, __u64);
37*e0401dceSYosry Ahmed 	__type(value, struct attach_counter);
38*e0401dceSYosry Ahmed } attach_counters SEC(".maps");
3988886309SYosry Ahmed 
4088886309SYosry Ahmed extern void cgroup_rstat_updated(struct cgroup *cgrp, int cpu) __ksym;
4188886309SYosry Ahmed extern void cgroup_rstat_flush(struct cgroup *cgrp) __ksym;
4288886309SYosry Ahmed 
cgroup_id(struct cgroup * cgrp)4388886309SYosry Ahmed static uint64_t cgroup_id(struct cgroup *cgrp)
4488886309SYosry Ahmed {
4588886309SYosry Ahmed 	return cgrp->kn->id;
4688886309SYosry Ahmed }
4788886309SYosry Ahmed 
create_percpu_attach_counter(__u64 cg_id,__u64 state)48*e0401dceSYosry Ahmed static int create_percpu_attach_counter(__u64 cg_id, __u64 state)
4988886309SYosry Ahmed {
50*e0401dceSYosry Ahmed 	struct percpu_attach_counter pcpu_init = {.state = state, .prev = 0};
5188886309SYosry Ahmed 
52*e0401dceSYosry Ahmed 	return bpf_map_update_elem(&percpu_attach_counters, &cg_id,
5388886309SYosry Ahmed 				   &pcpu_init, BPF_NOEXIST);
5488886309SYosry Ahmed }
5588886309SYosry Ahmed 
create_attach_counter(__u64 cg_id,__u64 state,__u64 pending)56*e0401dceSYosry Ahmed static int create_attach_counter(__u64 cg_id, __u64 state, __u64 pending)
5788886309SYosry Ahmed {
58*e0401dceSYosry Ahmed 	struct attach_counter init = {.state = state, .pending = pending};
5988886309SYosry Ahmed 
60*e0401dceSYosry Ahmed 	return bpf_map_update_elem(&attach_counters, &cg_id,
6188886309SYosry Ahmed 				   &init, BPF_NOEXIST);
6288886309SYosry Ahmed }
6388886309SYosry Ahmed 
64*e0401dceSYosry Ahmed SEC("fentry/cgroup_attach_task")
BPF_PROG(counter,struct cgroup * dst_cgrp,struct task_struct * leader,bool threadgroup)65*e0401dceSYosry Ahmed int BPF_PROG(counter, struct cgroup *dst_cgrp, struct task_struct *leader,
66*e0401dceSYosry Ahmed 	     bool threadgroup)
6788886309SYosry Ahmed {
68*e0401dceSYosry Ahmed 	__u64 cg_id = cgroup_id(dst_cgrp);
69*e0401dceSYosry Ahmed 	struct percpu_attach_counter *pcpu_counter = bpf_map_lookup_elem(
70*e0401dceSYosry Ahmed 			&percpu_attach_counters,
7188886309SYosry Ahmed 			&cg_id);
72*e0401dceSYosry Ahmed 
73*e0401dceSYosry Ahmed 	if (pcpu_counter)
74*e0401dceSYosry Ahmed 		pcpu_counter->state += 1;
75*e0401dceSYosry Ahmed 	else if (create_percpu_attach_counter(cg_id, 1))
7688886309SYosry Ahmed 		return 0;
7788886309SYosry Ahmed 
78*e0401dceSYosry Ahmed 	cgroup_rstat_updated(dst_cgrp, bpf_get_smp_processor_id());
7988886309SYosry Ahmed 	return 0;
8088886309SYosry Ahmed }
8188886309SYosry Ahmed 
8288886309SYosry Ahmed SEC("fentry/bpf_rstat_flush")
BPF_PROG(flusher,struct cgroup * cgrp,struct cgroup * parent,int cpu)83*e0401dceSYosry Ahmed int BPF_PROG(flusher, struct cgroup *cgrp, struct cgroup *parent, int cpu)
8488886309SYosry Ahmed {
85*e0401dceSYosry Ahmed 	struct percpu_attach_counter *pcpu_counter;
86*e0401dceSYosry Ahmed 	struct attach_counter *total_counter, *parent_counter;
8788886309SYosry Ahmed 	__u64 cg_id = cgroup_id(cgrp);
8888886309SYosry Ahmed 	__u64 parent_cg_id = parent ? cgroup_id(parent) : 0;
8988886309SYosry Ahmed 	__u64 state;
9088886309SYosry Ahmed 	__u64 delta = 0;
9188886309SYosry Ahmed 
9288886309SYosry Ahmed 	/* Add CPU changes on this level since the last flush */
93*e0401dceSYosry Ahmed 	pcpu_counter = bpf_map_lookup_percpu_elem(&percpu_attach_counters,
9488886309SYosry Ahmed 						  &cg_id, cpu);
95*e0401dceSYosry Ahmed 	if (pcpu_counter) {
96*e0401dceSYosry Ahmed 		state = pcpu_counter->state;
97*e0401dceSYosry Ahmed 		delta += state - pcpu_counter->prev;
98*e0401dceSYosry Ahmed 		pcpu_counter->prev = state;
9988886309SYosry Ahmed 	}
10088886309SYosry Ahmed 
101*e0401dceSYosry Ahmed 	total_counter = bpf_map_lookup_elem(&attach_counters, &cg_id);
102*e0401dceSYosry Ahmed 	if (!total_counter) {
103*e0401dceSYosry Ahmed 		if (create_attach_counter(cg_id, delta, 0))
10488886309SYosry Ahmed 			return 0;
10588886309SYosry Ahmed 		goto update_parent;
10688886309SYosry Ahmed 	}
10788886309SYosry Ahmed 
10888886309SYosry Ahmed 	/* Collect pending stats from subtree */
109*e0401dceSYosry Ahmed 	if (total_counter->pending) {
110*e0401dceSYosry Ahmed 		delta += total_counter->pending;
111*e0401dceSYosry Ahmed 		total_counter->pending = 0;
11288886309SYosry Ahmed 	}
11388886309SYosry Ahmed 
11488886309SYosry Ahmed 	/* Propagate changes to this cgroup's total */
115*e0401dceSYosry Ahmed 	total_counter->state += delta;
11688886309SYosry Ahmed 
11788886309SYosry Ahmed update_parent:
11888886309SYosry Ahmed 	/* Skip if there are no changes to propagate, or no parent */
11988886309SYosry Ahmed 	if (!delta || !parent_cg_id)
12088886309SYosry Ahmed 		return 0;
12188886309SYosry Ahmed 
12288886309SYosry Ahmed 	/* Propagate changes to cgroup's parent */
123*e0401dceSYosry Ahmed 	parent_counter = bpf_map_lookup_elem(&attach_counters,
12488886309SYosry Ahmed 					     &parent_cg_id);
125*e0401dceSYosry Ahmed 	if (parent_counter)
126*e0401dceSYosry Ahmed 		parent_counter->pending += delta;
12788886309SYosry Ahmed 	else
128*e0401dceSYosry Ahmed 		create_attach_counter(parent_cg_id, 0, delta);
12988886309SYosry Ahmed 	return 0;
13088886309SYosry Ahmed }
13188886309SYosry Ahmed 
13288886309SYosry Ahmed SEC("iter.s/cgroup")
BPF_PROG(dumper,struct bpf_iter_meta * meta,struct cgroup * cgrp)133*e0401dceSYosry Ahmed int BPF_PROG(dumper, struct bpf_iter_meta *meta, struct cgroup *cgrp)
13488886309SYosry Ahmed {
13588886309SYosry Ahmed 	struct seq_file *seq = meta->seq;
136*e0401dceSYosry Ahmed 	struct attach_counter *total_counter;
13788886309SYosry Ahmed 	__u64 cg_id = cgrp ? cgroup_id(cgrp) : 0;
13888886309SYosry Ahmed 
13988886309SYosry Ahmed 	/* Do nothing for the terminal call */
14088886309SYosry Ahmed 	if (!cg_id)
14188886309SYosry Ahmed 		return 1;
14288886309SYosry Ahmed 
14388886309SYosry Ahmed 	/* Flush the stats to make sure we get the most updated numbers */
14488886309SYosry Ahmed 	cgroup_rstat_flush(cgrp);
14588886309SYosry Ahmed 
146*e0401dceSYosry Ahmed 	total_counter = bpf_map_lookup_elem(&attach_counters, &cg_id);
147*e0401dceSYosry Ahmed 	if (!total_counter) {
148*e0401dceSYosry Ahmed 		BPF_SEQ_PRINTF(seq, "cg_id: %llu, attach_counter: 0\n",
14988886309SYosry Ahmed 			       cg_id);
15088886309SYosry Ahmed 	} else {
151*e0401dceSYosry Ahmed 		BPF_SEQ_PRINTF(seq, "cg_id: %llu, attach_counter: %llu\n",
152*e0401dceSYosry Ahmed 			       cg_id, total_counter->state);
15388886309SYosry Ahmed 	}
154*e0401dceSYosry Ahmed 	return 0;
15588886309SYosry Ahmed }
156