lkml.org 
[lkml]   [2023]   [Mar]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [PATCH 02/10] perf bpf filter: Implement event sample filtering
    Em Tue, Mar 14, 2023 at 04:42:29PM -0700, Namhyung Kim escreveu:
    > The BPF program will be attached to a perf_event and be triggered when
    > it overflows. It'd iterate the filters map and compare the sample
    > value according to the expression. If any of them fails, the sample
    > would be dropped.
    >
    > Also it needs to have the corresponding sample data for the expression
    > so it compares data->sample_flags with the given value. To access the
    > sample data, it uses the bpf_cast_to_kern_ctx() kfunc which was added
    > in v6.2 kernel.
    >
    > Acked-by: Jiri Olsa <jolsa@kernel.org>
    > Signed-off-by: Namhyung Kim <namhyung@kernel.org>


    I'm noticing this while building on a debian:11 container:

    GENSKEL /tmp/build/perf/util/bpf_skel/bperf_leader.skel.h
    GENSKEL /tmp/build/perf/util/bpf_skel/bperf_follower.skel.h
    GENSKEL /tmp/build/perf/util/bpf_skel/func_latency.skel.h
    GENSKEL /tmp/build/perf/util/bpf_skel/bpf_prog_profiler.skel.h
    GENSKEL /tmp/build/perf/util/bpf_skel/kwork_trace.skel.h
    GENSKEL /tmp/build/perf/util/bpf_skel/sample_filter.skel.h
    libbpf: failed to find BTF for extern 'bpf_cast_to_kern_ctx' [21] section: -2
    Error: failed to open BPF object file: No such file or directory
    make[2]: *** [Makefile.perf:1085: /tmp/build/perf/util/bpf_skel/sample_filter.skel.h] Error 254
    make[2]: *** Deleting file '/tmp/build/perf/util/bpf_skel/sample_filter.skel.h'
    make[2]: *** Waiting for unfinished jobs....
    make[1]: *** [Makefile.perf:236: sub-make] Error 2
    make: *** [Makefile:70: all] Error 2
    make: Leaving directory '/git/perf-6.3.0-rc1/tools/perf'
    + exit 1
    [perfbuilder@five 11]$

    - Arnaldo

    > ---
    > tools/perf/Makefile.perf | 2 +-
    > tools/perf/util/bpf-filter.c | 64 ++++++++++
    > tools/perf/util/bpf-filter.h | 26 ++--
    > tools/perf/util/bpf_skel/sample-filter.h | 24 ++++
    > tools/perf/util/bpf_skel/sample_filter.bpf.c | 126 +++++++++++++++++++
    > tools/perf/util/evsel.h | 7 +-
    > 6 files changed, 236 insertions(+), 13 deletions(-)
    > create mode 100644 tools/perf/util/bpf_skel/sample-filter.h
    > create mode 100644 tools/perf/util/bpf_skel/sample_filter.bpf.c
    >
    > diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
    > index dc9dda09b076..ed6b6a070f79 100644
    > --- a/tools/perf/Makefile.perf
    > +++ b/tools/perf/Makefile.perf
    > @@ -1050,7 +1050,7 @@ SKELETONS := $(SKEL_OUT)/bpf_prog_profiler.skel.h
    > SKELETONS += $(SKEL_OUT)/bperf_leader.skel.h $(SKEL_OUT)/bperf_follower.skel.h
    > SKELETONS += $(SKEL_OUT)/bperf_cgroup.skel.h $(SKEL_OUT)/func_latency.skel.h
    > SKELETONS += $(SKEL_OUT)/off_cpu.skel.h $(SKEL_OUT)/lock_contention.skel.h
    > -SKELETONS += $(SKEL_OUT)/kwork_trace.skel.h
    > +SKELETONS += $(SKEL_OUT)/kwork_trace.skel.h $(SKEL_OUT)/sample_filter.skel.h
    >
    > $(SKEL_TMP_OUT) $(LIBAPI_OUTPUT) $(LIBBPF_OUTPUT) $(LIBPERF_OUTPUT) $(LIBSUBCMD_OUTPUT) $(LIBSYMBOL_OUTPUT):
    > $(Q)$(MKDIR) -p $@
    > diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c
    > index c72e35d51240..f20e1bc03778 100644
    > --- a/tools/perf/util/bpf-filter.c
    > +++ b/tools/perf/util/bpf-filter.c
    > @@ -1,10 +1,74 @@
    > /* SPDX-License-Identifier: GPL-2.0 */
    > #include <stdlib.h>
    >
    > +#include <bpf/bpf.h>
    > +#include <linux/err.h>
    > +#include <internal/xyarray.h>
    > +
    > +#include "util/debug.h"
    > +#include "util/evsel.h"
    > +
    > #include "util/bpf-filter.h"
    > #include "util/bpf-filter-flex.h"
    > #include "util/bpf-filter-bison.h"
    >
    > +#include "bpf_skel/sample-filter.h"
    > +#include "bpf_skel/sample_filter.skel.h"
    > +
    > +#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
    > +
    > +int perf_bpf_filter__prepare(struct evsel *evsel)
    > +{
    > + int i, x, y, fd;
    > + struct sample_filter_bpf *skel;
    > + struct bpf_program *prog;
    > + struct bpf_link *link;
    > + struct perf_bpf_filter_expr *expr;
    > +
    > + skel = sample_filter_bpf__open_and_load();
    > + if (!skel) {
    > + pr_err("Failed to load perf sample-filter BPF skeleton\n");
    > + return -1;
    > + }
    > +
    > + i = 0;
    > + fd = bpf_map__fd(skel->maps.filters);
    > + list_for_each_entry(expr, &evsel->bpf_filters, list) {
    > + struct perf_bpf_filter_entry entry = {
    > + .op = expr->op,
    > + .flags = expr->sample_flags,
    > + .value = expr->val,
    > + };
    > + bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
    > + i++;
    > + }
    > +
    > + prog = skel->progs.perf_sample_filter;
    > + for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
    > + for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
    > + link = bpf_program__attach_perf_event(prog, FD(evsel, x, y));
    > + if (IS_ERR(link)) {
    > + pr_err("Failed to attach perf sample-filter program\n");
    > + return PTR_ERR(link);
    > + }
    > + }
    > + }
    > + evsel->bpf_skel = skel;
    > + return 0;
    > +}
    > +
    > +int perf_bpf_filter__destroy(struct evsel *evsel)
    > +{
    > + struct perf_bpf_filter_expr *expr, *tmp;
    > +
    > + list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) {
    > + list_del(&expr->list);
    > + free(expr);
    > + }
    > + sample_filter_bpf__destroy(evsel->bpf_skel);
    > + return 0;
    > +}
    > +
    > struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags,
    > enum perf_bpf_filter_op op,
    > unsigned long val)
    > diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h
    > index 93a0d3de038c..eb8e1ac43cdf 100644
    > --- a/tools/perf/util/bpf-filter.h
    > +++ b/tools/perf/util/bpf-filter.h
    > @@ -4,15 +4,7 @@
    >
    > #include <linux/list.h>
    >
    > -enum perf_bpf_filter_op {
    > - PBF_OP_EQ,
    > - PBF_OP_NEQ,
    > - PBF_OP_GT,
    > - PBF_OP_GE,
    > - PBF_OP_LT,
    > - PBF_OP_LE,
    > - PBF_OP_AND,
    > -};
    > +#include "bpf_skel/sample-filter.h"
    >
    > struct perf_bpf_filter_expr {
    > struct list_head list;
    > @@ -21,16 +13,30 @@ struct perf_bpf_filter_expr {
    > unsigned long val;
    > };
    >
    > +struct evsel;
    > +
    > #ifdef HAVE_BPF_SKEL
    > struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags,
    > enum perf_bpf_filter_op op,
    > unsigned long val);
    > int perf_bpf_filter__parse(struct list_head *expr_head, const char *str);
    > +int perf_bpf_filter__prepare(struct evsel *evsel);
    > +int perf_bpf_filter__destroy(struct evsel *evsel);
    > +
    > #else /* !HAVE_BPF_SKEL */
    > +
    > static inline int perf_bpf_filter__parse(struct list_head *expr_head __maybe_unused,
    > const char *str __maybe_unused)
    > {
    > - return -ENOSYS;
    > + return -EOPNOTSUPP;
    > +}
    > +static inline int perf_bpf_filter__prepare(struct evsel *evsel __maybe_unused)
    > +{
    > + return -EOPNOTSUPP;
    > +}
    > +static inline int perf_bpf_filter__destroy(struct evsel *evsel __maybe_unused)
    > +{
    > + return -EOPNOTSUPP;
    > }
    > #endif /* HAVE_BPF_SKEL*/
    > #endif /* PERF_UTIL_BPF_FILTER_H */
    > diff --git a/tools/perf/util/bpf_skel/sample-filter.h b/tools/perf/util/bpf_skel/sample-filter.h
    > new file mode 100644
    > index 000000000000..862060bfda14
    > --- /dev/null
    > +++ b/tools/perf/util/bpf_skel/sample-filter.h
    > @@ -0,0 +1,24 @@
    > +#ifndef PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H
    > +#define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H
    > +
    > +#define MAX_FILTERS 32
    > +
    > +/* supported filter operations */
    > +enum perf_bpf_filter_op {
    > + PBF_OP_EQ,
    > + PBF_OP_NEQ,
    > + PBF_OP_GT,
    > + PBF_OP_GE,
    > + PBF_OP_LT,
    > + PBF_OP_LE,
    > + PBF_OP_AND
    > +};
    > +
    > +/* BPF map entry for filtering */
    > +struct perf_bpf_filter_entry {
    > + enum perf_bpf_filter_op op;
    > + __u64 flags;
    > + __u64 value;
    > +};
    > +
    > +#endif /* PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H */
    > \ No newline at end of file
    > diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c
    > new file mode 100644
    > index 000000000000..c07256279c3e
    > --- /dev/null
    > +++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c
    > @@ -0,0 +1,126 @@
    > +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
    > +// Copyright (c) 2023 Google
    > +#include "vmlinux.h"
    > +#include <bpf/bpf_helpers.h>
    > +#include <bpf/bpf_tracing.h>
    > +#include <bpf/bpf_core_read.h>
    > +
    > +#include "sample-filter.h"
    > +
    > +/* BPF map that will be filled by user space */
    > +struct filters {
    > + __uint(type, BPF_MAP_TYPE_ARRAY);
    > + __type(key, int);
    > + __type(value, struct perf_bpf_filter_entry);
    > + __uint(max_entries, MAX_FILTERS);
    > +} filters SEC(".maps");
    > +
    > +int dropped;
    > +
    > +void *bpf_cast_to_kern_ctx(void *) __ksym;
    > +
    > +/* new kernel perf_sample_data definition */
    > +struct perf_sample_data___new {
    > + __u64 sample_flags;
    > +} __attribute__((preserve_access_index));
    > +
    > +/* helper function to return the given perf sample data */
    > +static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx,
    > + struct perf_bpf_filter_entry *entry)
    > +{
    > + struct perf_sample_data___new *data = (void *)kctx->data;
    > +
    > + if (!bpf_core_field_exists(data->sample_flags) ||
    > + (data->sample_flags & entry->flags) == 0)
    > + return 0;
    > +
    > + switch (entry->flags) {
    > + case PERF_SAMPLE_IP:
    > + return kctx->data->ip;
    > + case PERF_SAMPLE_ID:
    > + return kctx->data->id;
    > + case PERF_SAMPLE_TID:
    > + return kctx->data->tid_entry.tid;
    > + case PERF_SAMPLE_CPU:
    > + return kctx->data->cpu_entry.cpu;
    > + case PERF_SAMPLE_TIME:
    > + return kctx->data->time;
    > + case PERF_SAMPLE_ADDR:
    > + return kctx->data->addr;
    > + case PERF_SAMPLE_PERIOD:
    > + return kctx->data->period;
    > + case PERF_SAMPLE_TRANSACTION:
    > + return kctx->data->txn;
    > + case PERF_SAMPLE_WEIGHT:
    > + return kctx->data->weight.full;
    > + case PERF_SAMPLE_PHYS_ADDR:
    > + return kctx->data->phys_addr;
    > + case PERF_SAMPLE_CODE_PAGE_SIZE:
    > + return kctx->data->code_page_size;
    > + case PERF_SAMPLE_DATA_PAGE_SIZE:
    > + return kctx->data->data_page_size;
    > + default:
    > + break;
    > + }
    > + return 0;
    > +}
    > +
    > +/* BPF program to be called from perf event overflow handler */
    > +SEC("perf_event")
    > +int perf_sample_filter(void *ctx)
    > +{
    > + struct bpf_perf_event_data_kern *kctx;
    > + struct perf_bpf_filter_entry *entry;
    > + __u64 sample_data;
    > + int i;
    > +
    > + kctx = bpf_cast_to_kern_ctx(ctx);
    > +
    > + for (i = 0; i < MAX_FILTERS; i++) {
    > + int key = i; /* needed for verifier :( */
    > +
    > + entry = bpf_map_lookup_elem(&filters, &key);
    > + if (entry == NULL)
    > + break;
    > + sample_data = perf_get_sample(kctx, entry);
    > +
    > + switch (entry->op) {
    > + case PBF_OP_EQ:
    > + if (!(sample_data == entry->value))
    > + goto drop;
    > + break;
    > + case PBF_OP_NEQ:
    > + if (!(sample_data != entry->value))
    > + goto drop;
    > + break;
    > + case PBF_OP_GT:
    > + if (!(sample_data > entry->value))
    > + goto drop;
    > + break;
    > + case PBF_OP_GE:
    > + if (!(sample_data >= entry->value))
    > + goto drop;
    > + break;
    > + case PBF_OP_LT:
    > + if (!(sample_data < entry->value))
    > + goto drop;
    > + break;
    > + case PBF_OP_LE:
    > + if (!(sample_data <= entry->value))
    > + goto drop;
    > + break;
    > + case PBF_OP_AND:
    > + if (!(sample_data & entry->value))
    > + goto drop;
    > + break;
    > + }
    > + }
    > + /* generate sample data */
    > + return 1;
    > +
    > +drop:
    > + __sync_fetch_and_add(&dropped, 1);
    > + return 0;
    > +}
    > +
    > +char LICENSE[] SEC("license") = "Dual BSD/GPL";
    > diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
    > index c272c06565c0..68072ec655ce 100644
    > --- a/tools/perf/util/evsel.h
    > +++ b/tools/perf/util/evsel.h
    > @@ -150,8 +150,10 @@ struct evsel {
    > */
    > struct bpf_counter_ops *bpf_counter_ops;
    >
    > - /* for perf-stat -b */
    > - struct list_head bpf_counter_list;
    > + union {
    > + struct list_head bpf_counter_list; /* for perf-stat -b */
    > + struct list_head bpf_filters; /* for perf-record --filter */
    > + };
    >
    > /* for perf-stat --use-bpf */
    > int bperf_leader_prog_fd;
    > @@ -159,6 +161,7 @@ struct evsel {
    > union {
    > struct bperf_leader_bpf *leader_skel;
    > struct bperf_follower_bpf *follower_skel;
    > + void *bpf_skel;
    > };
    > unsigned long open_flags;
    > int precise_ip_original;
    > --
    > 2.40.0.rc1.284.g88254d51c5-goog
    >

    --

    - Arnaldo

    \
     
     \ /
      Last update: 2023-03-27 01:01    [W:4.263 / U:0.104 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site