netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH v2 bpf-next 1/2] bpf: separate bpf_get_[stack|stackid] for perf events BPF
       [not found] ` <20200715052601.2404533-2-songliubraving@fb.com>
@ 2020-07-16  5:55   ` Andrii Nakryiko
  2020-07-17  1:07   ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: Andrii Nakryiko @ 2020-07-16  5:55 UTC (permalink / raw)
  To: Song Liu
  Cc: open list, bpf, Networking, Alexei Starovoitov, Daniel Borkmann,
	Kernel Team, john fastabend, KP Singh, Jesper Dangaard Brouer,
	Peter Ziljstra

On Tue, Jul 14, 2020 at 11:08 PM Song Liu <songliubraving@fb.com> wrote:
>
> Calling get_perf_callchain() on perf_events from PEBS entries may cause
> unwinder errors. To fix this issue, the callchain is fetched early. Such
> perf_events are marked with __PERF_SAMPLE_CALLCHAIN_EARLY.
>
> Similarly, calling bpf_get_[stack|stackid] on perf_events from PEBS may
> also cause unwinder errors. To fix this, add separate version of these
> two helpers, bpf_get_[stack|stackid]_pe. These two hepers use callchain in
> bpf_perf_event_data_kern->data->callchain.
>
> Signed-off-by: Song Liu <songliubraving@fb.com>
> ---
>  include/linux/bpf.h      |   2 +
>  kernel/bpf/stackmap.c    | 204 +++++++++++++++++++++++++++++++++++----
>  kernel/trace/bpf_trace.c |   4 +-
>  3 files changed, 190 insertions(+), 20 deletions(-)
>

Glad this approach worked out! Few minor bugs below, though.

[...]

> +       if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK |
> +                              BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID)))
> +               return -EINVAL;
> +
> +       user = flags & BPF_F_USER_STACK;
> +       kernel = !user;
> +
> +       has_kernel = !event->attr.exclude_callchain_kernel;
> +       has_user = !event->attr.exclude_callchain_user;
> +
> +       if ((kernel && !has_kernel) || (user && !has_user))
> +               return -EINVAL;
> +
> +       trace = ctx->data->callchain;
> +       if (!trace || (!has_kernel && !has_user))

(!has_kernel && !has_user) can never happen, it's checked by if above
(one of kernel or user is always true => one of has_user or has_kernel
is always true).

> +               return -EFAULT;
> +
> +       if (has_kernel && has_user) {
> +               __u64 nr_kernel = count_kernel_ip(trace);
> +               int ret;
> +
> +               if (kernel) {
> +                       __u64 nr = trace->nr;
> +
> +                       trace->nr = nr_kernel;
> +                       ret = __bpf_get_stackid(map, trace, flags);
> +
> +                       /* restore nr */
> +                       trace->nr = nr;
> +               } else { /* user */
> +                       u64 skip = flags & BPF_F_SKIP_FIELD_MASK;
> +
> +                       skip += nr_kernel;
> +                       if (skip > ~BPF_F_SKIP_FIELD_MASK)

something fishy here: ~BPF_F_SKIP_FIELD_MASK is a really big number,
were you going to check that skip is not bigger than 255 (i.e., fits
within BPF_F_SKIP_FIELD_MASK)?

> +                               return -EFAULT;
> +
> +                       flags = (flags & ~BPF_F_SKIP_FIELD_MASK) |
> +                               (skip  & BPF_F_SKIP_FIELD_MASK);
> +                       ret = __bpf_get_stackid(map, trace, flags);
> +               }
> +               return ret;
> +       }
> +       return __bpf_get_stackid(map, trace, flags);
> +}
> +

[...]

> +
> +       has_kernel = !event->attr.exclude_callchain_kernel;
> +       has_user = !event->attr.exclude_callchain_user;
> +
> +       if ((kernel && !has_kernel) || (user && !has_user))
> +               goto clear;
> +
> +       err = -EFAULT;
> +       trace = ctx->data->callchain;
> +       if (!trace || (!has_kernel && !has_user))
> +               goto clear;

same as above for bpf_get_stackid, probably can be simplified

> +
> +       if (has_kernel && has_user) {
> +               __u64 nr_kernel = count_kernel_ip(trace);
> +               int ret;
> +
> +               if (kernel) {
> +                       __u64 nr = trace->nr;
> +
> +                       trace->nr = nr_kernel;
> +                       ret = __bpf_get_stack(ctx->regs, NULL, trace, buf,
> +                                             size, flags);
> +
> +                       /* restore nr */
> +                       trace->nr = nr;
> +               } else { /* user */
> +                       u64 skip = flags & BPF_F_SKIP_FIELD_MASK;
> +
> +                       skip += nr_kernel;
> +                       if (skip > ~BPF_F_SKIP_FIELD_MASK)
> +                               goto clear;
> +

and here

> +                       flags = (flags & ~BPF_F_SKIP_FIELD_MASK) |
> +                               (skip  & BPF_F_SKIP_FIELD_MASK);

actually if you check that skip <= BPF_F_SKIP_FIELD_MASK, you don't
need to mask it here, just `| skip`

> +                       ret = __bpf_get_stack(ctx->regs, NULL, trace, buf,
> +                                             size, flags);
> +               }
> +               return ret;
> +       }
> +       return __bpf_get_stack(ctx->regs, NULL, trace, buf, size, flags);
> +clear:
> +       memset(buf, 0, size);
> +       return err;
> +
> +}
> +

