File Index
Files and directories
Sign in
FILE

sample_filter.bpf.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/util/bpf_skel/sample_filter.bpf.c C
  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_HASH);
 13	__type(key, int);
 14	__type(value, struct perf_bpf_filter_entry[MAX_FILTERS]);
 15	__uint(max_entries, 1);
 16} filters SEC(".maps");
 17
 18/*
 19 * An evsel has multiple instances for each CPU or task but we need a single
 20 * id to be used as a key for the idx_hash.  This hashmap would translate the
 21 * instance's ID to a representative ID.
 22 */
 23struct event_hash {
 24	__uint(type, BPF_MAP_TYPE_HASH);
 25	__type(key, __u64);
 26	__type(value, __u64);
 27	__uint(max_entries, 1);
 28} event_hash SEC(".maps");
 29
 30/* tgid/evtid to filter index */
 31struct idx_hash {
 32	__uint(type, BPF_MAP_TYPE_HASH);
 33	__type(key, struct idx_hash_key);
 34	__type(value, int);
 35	__uint(max_entries, 1);
 36} idx_hash SEC(".maps");
 37
 38/* tgid to filter index */
 39struct lost_count {
 40	__uint(type, BPF_MAP_TYPE_ARRAY);
 41	__type(key, int);
 42	__type(value, int);
 43	__uint(max_entries, 1);
 44} dropped SEC(".maps");
 45
 46volatile const int use_idx_hash;
 47
 48void *bpf_cast_to_kern_ctx(void *) __ksym;
 49
 50/* new kernel perf_sample_data definition */
 51struct perf_sample_data___new {
 52	__u64 sample_flags;
 53} __attribute__((preserve_access_index));
 54
 55/* new kernel perf_mem_data_src definition */
 56union perf_mem_data_src___new {
 57	__u64 val;
 58	struct {
 59		__u64   mem_op:5,	/* type of opcode */
 60			mem_lvl:14,	/* memory hierarchy level */
 61			mem_snoop:5,	/* snoop mode */
 62			mem_lock:2,	/* lock instr */
 63			mem_dtlb:7,	/* tlb access */
 64			mem_lvl_num:4,	/* memory hierarchy level number */
 65			mem_remote:1,   /* remote */
 66			mem_snoopx:2,	/* snoop mode, ext */
 67			mem_blk:3,	/* access blocked */
 68			mem_hops:3,	/* hop level */
 69			mem_rsvd:18;
 70	};
 71};
 72
 73/* helper function to return the given perf sample data */
 74static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx,
 75				    struct perf_bpf_filter_entry *entry)
 76{
 77	struct perf_sample_data___new *data = (void *)kctx->data;
 78
 79	if (!bpf_core_field_exists(data->sample_flags))
 80		return 0;
 81
 82#define BUILD_CHECK_SAMPLE(x)					\
 83	_Static_assert((1 << (PBF_TERM_##x - PBF_TERM_SAMPLE_START)) == PERF_SAMPLE_##x, \
 84		"Mismatched PBF term to sample bit " #x)
 85	BUILD_CHECK_SAMPLE(IP);
 86	BUILD_CHECK_SAMPLE(TID);
 87	BUILD_CHECK_SAMPLE(TIME);
 88	BUILD_CHECK_SAMPLE(ADDR);
 89	BUILD_CHECK_SAMPLE(ID);
 90	BUILD_CHECK_SAMPLE(CPU);
 91	BUILD_CHECK_SAMPLE(PERIOD);
 92	BUILD_CHECK_SAMPLE(WEIGHT);
 93	BUILD_CHECK_SAMPLE(DATA_SRC);
 94	BUILD_CHECK_SAMPLE(TRANSACTION);
 95	BUILD_CHECK_SAMPLE(PHYS_ADDR);
 96	BUILD_CHECK_SAMPLE(CGROUP);
 97	BUILD_CHECK_SAMPLE(DATA_PAGE_SIZE);
 98	BUILD_CHECK_SAMPLE(CODE_PAGE_SIZE);
 99	BUILD_CHECK_SAMPLE(WEIGHT_STRUCT);
100#undef BUILD_CHECK_SAMPLE
101
102	/* For sample terms check the sample bit is set. */
103	if (entry->term >= PBF_TERM_SAMPLE_START && entry->term <= PBF_TERM_SAMPLE_END &&
104	    (data->sample_flags & (1 << (entry->term - PBF_TERM_SAMPLE_START))) == 0)
105		return 0;
106
107	switch (entry->term) {
108	case PBF_TERM_IP:
109		return kctx->data->ip;
110	case PBF_TERM_ID:
111		return kctx->data->id;
112	case PBF_TERM_TID:
113		if (entry->part)
114			return kctx->data->tid_entry.pid;
115		else
116			return kctx->data->tid_entry.tid;
117	case PBF_TERM_CPU:
118		return kctx->data->cpu_entry.cpu;
119	case PBF_TERM_TIME:
120		return kctx->data->time;
121	case PBF_TERM_ADDR:
122		return kctx->data->addr;
123	case PBF_TERM_PERIOD:
124		return kctx->data->period;
125	case PBF_TERM_TRANSACTION:
126		return kctx->data->txn;
127	case PBF_TERM_WEIGHT_STRUCT:
128		if (entry->part == 1)
129			return kctx->data->weight.var1_dw;
130		if (entry->part == 2)
131			return kctx->data->weight.var2_w;
132		if (entry->part == 3)
133			return kctx->data->weight.var3_w;
134		/* fall through */
135	case PBF_TERM_WEIGHT:
136		return kctx->data->weight.full;
137	case PBF_TERM_PHYS_ADDR:
138		return kctx->data->phys_addr;
139	case PBF_TERM_CGROUP:
140		return kctx->data->cgroup;
141	case PBF_TERM_CODE_PAGE_SIZE:
142		return kctx->data->code_page_size;
143	case PBF_TERM_DATA_PAGE_SIZE:
144		return kctx->data->data_page_size;
145	case PBF_TERM_DATA_SRC:
146		if (entry->part == 1)
147			return kctx->data->data_src.mem_op;
148		if (entry->part == 2)
149			return kctx->data->data_src.mem_lvl_num;
150		if (entry->part == 3) {
151			__u32 snoop = kctx->data->data_src.mem_snoop;
152			__u32 snoopx = kctx->data->data_src.mem_snoopx;
153
154			return (snoopx << 5) | snoop;
155		}
156		if (entry->part == 4)
157			return kctx->data->data_src.mem_remote;
158		if (entry->part == 5)
159			return kctx->data->data_src.mem_lock;
160		if (entry->part == 6)
161			return kctx->data->data_src.mem_dtlb;
162		if (entry->part == 7)
163			return kctx->data->data_src.mem_blk;
164		if (entry->part == 8) {
165			union perf_mem_data_src___new *data = (void *)&kctx->data->data_src;
166
167			if (__builtin_preserve_field_info(data->mem_hops, BPF_FIELD_EXISTS))
168				return data->mem_hops;
169
170			return 0;
171		}
172		/* return the whole word */
173		return kctx->data->data_src.val;
174	case PBF_TERM_UID:
175		return bpf_get_current_uid_gid() & 0xFFFFFFFF;
176	case PBF_TERM_GID:
177		return bpf_get_current_uid_gid() >> 32;
178	case PBF_TERM_NONE:
179	case __PBF_UNUSED_TERM4:
180	case __PBF_UNUSED_TERM5:
181	case __PBF_UNUSED_TERM9:
182	case __PBF_UNUSED_TERM10:
183	case __PBF_UNUSED_TERM11:
184	case __PBF_UNUSED_TERM12:
185	case __PBF_UNUSED_TERM13:
186	case __PBF_UNUSED_TERM16:
187	case __PBF_UNUSED_TERM18:
188	case __PBF_UNUSED_TERM20:
189	default:
190		break;
191	}
192	return 0;
193}
194
195#define CHECK_RESULT(data, op, val)			\
196	if (!(data op val)) {				\
197		if (!in_group)				\
198			goto drop;			\
199	} else if (in_group) {				\
200		group_result = 1;			\
201	}
202
203/* BPF program to be called from perf event overflow handler */
204SEC("perf_event")
205int perf_sample_filter(void *ctx)
206{
207	struct bpf_perf_event_data_kern *kctx;
208	struct perf_bpf_filter_entry *entry;
209	__u64 sample_data;
210	int in_group = 0;
211	int group_result = 0;
212	int i, k;
213	int *losts;
214
215	kctx = bpf_cast_to_kern_ctx(ctx);
216
217	k = 0;
218
219	if (use_idx_hash) {
220		struct idx_hash_key key = {
221			.tgid = bpf_get_current_pid_tgid() >> 32,
222		};
223		__u64 eid = kctx->event->id;
224		__u64 *key_id;
225		int *idx;
226
227		/* get primary_event_id */
228		if (kctx->event->parent)
229			eid = kctx->event->parent->id;
230
231		key_id = bpf_map_lookup_elem(&event_hash, &eid);
232		if (key_id == NULL)
233			goto drop;
234
235		key.evt_id = *key_id;
236
237		idx = bpf_map_lookup_elem(&idx_hash, &key);
238		if (idx)
239			k = *idx;
240		else
241			goto drop;
242	}
243
244	entry = bpf_map_lookup_elem(&filters, &k);
245	if (entry == NULL)
246		goto drop;
247
248	for (i = 0; i < MAX_FILTERS; i++) {
249		sample_data = perf_get_sample(kctx, &entry[i]);
250
251		switch (entry[i].op) {
252		case PBF_OP_EQ:
253			CHECK_RESULT(sample_data, ==, entry[i].value)
254			break;
255		case PBF_OP_NEQ:
256			CHECK_RESULT(sample_data, !=, entry[i].value)
257			break;
258		case PBF_OP_GT:
259			CHECK_RESULT(sample_data, >, entry[i].value)
260			break;
261		case PBF_OP_GE:
262			CHECK_RESULT(sample_data, >=, entry[i].value)
263			break;
264		case PBF_OP_LT:
265			CHECK_RESULT(sample_data, <, entry[i].value)
266			break;
267		case PBF_OP_LE:
268			CHECK_RESULT(sample_data, <=, entry[i].value)
269			break;
270		case PBF_OP_AND:
271			CHECK_RESULT(sample_data, &, entry[i].value)
272			break;
273		case PBF_OP_GROUP_BEGIN:
274			in_group = 1;
275			group_result = 0;
276			break;
277		case PBF_OP_GROUP_END:
278			if (group_result == 0)
279				goto drop;
280			in_group = 0;
281			break;
282		case PBF_OP_DONE:
283			/* no failures so far, accept it */
284			return 1;
285		}
286	}
287	/* generate sample data */
288	return 1;
289
290drop:
291	losts = bpf_map_lookup_elem(&dropped, &k);
292	if (losts != NULL)
293		__sync_fetch_and_add(losts, 1);
294
295	return 0;
296}
297
298char LICENSE[] SEC("license") = "Dual BSD/GPL";
~/ Read-only file preview.