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