[...]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2 bpf-next 2/2] selftests/bpf: add callchain_stackid
       [not found] ` <20200715052601.2404533-3-songliubraving@fb.com>
@ 2020-07-16  6:04   ` Andrii Nakryiko
  0 siblings, 0 replies; 3+ messages in thread
From: Andrii Nakryiko @ 2020-07-16  6:04 UTC (permalink / raw)
  To: Song Liu
  Cc: open list, bpf, Networking, Alexei Starovoitov, Daniel Borkmann,
	Kernel Team, john fastabend, KP Singh, Jesper Dangaard Brouer,
	Peter Ziljstra

On Tue, Jul 14, 2020 at 11:09 PM Song Liu <songliubraving@fb.com> wrote:
>
> This tests new helper function bpf_get_stackid_pe and bpf_get_stack_pe.
> These two helpers have different implementation for perf_event with PEB
> entries.
>
> Signed-off-by: Song Liu <songliubraving@fb.com>
> ---
>  .../bpf/prog_tests/perf_event_stackmap.c      | 120 ++++++++++++++++++
>  .../selftests/bpf/progs/perf_event_stackmap.c |  64 ++++++++++
>  2 files changed, 184 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/perf_event_stackmap.c
>  create mode 100644 tools/testing/selftests/bpf/progs/perf_event_stackmap.c
>

Just few simplification suggestions, but overall looks good, so please add:

Acked-by: Andrii Nakryiko <andriin@fb.com>

[...]

> +
> +void test_perf_event_stackmap(void)
> +{
> +       struct perf_event_attr attr = {
> +               /* .type = PERF_TYPE_SOFTWARE, */
> +               .type = PERF_TYPE_HARDWARE,
> +               .config = PERF_COUNT_HW_CPU_CYCLES,
> +               .precise_ip = 2,
> +               .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_BRANCH_STACK |
> +                       PERF_SAMPLE_CALLCHAIN,
> +               .branch_sample_type = PERF_SAMPLE_BRANCH_USER |
> +                       PERF_SAMPLE_BRANCH_NO_FLAGS |
> +                       PERF_SAMPLE_BRANCH_NO_CYCLES |
> +                       PERF_SAMPLE_BRANCH_CALL_STACK,
> +               .sample_period = 5000,
> +               .size = sizeof(struct perf_event_attr),
> +       };
> +       struct perf_event_stackmap *skel;
> +       __u32 duration = 0;
> +       cpu_set_t cpu_set;
> +       int pmu_fd, err;
> +
> +       skel = perf_event_stackmap__open();
> +
> +       if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
> +               return;
> +
> +       /* override program type */
> +       bpf_program__set_perf_event(skel->progs.oncpu);

this should be unnecessary, didn't libbpf detect the type correctly
from SEC? If not, let's fix that.

> +
> +       err = perf_event_stackmap__load(skel);
> +       if (CHECK(err, "skel_load", "skeleton load failed: %d\n", err))
> +               goto cleanup;
> +
> +       CPU_ZERO(&cpu_set);
> +       CPU_SET(0, &cpu_set);
> +       err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
> +       if (CHECK(err, "set_affinity", "err %d, errno %d\n", err, errno))
> +               goto cleanup;
> +
> +       pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
> +                        0 /* cpu 0 */, -1 /* group id */,
> +                        0 /* flags */);
> +       if (pmu_fd < 0) {
> +               printf("%s:SKIP:cpu doesn't support the event\n", __func__);
> +               test__skip();
> +               goto cleanup;
> +       }
> +
> +       skel->links.oncpu = bpf_program__attach_perf_event(skel->progs.oncpu,
> +                                                          pmu_fd);
> +       if (CHECK(IS_ERR(skel->links.oncpu), "attach_perf_event",
> +                 "err %ld\n", PTR_ERR(skel->links.oncpu))) {
> +               close(pmu_fd);
> +               goto cleanup;
> +       }
> +
> +       /* create kernel and user stack traces for testing */
> +       func_6();
> +
> +       CHECK(skel->data->stackid_kernel != 2, "get_stackid_kernel", "failed\n");
> +       CHECK(skel->data->stackid_user != 2, "get_stackid_user", "failed\n");
> +       CHECK(skel->data->stack_kernel != 2, "get_stack_kernel", "failed\n");
> +       CHECK(skel->data->stack_user != 2, "get_stack_user", "failed\n");
> +       close(pmu_fd);

