1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2// Copyright (c) 2023 Google
3#include "vmlinux.h"
4#include <bpf/bpf_helpers.h>
5#include <bpf/bpf_tracing.h>
6#include <bpf/bpf_core_read.h>
7
8#include "sample-filter.h"
9
10/* BPF map that will be filled by user space */
11struct filters {
12	__uint(type, BPF_MAP_TYPE_ARRAY);
13	__type(key, int);
14	__type(value, struct perf_bpf_filter_entry);
15	__uint(max_entries, MAX_FILTERS);
16} filters SEC(".maps");
17
18int dropped;
19
20void *bpf_cast_to_kern_ctx(void *) __ksym;
21
22/* new kernel perf_sample_data definition */
23struct perf_sample_data___new {
24	__u64 sample_flags;
25} __attribute__((preserve_access_index));
26
27/* new kernel perf_mem_data_src definition */
28union perf_mem_data_src___new {
29	__u64 val;
30	struct {
31		__u64   mem_op:5,	/* type of opcode */
32			mem_lvl:14,	/* memory hierarchy level */
33			mem_snoop:5,	/* snoop mode */
34			mem_lock:2,	/* lock instr */
35			mem_dtlb:7,	/* tlb access */
36			mem_lvl_num:4,	/* memory hierarchy level number */
37			mem_remote:1,   /* remote */
38			mem_snoopx:2,	/* snoop mode, ext */
39			mem_blk:3,	/* access blocked */
40			mem_hops:3,	/* hop level */
41			mem_rsvd:18;
42	};
43};
44
45/* helper function to return the given perf sample data */
46static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx,
47				    struct perf_bpf_filter_entry *entry)
48{
49	struct perf_sample_data___new *data = (void *)kctx->data;
50
51	if (!bpf_core_field_exists(data->sample_flags) ||
52	    (data->sample_flags & entry->flags) == 0)
53		return 0;
54
55	switch (entry->flags) {
56	case PERF_SAMPLE_IP:
57		return kctx->data->ip;
58	case PERF_SAMPLE_ID:
59		return kctx->data->id;
60	case PERF_SAMPLE_TID:
61		if (entry->part)
62			return kctx->data->tid_entry.pid;
63		else
64			return kctx->data->tid_entry.tid;
65	case PERF_SAMPLE_CPU:
66		return kctx->data->cpu_entry.cpu;
67	case PERF_SAMPLE_TIME:
68		return kctx->data->time;
69	case PERF_SAMPLE_ADDR:
70		return kctx->data->addr;
71	case PERF_SAMPLE_PERIOD:
72		return kctx->data->period;
73	case PERF_SAMPLE_TRANSACTION:
74		return kctx->data->txn;
75	case PERF_SAMPLE_WEIGHT_STRUCT:
76		if (entry->part == 1)
77			return kctx->data->weight.var1_dw;
78		if (entry->part == 2)
79			return kctx->data->weight.var2_w;
80		if (entry->part == 3)
81			return kctx->data->weight.var3_w;
82		/* fall through */
83	case PERF_SAMPLE_WEIGHT:
84		return kctx->data->weight.full;
85	case PERF_SAMPLE_PHYS_ADDR:
86		return kctx->data->phys_addr;
87	case PERF_SAMPLE_CODE_PAGE_SIZE:
88		return kctx->data->code_page_size;
89	case PERF_SAMPLE_DATA_PAGE_SIZE:
90		return kctx->data->data_page_size;
91	case PERF_SAMPLE_DATA_SRC:
92		if (entry->part == 1)
93			return kctx->data->data_src.mem_op;
94		if (entry->part == 2)
95			return kctx->data->data_src.mem_lvl_num;
96		if (entry->part == 3) {
97			__u32 snoop = kctx->data->data_src.mem_snoop;
98			__u32 snoopx = kctx->data->data_src.mem_snoopx;
99
100			return (snoopx << 5) | snoop;
101		}
102		if (entry->part == 4)
103			return kctx->data->data_src.mem_remote;
104		if (entry->part == 5)
105			return kctx->data->data_src.mem_lock;
106		if (entry->part == 6)
107			return kctx->data->data_src.mem_dtlb;
108		if (entry->part == 7)
109			return kctx->data->data_src.mem_blk;
110		if (entry->part == 8) {
111			union perf_mem_data_src___new *data = (void *)&kctx->data->data_src;
112
113			if (bpf_core_field_exists(data->mem_hops))
114				return data->mem_hops;
115
116			return 0;
117		}
118		/* return the whole word */
119		return kctx->data->data_src.val;
120	default:
121		break;
122	}
123	return 0;
124}
125
126#define CHECK_RESULT(data, op, val)			\
127	if (!(data op val)) {				\
128		if (!in_group)				\
129			goto drop;			\
130	} else if (in_group) {				\
131		group_result = 1;			\
132	}
133
134/* BPF program to be called from perf event overflow handler */
135SEC("perf_event")
136int perf_sample_filter(void *ctx)
137{
138	struct bpf_perf_event_data_kern *kctx;
139	struct perf_bpf_filter_entry *entry;
140	__u64 sample_data;
141	int in_group = 0;
142	int group_result = 0;
143	int i;
144
145	kctx = bpf_cast_to_kern_ctx(ctx);
146
147	for (i = 0; i < MAX_FILTERS; i++) {
148		int key = i; /* needed for verifier :( */
149
150		entry = bpf_map_lookup_elem(&filters, &key);
151		if (entry == NULL)
152			break;
153		sample_data = perf_get_sample(kctx, entry);
154
155		switch (entry->op) {
156		case PBF_OP_EQ:
157			CHECK_RESULT(sample_data, ==, entry->value)
158			break;
159		case PBF_OP_NEQ:
160			CHECK_RESULT(sample_data, !=, entry->value)
161			break;
162		case PBF_OP_GT:
163			CHECK_RESULT(sample_data, >, entry->value)
164			break;
165		case PBF_OP_GE:
166			CHECK_RESULT(sample_data, >=, entry->value)
167			break;
168		case PBF_OP_LT:
169			CHECK_RESULT(sample_data, <, entry->value)
170			break;
171		case PBF_OP_LE:
172			CHECK_RESULT(sample_data, <=, entry->value)
173			break;
174		case PBF_OP_AND:
175			CHECK_RESULT(sample_data, &, entry->value)
176			break;
177		case PBF_OP_GROUP_BEGIN:
178			in_group = 1;
179			group_result = 0;
180			break;
181		case PBF_OP_GROUP_END:
182			if (group_result == 0)
183				goto drop;
184			in_group = 0;
185			break;
186		}
187	}
188	/* generate sample data */
189	return 1;
190
191drop:
192	__sync_fetch_and_add(&dropped, 1);
193	return 0;
194}
195
196char LICENSE[] SEC("license") = "Dual BSD/GPL";
197