[bpf-next,1/3] perf/core: Prepare sample data before calling BPF
Commit Message
To allow bpf overflow handler to access the perf sample data, it needs to
prepare missing but requested data before calling the handler.
I'm taking a conservative approach to allow a list of sample formats only
instead of allowing them all. For now, IP and ADDR data are allowed and
I think it's good enough to build and verify general BPF-based sample
filters for perf events.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
kernel/events/core.c | 40 +++++++++++++++++++++++++++++++---------
1 file changed, 31 insertions(+), 9 deletions(-)
Comments
On Mon, Oct 31, 2022 at 10:23:38PM -0700, Namhyung Kim wrote:
> To allow bpf overflow handler to access the perf sample data, it needs to
> prepare missing but requested data before calling the handler.
>
> I'm taking a conservative approach to allow a list of sample formats only
> instead of allowing them all. For now, IP and ADDR data are allowed and
> I think it's good enough to build and verify general BPF-based sample
> filters for perf events.
>
> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> ---
> kernel/events/core.c | 40 +++++++++++++++++++++++++++++++---------
> 1 file changed, 31 insertions(+), 9 deletions(-)
>
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index aefc1e08e015..519f30c33a24 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -7329,8 +7329,10 @@ void perf_prepare_sample(struct perf_event_header *header,
> filtered_sample_type = sample_type & ~data->sample_flags;
> __perf_event_header__init_id(header, data, event, filtered_sample_type);
>
> - if (sample_type & (PERF_SAMPLE_IP | PERF_SAMPLE_CODE_PAGE_SIZE))
> - data->ip = perf_instruction_pointer(regs);
> + if (sample_type & (PERF_SAMPLE_IP | PERF_SAMPLE_CODE_PAGE_SIZE)) {
> + if (filtered_sample_type & PERF_SAMPLE_IP)
> + data->ip = perf_instruction_pointer(regs);
> + }
>
> if (sample_type & PERF_SAMPLE_CALLCHAIN) {
> int size = 1;
> @@ -10006,6 +10008,32 @@ static void perf_event_free_filter(struct perf_event *event)
> }
>
> #ifdef CONFIG_BPF_SYSCALL
> +static void bpf_prepare_sample(struct bpf_prog *prog,
> + struct perf_event *event,
> + struct perf_sample_data *data,
> + struct pt_regs *regs)
> +{
> + u64 filtered_sample_type;
> +
> + filtered_sample_type = event->attr.sample_type & ~data->sample_flags;
could we add the same comment in here as is in perf_prepare_sample
/*
* Clear the sample flags that have already been done by the
* PMU driver.
*/
it took me while to recall while we set addr to 0 in here ;-)
thanks,
jirka
> +
> + if (prog->call_get_stack &&
> + (filtered_sample_type & PERF_SAMPLE_CALLCHAIN)) {
> + data->callchain = perf_callchain(event, regs);
> + data->sample_flags |= PERF_SAMPLE_CALLCHAIN;
> + }
> +
> + if (filtered_sample_type & PERF_SAMPLE_IP) {
> + data->ip = perf_instruction_pointer(regs);
> + data->sample_flags |= PERF_SAMPLE_IP;
> + }
> +
> + if (filtered_sample_type & PERF_SAMPLE_ADDR) {
> + data->addr = 0;
> + data->sample_flags |= PERF_SAMPLE_ADDR;
> + }
> +}
> +
> static void bpf_overflow_handler(struct perf_event *event,
> struct perf_sample_data *data,
> struct pt_regs *regs)
> @@ -10023,13 +10051,7 @@ static void bpf_overflow_handler(struct perf_event *event,
> rcu_read_lock();
> prog = READ_ONCE(event->prog);
> if (prog) {
> - if (prog->call_get_stack &&
> - (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) &&
> - !(data->sample_flags & PERF_SAMPLE_CALLCHAIN)) {
> - data->callchain = perf_callchain(event, regs);
> - data->sample_flags |= PERF_SAMPLE_CALLCHAIN;
> - }
> -
> + bpf_prepare_sample(prog, event, data, regs);
> ret = bpf_prog_run(prog, &ctx);
> }
> rcu_read_unlock();
> --
> 2.38.1.273.g43a17bfeac-goog
>
Hi Jiri,
On Tue, Nov 1, 2022 at 3:03 AM Jiri Olsa <olsajiri@gmail.com> wrote:
>
> On Mon, Oct 31, 2022 at 10:23:38PM -0700, Namhyung Kim wrote:
> > To allow bpf overflow handler to access the perf sample data, it needs to
> > prepare missing but requested data before calling the handler.
> >
> > I'm taking a conservative approach to allow a list of sample formats only
> > instead of allowing them all. For now, IP and ADDR data are allowed and
> > I think it's good enough to build and verify general BPF-based sample
> > filters for perf events.
> >
> > Signed-off-by: Namhyung Kim <namhyung@kernel.org>
> > ---
> > kernel/events/core.c | 40 +++++++++++++++++++++++++++++++---------
> > 1 file changed, 31 insertions(+), 9 deletions(-)
> >
> > diff --git a/kernel/events/core.c b/kernel/events/core.c
> > index aefc1e08e015..519f30c33a24 100644
> > --- a/kernel/events/core.c
> > +++ b/kernel/events/core.c
> > @@ -7329,8 +7329,10 @@ void perf_prepare_sample(struct perf_event_header *header,
> > filtered_sample_type = sample_type & ~data->sample_flags;
> > __perf_event_header__init_id(header, data, event, filtered_sample_type);
> >
> > - if (sample_type & (PERF_SAMPLE_IP | PERF_SAMPLE_CODE_PAGE_SIZE))
> > - data->ip = perf_instruction_pointer(regs);
> > + if (sample_type & (PERF_SAMPLE_IP | PERF_SAMPLE_CODE_PAGE_SIZE)) {
> > + if (filtered_sample_type & PERF_SAMPLE_IP)
> > + data->ip = perf_instruction_pointer(regs);
> > + }
> >
> > if (sample_type & PERF_SAMPLE_CALLCHAIN) {
> > int size = 1;
> > @@ -10006,6 +10008,32 @@ static void perf_event_free_filter(struct perf_event *event)
> > }
> >
> > #ifdef CONFIG_BPF_SYSCALL
> > +static void bpf_prepare_sample(struct bpf_prog *prog,
> > + struct perf_event *event,
> > + struct perf_sample_data *data,
> > + struct pt_regs *regs)
> > +{
> > + u64 filtered_sample_type;
> > +
> > + filtered_sample_type = event->attr.sample_type & ~data->sample_flags;
>
> could we add the same comment in here as is in perf_prepare_sample
>
> /*
> * Clear the sample flags that have already been done by the
> * PMU driver.
> */
>
> it took me while to recall while we set addr to 0 in here ;-)
Sorry about that! :) I'll add the comment.
Thanks,
Namhyung
>
> > +
> > + if (prog->call_get_stack &&
> > + (filtered_sample_type & PERF_SAMPLE_CALLCHAIN)) {
> > + data->callchain = perf_callchain(event, regs);
> > + data->sample_flags |= PERF_SAMPLE_CALLCHAIN;
> > + }
> > +
> > + if (filtered_sample_type & PERF_SAMPLE_IP) {
> > + data->ip = perf_instruction_pointer(regs);
> > + data->sample_flags |= PERF_SAMPLE_IP;
> > + }
> > +
> > + if (filtered_sample_type & PERF_SAMPLE_ADDR) {
> > + data->addr = 0;
> > + data->sample_flags |= PERF_SAMPLE_ADDR;
> > + }
> > +}
> > +
> > static void bpf_overflow_handler(struct perf_event *event,
> > struct perf_sample_data *data,
> > struct pt_regs *regs)
> > @@ -10023,13 +10051,7 @@ static void bpf_overflow_handler(struct perf_event *event,
> > rcu_read_lock();
> > prog = READ_ONCE(event->prog);
> > if (prog) {
> > - if (prog->call_get_stack &&
> > - (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) &&
> > - !(data->sample_flags & PERF_SAMPLE_CALLCHAIN)) {
> > - data->callchain = perf_callchain(event, regs);
> > - data->sample_flags |= PERF_SAMPLE_CALLCHAIN;
> > - }
> > -
> > + bpf_prepare_sample(prog, event, data, regs);
> > ret = bpf_prog_run(prog, &ctx);
> > }
> > rcu_read_unlock();
> > --
> > 2.38.1.273.g43a17bfeac-goog
> >
@@ -7329,8 +7329,10 @@ void perf_prepare_sample(struct perf_event_header *header,
filtered_sample_type = sample_type & ~data->sample_flags;
__perf_event_header__init_id(header, data, event, filtered_sample_type);
- if (sample_type & (PERF_SAMPLE_IP | PERF_SAMPLE_CODE_PAGE_SIZE))
- data->ip = perf_instruction_pointer(regs);
+ if (sample_type & (PERF_SAMPLE_IP | PERF_SAMPLE_CODE_PAGE_SIZE)) {
+ if (filtered_sample_type & PERF_SAMPLE_IP)
+ data->ip = perf_instruction_pointer(regs);
+ }
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
int size = 1;
@@ -10006,6 +10008,32 @@ static void perf_event_free_filter(struct perf_event *event)
}
#ifdef CONFIG_BPF_SYSCALL
+static void bpf_prepare_sample(struct bpf_prog *prog,
+ struct perf_event *event,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ u64 filtered_sample_type;
+
+ filtered_sample_type = event->attr.sample_type & ~data->sample_flags;
+
+ if (prog->call_get_stack &&
+ (filtered_sample_type & PERF_SAMPLE_CALLCHAIN)) {
+ data->callchain = perf_callchain(event, regs);
+ data->sample_flags |= PERF_SAMPLE_CALLCHAIN;
+ }
+
+ if (filtered_sample_type & PERF_SAMPLE_IP) {
+ data->ip = perf_instruction_pointer(regs);
+ data->sample_flags |= PERF_SAMPLE_IP;
+ }
+
+ if (filtered_sample_type & PERF_SAMPLE_ADDR) {
+ data->addr = 0;
+ data->sample_flags |= PERF_SAMPLE_ADDR;
+ }
+}
+
static void bpf_overflow_handler(struct perf_event *event,
struct perf_sample_data *data,
struct pt_regs *regs)
@@ -10023,13 +10051,7 @@ static void bpf_overflow_handler(struct perf_event *event,
rcu_read_lock();
prog = READ_ONCE(event->prog);
if (prog) {
- if (prog->call_get_stack &&
- (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) &&
- !(data->sample_flags & PERF_SAMPLE_CALLCHAIN)) {
- data->callchain = perf_callchain(event, regs);
- data->sample_flags |= PERF_SAMPLE_CALLCHAIN;
- }
-
+ bpf_prepare_sample(prog, event, data, regs);
ret = bpf_prog_run(prog, &ctx);
}
rcu_read_unlock();