I think pmu_fd will be closed by perf_event_stackmap__destory (through
closing the link)

> +
> +cleanup:
> +       perf_event_stackmap__destroy(skel);
> +}
> diff --git a/tools/testing/selftests/bpf/progs/perf_event_stackmap.c b/tools/testing/selftests/bpf/progs/perf_event_stackmap.c
> new file mode 100644
> index 0000000000000..1b0457efeedec
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/perf_event_stackmap.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2020 Facebook
> +#include "vmlinux.h"
> +#include <bpf/bpf_helpers.h>
> +
> +#ifndef PERF_MAX_STACK_DEPTH
> +#define PERF_MAX_STACK_DEPTH         127
> +#endif
> +
> +#ifndef BPF_F_USER_STACK
> +#define BPF_F_USER_STACK               (1ULL << 8)
> +#endif

BPF_F_USER_STACK should be in vmlinux.h already, similarly to BPF_F_CURRENT_CPU

> +
> +typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH];
> +struct {
> +       __uint(type, BPF_MAP_TYPE_STACK_TRACE);
> +       __uint(max_entries, 16384);
> +       __uint(key_size, sizeof(__u32));
> +       __uint(value_size, sizeof(stack_trace_t));
> +} stackmap SEC(".maps");
> +
> +struct {
> +       __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
> +       __uint(max_entries, 1);
> +       __type(key, __u32);
> +       __type(value, stack_trace_t);
> +} stackdata_map SEC(".maps");
> +
> +long stackid_kernel = 1;
> +long stackid_user = 1;
> +long stack_kernel = 1;
> +long stack_user = 1;
> +

nit: kind of unusual to go from 1 -> 2, why no zero to one as a flag?
those variables will be available through skel->bss, btw

> +SEC("perf_event")
> +int oncpu(void *ctx)
> +{
> +       int max_len = PERF_MAX_STACK_DEPTH * sizeof(__u64);
> +       stack_trace_t *trace;
> +       __u32 key = 0;
> +       long val;
> +
> +       val = bpf_get_stackid(ctx, &stackmap, 0);
> +       if (val > 0)
> +               stackid_kernel = 2;
> +       val = bpf_get_stackid(ctx, &stackmap, BPF_F_USER_STACK);
> +       if (val > 0)
> +               stackid_user = 2;
> +
> +       trace = bpf_map_lookup_elem(&stackdata_map, &key);
> +       if (!trace)
> +               return 0;
> +
> +       val = bpf_get_stack(ctx, trace, max_len, 0);

given you don't care about contents of trace, you could have used
`stack_trace_t trace = {}` global variable instead of PERCPU_ARRAY.

> +       if (val > 0)
> +               stack_kernel = 2;
> +
> +       val = bpf_get_stack(ctx, trace, max_len, BPF_F_USER_STACK);

nit: max_len == sizeof(stack_trace_t) ?

> +       if (val > 0)
> +               stack_user = 2;
> +
> +       return 0;
> +}
> +
> +char LICENSE[] SEC("license") = "GPL";
> --
> 2.24.1
>

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2 bpf-next 1/2] bpf: separate bpf_get_[stack|stackid] for perf events BPF
       [not found] ` <20200715052601.2404533-2-songliubraving@fb.com>
  2020-07-16  5:55   ` [PATCH v2 bpf-next 1/2] bpf: separate bpf_get_[stack|stackid] for perf events BPF Andrii Nakryiko
@ 2020-07-17  1:07   ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kernel test robot @ 2020-07-17  1:07 UTC (permalink / raw)
  To: Song Liu, linux-kernel, bpf, netdev
  Cc: kbuild-all, clang-built-linux, ast, daniel, kernel-team,
	john.fastabend, kpsingh, brouer, peterz

[-- Attachment #1: Type: text/plain, Size: 5393 bytes --]

Hi Song,

I love your patch! Yet something to improve:

[auto build test ERROR on bpf-next/master]

url:    https://github.com/0day-ci/linux/commits/Song-Liu/bpf-fix-stackmap-on-perf_events-with-PEBS/20200715-133118
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
config: arm64-randconfig-r004-20200716 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project ed6b578040a85977026c93bf4188f996148f3218)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install arm64 cross compiling tool for clang build
        # apt-get install binutils-aarch64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> kernel/bpf/stackmap.c:698:26: error: incompatible pointer types passing 'bpf_user_pt_regs_t *' (aka 'struct user_pt_regs *') to parameter of type 'struct pt_regs *' [-Werror,-Wincompatible-pointer-types]
                   return __bpf_get_stack(ctx->regs, NULL, NULL, buf, size, flags);
                                          ^~~~~~~~~
   kernel/bpf/stackmap.c:581:45: note: passing argument to parameter 'regs' here
   static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
                                               ^
   kernel/bpf/stackmap.c:726:26: error: incompatible pointer types passing 'bpf_user_pt_regs_t *' (aka 'struct user_pt_regs *') to parameter of type 'struct pt_regs *' [-Werror,-Wincompatible-pointer-types]
                           ret = __bpf_get_stack(ctx->regs, NULL, trace, buf,
                                                 ^~~~~~~~~
   kernel/bpf/stackmap.c:581:45: note: passing argument to parameter 'regs' here
   static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
                                               ^
   kernel/bpf/stackmap.c:740:26: error: incompatible pointer types passing 'bpf_user_pt_regs_t *' (aka 'struct user_pt_regs *') to parameter of type 'struct pt_regs *' [-Werror,-Wincompatible-pointer-types]
                           ret = __bpf_get_stack(ctx->regs, NULL, trace, buf,
                                                 ^~~~~~~~~
   kernel/bpf/stackmap.c:581:45: note: passing argument to parameter 'regs' here
   static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
                                               ^
   kernel/bpf/stackmap.c:745:25: error: incompatible pointer types passing 'bpf_user_pt_regs_t *' (aka 'struct user_pt_regs *') to parameter of type 'struct pt_regs *' [-Werror,-Wincompatible-pointer-types]
           return __bpf_get_stack(ctx->regs, NULL, trace, buf, size, flags);
                                  ^~~~~~~~~
   kernel/bpf/stackmap.c:581:45: note: passing argument to parameter 'regs' here
   static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
                                               ^
   4 errors generated.

vim +698 kernel/bpf/stackmap.c

   687	
   688	BPF_CALL_4(bpf_get_stack_pe, struct bpf_perf_event_data_kern *, ctx,
   689		   void *, buf, u32, size, u64, flags)
   690	{
   691		struct perf_event *event = ctx->event;
   692		struct perf_callchain_entry *trace;
   693		bool has_kernel, has_user;
   694		bool kernel, user;
   695		int err = -EINVAL;
   696	
   697		if (!(event->attr.sample_type & __PERF_SAMPLE_CALLCHAIN_EARLY))
 > 698			return __bpf_get_stack(ctx->regs, NULL, NULL, buf, size, flags);
   699	
   700		if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK |
   701				       BPF_F_USER_BUILD_ID)))
   702			goto clear;
   703	
   704		user = flags & BPF_F_USER_STACK;
   705		kernel = !user;
   706	
   707		has_kernel = !event->attr.exclude_callchain_kernel;
   708		has_user = !event->attr.exclude_callchain_user;
   709	
   710		if ((kernel && !has_kernel) || (user && !has_user))
   711			goto clear;
   712	
   713		err = -EFAULT;
   714		trace = ctx->data->callchain;
   715		if (!trace || (!has_kernel && !has_user))
   716			goto clear;
   717	
   718		if (has_kernel && has_user) {
   719			__u64 nr_kernel = count_kernel_ip(trace);
   720			int ret;
   721	
   722			if (kernel) {
   723				__u64 nr = trace->nr;
   724	
   725				trace->nr = nr_kernel;
   726				ret = __bpf_get_stack(ctx->regs, NULL, trace, buf,
   727						      size, flags);
   728	
   729				/* restore nr */
   730				trace->nr = nr;
   731			} else { /* user */
   732				u64 skip = flags & BPF_F_SKIP_FIELD_MASK;
   733	
   734				skip += nr_kernel;
   735				if (skip > ~BPF_F_SKIP_FIELD_MASK)
   736					goto clear;
   737	
   738				flags = (flags & ~BPF_F_SKIP_FIELD_MASK) |
   739					(skip  & BPF_F_SKIP_FIELD_MASK);
   740				ret = __bpf_get_stack(ctx->regs, NULL, trace, buf,
   741						      size, flags);
   742			}
   743			return ret;
   744		}
   745		return __bpf_get_stack(ctx->regs, NULL, trace, buf, size, flags);
   746	clear:
   747		memset(buf, 0, size);
   748		return err;
   749	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 38706 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-07-17  1:09 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20200715052601.2404533-1-songliubraving@fb.com>
     [not found] ` <20200715052601.2404533-3-songliubraving@fb.com>
2020-07-16  6:04   ` [PATCH v2 bpf-next 2/2] selftests/bpf: add callchain_stackid Andrii Nakryiko
     [not found] ` <20200715052601.2404533-2-songliubraving@fb.com>
2020-07-16  5:55   ` [PATCH v2 bpf-next 1/2] bpf: separate bpf_get_[stack|stackid] for perf events BPF Andrii Nakryiko
2020-07-17  1:07   ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).